diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java index 747b755263..2f927d3142 100644 --- a/src/com/android/launcher3/FastScrollRecyclerView.java +++ b/src/com/android/launcher3/FastScrollRecyclerView.java @@ -24,7 +24,6 @@ import android.view.ViewGroup; import android.view.accessibility.AccessibilityNodeInfo; import androidx.annotation.Nullable; -import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.android.launcher3.compat.AccessibilityManagerCompat; @@ -92,8 +91,7 @@ public abstract class FastScrollRecyclerView extends RecyclerView { protected int getAvailableScrollHeight() { // AvailableScrollHeight = Total height of the all items - first page height int firstPageHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); - int totalHeightOfAllItems = getItemsHeight(/* untilIndex= */ getAdapter().getItemCount()); - int availableScrollHeight = totalHeightOfAllItems - firstPageHeight; + int availableScrollHeight = computeVerticalScrollRange() - firstPageHeight; return Math.max(0, availableScrollHeight); } @@ -146,10 +144,7 @@ public abstract class FastScrollRecyclerView extends RecyclerView { // IF scroller is at the very top OR there is no scroll bar because there is probably not // enough items to scroll, THEN it's okay for the container to be pulled down. - if (getCurrentScrollY() == 0) { - return true; - } - return getAdapter() == null || getAdapter().getItemCount() == 0; + return computeVerticalScrollOffset() == 0; } /** @@ -159,53 +154,6 @@ public abstract class FastScrollRecyclerView extends RecyclerView { return true; } - /** - * @return the scroll top of this recycler view. - */ - public int getCurrentScrollY() { - Adapter adapter = getAdapter(); - if (adapter == null) { - return -1; - } - if (adapter.getItemCount() == 0 || getChildCount() == 0) { - return -1; - } - - int itemPosition = NO_POSITION; - View child = null; - - LayoutManager layoutManager = getLayoutManager(); - if (layoutManager instanceof LinearLayoutManager) { - // Use the LayoutManager as the source of truth for visible positions. During - // animations, the view group child may not correspond to the visible views that appear - // at the top. - itemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); - child = layoutManager.findViewByPosition(itemPosition); - } - - if (child == null) { - // If the layout manager returns null for any reason, which can happen before layout - // has occurred for the position, then look at the child of this view as a ViewGroup. - child = getChildAt(0); - itemPosition = getChildAdapterPosition(child); - } - if (itemPosition == NO_POSITION) { - return -1; - } - return getPaddingTop() + getItemsHeight(itemPosition) - - layoutManager.getDecoratedTop(child); - } - - /** - * Returns the sum of the height, in pixels, of this list adapter's items from index - * 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount, - * it returns the full height of all the items. - * - *
If the untilIndex is larger than the total number of items in this adapter, returns the - * sum of all items' height. - */ - protected abstract int getItemsHeight(int untilIndex); - /** * Maps the touch (from 0..1) to the adapter position that should be visible. *
Override in each subclass of this base class.
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 3abefe0cad..07d0f55321 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2803,7 +2803,7 @@ public class Launcher extends StatefulActivity If the untilIndex is larger than the total number of items in this adapter, returns the
+ * sum of all items' height.
+ */
+ private int getItemsHeight(Adapter adapter, int untilIndex) {
+ final int totalItems = adapter.getItemCount();
+ if (mTotalHeightCache.length < (totalItems + 1)) {
+ mTotalHeightCache = new int[totalItems + 1];
+ mLastValidHeightIndex = 0;
+ }
+ if (untilIndex > totalItems) {
+ untilIndex = totalItems;
+ } else if (untilIndex < 0) {
+ untilIndex = 0;
+ }
+ if (untilIndex <= mLastValidHeightIndex) {
+ return mTotalHeightCache[untilIndex];
+ }
+
+ int totalItemsHeight = mTotalHeightCache[mLastValidHeightIndex];
+ for (int i = mLastValidHeightIndex; i < untilIndex; i++) {
+ totalItemsHeight = incrementTotalHeight(adapter, i, totalItemsHeight);
+ mTotalHeightCache[i + 1] = totalItemsHeight;
+ }
+ mLastValidHeightIndex = untilIndex;
+ return totalItemsHeight;
+ }
+
+ /**
+ * The current implementation assumes a linear list with every item taking up the whole row.
+ * Subclasses should override this method to account for any spanning logic
+ */
+ protected int incrementTotalHeight(Adapter adapter, int position, int heightUntilLastPos) {
+ return heightUntilLastPos + mCachedSizes.get(adapter.getItemViewType(position));
+ }
+
+ private void invalidateScrollCache() {
+ mLastValidHeightIndex = 0;
+ }
+
+ @Override
+ public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
+ super.onItemsAdded(recyclerView, positionStart, itemCount);
+ invalidateScrollCache();
+ }
+
+ @Override
+ public void onItemsChanged(RecyclerView recyclerView) {
+ super.onItemsChanged(recyclerView);
+ invalidateScrollCache();
+ }
+
+ @Override
+ public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
+ super.onItemsRemoved(recyclerView, positionStart, itemCount);
+ invalidateScrollCache();
+ }
+
+ @Override
+ public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
+ super.onItemsMoved(recyclerView, from, to, itemCount);
+ invalidateScrollCache();
+ }
+
+ @Override
+ public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
+ Object payload) {
+ super.onItemsUpdated(recyclerView, positionStart, itemCount, payload);
+ invalidateScrollCache();
+ }
+}
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 40e4ce1c04..3af2e3c9dc 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -119,7 +119,6 @@ public class RecyclerViewFastScroller extends View {
// prevent jumping, this offset is applied as the user scrolls.
protected int mTouchOffsetY;
protected int mThumbOffsetY;
- protected int mRvOffsetY;
// Fast scroller popup
private TextView mPopupView;
@@ -207,16 +206,11 @@ public class RecyclerViewFastScroller extends View {
public void setThumbOffsetY(int y) {
if (mThumbOffsetY == y) {
- int rvCurrentOffsetY = mRv.getCurrentScrollY();
- if (mRvOffsetY != rvCurrentOffsetY) {
- mRvOffsetY = mRv.getCurrentScrollY();
- }
return;
}
updatePopupY(y);
mThumbOffsetY = y;
invalidate();
- mRvOffsetY = mRv.getCurrentScrollY();
}
public int getThumbOffsetY() {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index 35fa7a42cc..5969e3e154 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -19,7 +19,6 @@ package com.android.launcher3.widget.picker;
import android.content.Context;
import android.graphics.Point;
import android.util.AttributeSet;
-import android.util.SparseIntArray;
import android.view.MotionEvent;
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -28,6 +27,7 @@ import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
import com.android.launcher3.FastScrollRecyclerView;
import com.android.launcher3.R;
+import com.android.launcher3.util.ScrollableLayoutManager;
/**
* The widgets recycler view.
@@ -42,14 +42,6 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte
private boolean mTouchDownOnScroller;
private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
- /**
- * There is always 1 or 0 item of VIEW_TYPE_WIDGETS_LIST. Other types have fixes sizes, so the
- * the size can be used for all other items of same type. Caching the last know size for
- * VIEW_TYPE_WIDGETS_LIST allows us to use it to estimate full size even when
- * VIEW_TYPE_WIDGETS_LIST is not visible on the screen.
- */
- private final SparseIntArray mCachedSizes = new SparseIntArray();
-
public WidgetsRecyclerView(Context context) {
this(context, null);
}
@@ -68,9 +60,7 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- // create a layout manager with Launcher's context so that scroll position
- // can be preserved during screen rotation.
- setLayoutManager(new LinearLayoutManager(getContext()));
+ setLayoutManager(new ScrollableLayoutManager(getContext()));
}
@Override
@@ -114,7 +104,7 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte
}
// Skip early if, there no child laid out in the container.
- int scrollY = getCurrentScrollY();
+ int scrollY = computeVerticalScrollOffset();
if (scrollY < 0) {
mScrollbar.setThumbOffsetY(-1);
return;
@@ -163,39 +153,6 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte
mHeaderViewDimensionsProvider = headerViewDimensionsProvider;
}
- /**
- * Returns the sum of the height, in pixels, of this list adapter's items from index 0 until
- * {@code untilIndex}.
- *
- * If the untilIndex is larger than the total number of items in this adapter, returns the
- * sum of all items' height.
- */
- @Override
- protected int getItemsHeight(int untilIndex) {
- // Initialize cache
- int childCount = getChildCount();
- int startPosition;
- if (childCount > 0
- && ((startPosition = getChildAdapterPosition(getChildAt(0))) != NO_POSITION)) {
- int loopCount = Math.min(getChildCount(), getAdapter().getItemCount() - startPosition);
- for (int i = 0; i < loopCount; i++) {
- mCachedSizes.put(
- mAdapter.getItemViewType(startPosition + i),
- getChildAt(i).getMeasuredHeight());
- }
- }
-
- if (untilIndex > mAdapter.getItems().size()) {
- untilIndex = mAdapter.getItems().size();
- }
- int totalItemsHeight = 0;
- for (int i = 0; i < untilIndex; i++) {
- int type = mAdapter.getItemViewType(i);
- totalItemsHeight += mCachedSizes.get(type);
- }
- return totalItemsHeight;
- }
-
/**
* Provides dimensions of the header view that is shown at the top of a
* {@link WidgetsRecyclerView}.
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 32b62fb317..70d122bc90 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -537,7 +537,7 @@ public abstract class AbstractLauncherUiTest {
}
protected int getAllAppsScroll(Launcher launcher) {
- return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
+ return launcher.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset();
}
private void checkLauncherIntegrity(
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index a3eee00c51..cf5f5fc320 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -302,7 +302,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
}
private int getWidgetsScroll(Launcher launcher) {
- return getWidgetsView(launcher).getCurrentScrollY();
+ return getWidgetsView(launcher).computeVerticalScrollOffset();
}
private boolean isOptionsPopupVisible(Launcher launcher) {