From b655b8850064cc4f8e59652b4e3bffc09090769a Mon Sep 17 00:00:00 2001 From: Winson Date: Mon, 11 Jul 2016 18:59:18 -0700 Subject: Cleaning up scrollbar logic to properly calculate stable extents. - Removing old logic which assumed that views were the same size, especially now we can have variable dividers, etc. - Simplifying old scroll position logic. - Removing unnecessary prediction icon layout (same as normal icon) Bug: 30023608 Change-Id: I39e1126fa586a76a9bdd3ff38cd6e360ac3021e6 --- res/drawable/all_apps_divider.xml | 20 +++ res/drawable/all_apps_search_divider.xml | 20 +++ res/layout/all_apps_divider.xml | 15 +- res/layout/all_apps_prediction_bar_icon.xml | 28 ---- res/layout/all_apps_search_divider.xml | 25 ++++ res/values/dimens.xml | 1 - src/com/android/launcher3/BaseRecyclerView.java | 46 +----- .../launcher3/allapps/AllAppsContainerView.java | 19 +-- .../launcher3/allapps/AllAppsFastScrollHelper.java | 5 +- .../launcher3/allapps/AllAppsGridAdapter.java | 22 +-- .../launcher3/allapps/AllAppsRecyclerView.java | 166 ++++++++++++++------- .../launcher3/widget/WidgetsRecyclerView.java | 48 +++--- 12 files changed, 224 insertions(+), 191 deletions(-) create mode 100644 res/drawable/all_apps_divider.xml create mode 100644 res/drawable/all_apps_search_divider.xml delete mode 100644 res/layout/all_apps_prediction_bar_icon.xml create mode 100644 res/layout/all_apps_search_divider.xml diff --git a/res/drawable/all_apps_divider.xml b/res/drawable/all_apps_divider.xml new file mode 100644 index 000000000..3fe529556 --- /dev/null +++ b/res/drawable/all_apps_divider.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/res/drawable/all_apps_search_divider.xml b/res/drawable/all_apps_search_divider.xml new file mode 100644 index 000000000..99905e423 --- /dev/null +++ b/res/drawable/all_apps_search_divider.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/res/layout/all_apps_divider.xml b/res/layout/all_apps_divider.xml index b2ee7c1b4..1eaf685c4 100644 --- a/res/layout/all_apps_divider.xml +++ b/res/layout/all_apps_divider.xml @@ -13,13 +13,14 @@ See the License for the specific language governing permissions and limitations under the License. --> - \ No newline at end of file diff --git a/res/layout/all_apps_prediction_bar_icon.xml b/res/layout/all_apps_prediction_bar_icon.xml deleted file mode 100644 index 3836fed89..000000000 --- a/res/layout/all_apps_prediction_bar_icon.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - diff --git a/res/layout/all_apps_search_divider.xml b/res/layout/all_apps_search_divider.xml new file mode 100644 index 000000000..d2ef691ea --- /dev/null +++ b/res/layout/all_apps_search_divider.xml @@ -0,0 +1,25 @@ + + + \ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 9abe3e601..1775d098f 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -80,7 +80,6 @@ 16dp 6dp - 1dp 8dp 24dp diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java index 4cb050e24..8bd5eba00 100644 --- a/src/com/android/launcher3/BaseRecyclerView.java +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -41,21 +41,6 @@ public abstract class BaseRecyclerView extends RecyclerView @Thunk int mDy = 0; private float mDeltaThreshold; - /** - * The current scroll state of the recycler view. We use this in onUpdateScrollbar() - * and scrollToPositionAtProgress() to determine the scroll position of the recycler view so - * that we can calculate what the scroll bar looks like, and where to jump to from the fast - * scroller. - */ - public static class ScrollPositionState { - // The index of the first visible row - public int rowIndex; - // The offset of the first visible row - public int rowTopOffset; - // The adapter position of the first visible item - public int itemPos; - } - protected BaseRecyclerViewFastScrollBar mScrollbar; private int mDownX; @@ -199,11 +184,7 @@ public abstract class BaseRecyclerView extends RecyclerView * Returns the available scroll height: * AvailableScrollHeight = Total height of the all items - last page height */ - protected int getAvailableScrollHeight(int rowCount) { - int totalHeight = getPaddingTop() + getTop(rowCount) + getPaddingBottom(); - int availableScrollHeight = totalHeight - getVisibleHeight(); - return availableScrollHeight; - } + protected abstract int getAvailableScrollHeight(); /** * Returns the available scroll bar height: @@ -247,15 +228,12 @@ public abstract class BaseRecyclerView extends RecyclerView * this by mapping the available scroll area of the recycler view to the available space for the * scroll bar. * - * @param scrollPosState the current scroll position - * @param rowCount the number of rows, used to calculate the total scroll height (assumes that - * all rows are the same height) + * @param scrollY the current scroll y */ - protected void synchronizeScrollBarThumbOffsetToViewScroll(ScrollPositionState scrollPosState, - int rowCount) { + protected void synchronizeScrollBarThumbOffsetToViewScroll(int scrollY, + int availableScrollHeight) { // Only show the scrollbar if there is height to be scrolled int availableScrollBarHeight = getAvailableScrollBarHeight(); - int availableScrollHeight = getAvailableScrollHeight(rowCount); if (availableScrollHeight <= 0) { mScrollbar.setThumbOffset(-1, -1); return; @@ -264,7 +242,6 @@ public abstract class BaseRecyclerView extends RecyclerView // Calculate the current scroll position, the scrollY of the recycler view accounts for the // view padding, while the scrollBarY is drawn right up to the background padding (ignoring // padding) - int scrollY = Math.max(0, getScrollTop(scrollPosState)); int scrollBarY = mBackgroundPadding.top + (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight); @@ -291,20 +268,7 @@ public abstract class BaseRecyclerView extends RecyclerView * * @return the scroll top of this recycler view. */ - protected int getScrollTop(ScrollPositionState scrollPosState) { - return getPaddingTop() + getTop(scrollPosState.rowIndex) - - scrollPosState.rowTopOffset; - } - - /** - * Returns information about the item that the recycler view is currently scrolled to. - */ - protected abstract void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask); - - /** - * Returns the top (or y position) of the row at the specified index. - */ - protected abstract int getTop(int rowIndex); + protected abstract int getCurrentScrollY(); /** * Maps the touch (from 0..1) to the adapter position that should be visible. diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 717ce74c7..a9cc8f379 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -364,26 +364,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mAppsRecyclerView); mAppsRecyclerView.addItemDecoration(focusedItemDecorator); + mAppsRecyclerView.preMeasureViews(mAdapter); mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener()); - // Precalculate the prediction icon and normal icon sizes - LayoutInflater layoutInflater = LayoutInflater.from(getContext()); - final int widthMeasureSpec = MeasureSpec.makeMeasureSpec( - getResources().getDisplayMetrics().widthPixels, MeasureSpec.AT_MOST); - final int heightMeasureSpec = MeasureSpec.makeMeasureSpec( - getResources().getDisplayMetrics().heightPixels, MeasureSpec.AT_MOST); - - BubbleTextView icon = (BubbleTextView) layoutInflater.inflate( - R.layout.all_apps_icon, this, false); - icon.applyDummyInfo(); - icon.measure(widthMeasureSpec, heightMeasureSpec); - BubbleTextView predIcon = (BubbleTextView) layoutInflater.inflate( - R.layout.all_apps_prediction_bar_icon, this, false); - predIcon.applyDummyInfo(); - predIcon.measure(widthMeasureSpec, heightMeasureSpec); - mAppsRecyclerView.setPremeasuredIconHeights(predIcon.getMeasuredHeight(), - icon.getMeasuredHeight()); - // TODO(hyunyoungs): clean up setting the content and the reveal view. if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { getContentView().setBackground(null); diff --git a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java index 73de45e39..6d9094f78 100644 --- a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java +++ b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java @@ -18,7 +18,6 @@ package com.android.launcher3.allapps; import android.support.v7.widget.RecyclerView; import android.view.View; -import com.android.launcher3.BaseRecyclerView; import com.android.launcher3.BaseRecyclerViewFastScrollBar; import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.util.Thunk; @@ -144,8 +143,8 @@ public class AllAppsFastScrollHelper implements AllAppsGridAdapter.BindViewCallb // Calculate the full animation from the current scroll position to the final scroll // position, and then run the animation for the duration. - int newScrollY = Math.min(availableScrollHeight, - mRv.getPaddingTop() + mRv.getTop(info.fastScrollToItem.rowIndex)); + int newPosition = info.fastScrollToItem.position; + int newScrollY = Math.min(availableScrollHeight, mRv.getCurrentScrollY(newPosition, 0)); int numFrames = mFastScrollFrames.length; for (int i = 0; i < numFrames; i++) { // TODO(winsonc): We can interpolate this as well. diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 7d856c04f..268019772 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -334,7 +334,6 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter items = mApps.getAdapterItems(); - if (items.isEmpty() || mNumAppsPerRow == 0) { - return; + if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) { + return -1; + } + + // Calculate the y and offset for the item + View child = getChildAt(0); + int position = getChildPosition(child); + if (position == NO_POSITION) { + return -1; } + return getCurrentScrollY(position, getLayoutManager().getDecoratedTop(child)); + } - int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - int position = getChildPosition(child); - if (position != NO_POSITION) { - AlphabeticalAppsList.AdapterItem item = items.get(position); - if (AllAppsGridAdapter.isViewType(item.viewType, viewTypeMask)) { - stateOut.rowIndex = item.rowIndex; - stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child); - stateOut.itemPos = position; - return; + public int getCurrentScrollY(int position, int offset) { + List items = mApps.getAdapterItems(); + AlphabeticalAppsList.AdapterItem posItem = position < items.size() ? + items.get(position) : null; + int y = mCachedScrollPositions.get(position, -1); + if (y < 0) { + y = 0; + for (int i = 0; i < position; i++) { + AlphabeticalAppsList.AdapterItem item = items.get(i); + if (AllAppsGridAdapter.isIconViewType(item.viewType)) { + // Break once we reach the desired row + if (posItem != null && posItem.viewType == item.viewType && + posItem.rowIndex == item.rowIndex) { + break; + } + // Otherwise, only account for the first icon in the row since they are the same + // size within a row + if (item.rowAppIndex == 0) { + y += mViewHeights.get(item.viewType, 0); + } + } else { + // Rest of the views span the full width + y += mViewHeights.get(item.viewType, 0); } } + mCachedScrollPositions.put(position, y); } - return; - } - @Override - protected boolean supportsFastScrolling() { - // Only allow fast scrolling when the user is not searching, since the results are not - // grouped in a meaningful order - return !mApps.hasFilter(); + return getPaddingTop() + y - offset; } - protected int getTop(int rowIndex) { - if (getChildCount() == 0 || rowIndex <= 0) { - return 0; - } - - // The prediction bar icons have more padding, so account for that in the row offset - return mPredictionIconHeight + (rowIndex - 1) * mIconHeight; + /** + * Returns the available scroll height: + * AvailableScrollHeight = Total height of the all items - last page height + */ + @Override + protected int getAvailableScrollHeight() { + int paddedHeight = getCurrentScrollY(mApps.getAdapterItems().size(), 0); + int totalHeight = paddedHeight + getPaddingBottom(); + return totalHeight - getVisibleHeight(); } /** diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index 2e3cc1aa1..097520605 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -33,7 +33,6 @@ public class WidgetsRecyclerView extends BaseRecyclerView { private static final String TAG = "WidgetsRecyclerView"; private WidgetsModel mWidgets; - private ScrollPositionState mScrollPosState = new ScrollPositionState(); public WidgetsRecyclerView(Context context) { this(context, null); @@ -99,9 +98,8 @@ public class WidgetsRecyclerView extends BaseRecyclerView { stopScroll(); int rowCount = mWidgets.getPackageSize(); - getCurScrollState(mScrollPosState, -1); float pos = rowCount * touchFraction; - int availableScrollHeight = getAvailableScrollHeight(rowCount); + int availableScrollHeight = getAvailableScrollHeight(); LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager()); layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction)); @@ -121,45 +119,41 @@ public class WidgetsRecyclerView extends BaseRecyclerView { } // Skip early if, there no child laid out in the container. - getCurScrollState(mScrollPosState, -1); - if (mScrollPosState.rowIndex < 0) { + int scrollY = getCurrentScrollY(); + if (scrollY < 0) { mScrollbar.setThumbOffset(-1, -1); return; } - synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, mWidgets.getPackageSize()); + synchronizeScrollBarThumbOffsetToViewScroll(scrollY, getAvailableScrollHeight()); } - /** - * Returns the current scroll state. - */ - protected void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask) { - stateOut.rowIndex = -1; - stateOut.rowTopOffset = -1; - stateOut.itemPos = -1; - + @Override + protected int getCurrentScrollY() { // Skip early if widgets are not bound. - if (isModelNotReady()) { - return; + if (isModelNotReady() || getChildCount() == 0) { + return -1; } View child = getChildAt(0); - int position = getChildPosition(child); + int rowIndex = getChildPosition(child); + int y = (child.getMeasuredHeight() * rowIndex); + int offset = getLayoutManager().getDecoratedTop(child); - stateOut.rowIndex = position; - stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child); - stateOut.itemPos = position; + return getPaddingTop() + y - offset; } + /** + * Returns the available scroll height: + * AvailableScrollHeight = Total height of the all items - last page height + */ @Override - protected int getTop(int rowIndex) { - if (getChildCount() == 0) { - return 0; - } - - // All the rows are the same height, return any child height + protected int getAvailableScrollHeight() { View child = getChildAt(0); - return child.getMeasuredHeight() * rowIndex; + int height = child.getMeasuredHeight() * mWidgets.getPackageSize(); + int totalHeight = getPaddingTop() + height + getPaddingBottom(); + int availableScrollHeight = totalHeight - getVisibleHeight(); + return availableScrollHeight; } private boolean isModelNotReady() { -- cgit v1.2.3