diff options
author | Winson Chung <winsonc@google.com> | 2016-07-13 01:04:39 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2016-07-13 01:04:39 +0000 |
commit | b029e9fd666263d3954387e3d96d67f377c23f8b (patch) | |
tree | e4607257e92b2388d8dece03bd0af0128a179693 | |
parent | 4dd8501a256e934b8b1e4a80f57555d35d52d505 (diff) | |
parent | b655b8850064cc4f8e59652b4e3bffc09090769a (diff) | |
download | android_packages_apps_Trebuchet-b029e9fd666263d3954387e3d96d67f377c23f8b.tar.gz android_packages_apps_Trebuchet-b029e9fd666263d3954387e3d96d67f377c23f8b.tar.bz2 android_packages_apps_Trebuchet-b029e9fd666263d3954387e3d96d67f377c23f8b.zip |
Merge "Cleaning up scrollbar logic to properly calculate stable extents." into ub-launcher3-calgary
-rw-r--r-- | res/drawable/all_apps_divider.xml | 20 | ||||
-rw-r--r-- | res/drawable/all_apps_search_divider.xml | 20 | ||||
-rw-r--r-- | res/layout/all_apps_divider.xml | 15 | ||||
-rw-r--r-- | res/layout/all_apps_search_divider.xml (renamed from res/layout/all_apps_prediction_bar_icon.xml) | 19 | ||||
-rw-r--r-- | res/values/dimens.xml | 1 | ||||
-rw-r--r-- | src/com/android/launcher3/BaseRecyclerView.java | 46 | ||||
-rw-r--r-- | src/com/android/launcher3/allapps/AllAppsContainerView.java | 19 | ||||
-rw-r--r-- | src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java | 5 | ||||
-rw-r--r-- | src/com/android/launcher3/allapps/AllAppsGridAdapter.java | 22 | ||||
-rw-r--r-- | src/com/android/launcher3/allapps/AllAppsRecyclerView.java | 166 | ||||
-rw-r--r-- | src/com/android/launcher3/widget/WidgetsRecyclerView.java | 48 |
11 files changed, 207 insertions, 174 deletions
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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@color/all_apps_divider_color" /> + <size android:height="1dp" /> +</shape>
\ 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="?android:attr/colorAccent" /> + <size android:height="1dp" /> +</shape>
\ 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. --> -<View xmlns:android="http://schemas.android.com/apk/res/android" +<ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:importantForAccessibility="no" android:layout_width="match_parent" - android:layout_height="@dimen/all_apps_divider_height" - android:layout_marginBottom="@dimen/all_apps_divider_margin_vertical" - android:layout_marginLeft="@dimen/container_fastscroll_thumb_max_width" - android:layout_marginRight="@dimen/container_fastscroll_thumb_max_width" - android:layout_marginTop="@dimen/all_apps_divider_margin_vertical" - android:background="@color/all_apps_divider_color" + android:layout_height="wrap_content" + android:paddingTop="@dimen/all_apps_divider_margin_vertical" + android:paddingBottom="@dimen/all_apps_divider_margin_vertical" + android:paddingLeft="@dimen/container_fastscroll_thumb_max_width" + android:paddingRight="@dimen/container_fastscroll_thumb_max_width" + android:src="@drawable/all_apps_divider" + android:scaleType="fitXY" android:focusable="false" />
\ No newline at end of file diff --git a/res/layout/all_apps_prediction_bar_icon.xml b/res/layout/all_apps_search_divider.xml index 3836fed89..d2ef691ea 100644 --- a/res/layout/all_apps_prediction_bar_icon.xml +++ b/res/layout/all_apps_search_divider.xml @@ -13,16 +13,13 @@ See the License for the specific language governing permissions and limitations under the License. --> -<com.android.launcher3.BubbleTextView - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:launcher="http://schemas.android.com/apk/res-auto" - style="@style/Icon.AllApps" - android:id="@+id/icon" +<ImageView xmlns:android="http://schemas.android.com/apk/res/android" + android:importantForAccessibility="no" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="center" - android:paddingTop="@dimen/all_apps_icon_top_bottom_padding" - android:paddingBottom="@dimen/all_apps_icon_top_bottom_padding" - android:focusable="true" - launcher:iconDisplay="all_apps" /> - + android:paddingBottom="@dimen/all_apps_divider_margin_vertical" + android:paddingLeft="@dimen/container_fastscroll_thumb_max_width" + android:paddingRight="@dimen/container_fastscroll_thumb_max_width" + android:src="@drawable/all_apps_search_divider" + android:scaleType="fitXY" + android:focusable="false" />
\ 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 @@ <dimen name="all_apps_header_scroll_to_elevation">16dp</dimen> <dimen name="all_apps_header_shadow_height">6dp</dimen> - <dimen name="all_apps_divider_height">1dp</dimen> <dimen name="all_apps_divider_margin_vertical">8dp</dimen> <dimen name="all_apps_bezel_swipe_height">24dp</dimen> 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 a0cb1ef13..fc1288da8 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -366,26 +366,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<AllAppsGridAdapter. private final int mSectionNamesMargin; private final int mSectionHeaderOffset; private final Paint mSectionTextPaint; - private int mAccentColor; private int mAppsPerRow; @@ -364,12 +363,10 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.all_apps_grid_section_y_offset); mIsRtl = Utilities.isRtl(res); - mAccentColor = Utilities.getColorAccent(launcher); - mSectionTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mSectionTextPaint.setTextSize(res.getDimensionPixelSize( R.dimen.all_apps_grid_section_text_size)); - mSectionTextPaint.setColor(mAccentColor); + mSectionTextPaint.setColor(Utilities.getColorAccent(launcher)); } public static boolean isDividerViewType(int viewType) { @@ -380,6 +377,10 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. return isViewType(viewType, VIEW_TYPE_MASK_ICON); } + public static boolean isPredictionIconViewType(int viewType) { + return isViewType(viewType, VIEW_TYPE_PREDICTION_ICON); + } + public static boolean isViewType(int viewType, int viewTypeMask) { return (viewType & viewTypeMask) != 0; } @@ -449,8 +450,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. /* falls through */ case VIEW_TYPE_PREDICTION_ICON: { BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( - viewType == VIEW_TYPE_ICON ? R.layout.all_apps_icon : - R.layout.all_apps_prediction_bar_icon, parent, false); + R.layout.all_apps_icon, parent, false); icon.setOnClickListener(mIconClickListener); icon.setOnLongClickListener(mIconLongClickListener); icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext()) @@ -472,14 +472,8 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. }); return new ViewHolder(searchMarketView); case VIEW_TYPE_SEARCH_DIVIDER: - final View searchDivider = - mLayoutInflater.inflate(R.layout.all_apps_divider, parent, false); - searchDivider.setBackgroundColor(mAccentColor); - final GridLayoutManager.LayoutParams searchDividerParams = - (GridLayoutManager.LayoutParams) searchDivider.getLayoutParams(); - searchDividerParams.topMargin = 0; - searchDivider.setLayoutParams(searchDividerParams); - return new ViewHolder(searchDivider); + return new ViewHolder(mLayoutInflater.inflate( + R.layout.all_apps_search_divider, parent, false)); case VIEW_TYPE_PREDICTION_DIVIDER: /* falls through */ case VIEW_TYPE_SEARCH_MARKET_DIVIDER: diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index ac881138d..3a4485320 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -21,6 +21,7 @@ import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; +import android.util.SparseIntArray; import android.view.View; import com.android.launcher3.BaseRecyclerView; @@ -42,13 +43,11 @@ public class AllAppsRecyclerView extends BaseRecyclerView private AlphabeticalAppsList mApps; private AllAppsFastScrollHelper mFastScrollHelper; - private BaseRecyclerView.ScrollPositionState mScrollPosState = - new BaseRecyclerView.ScrollPositionState(); private int mNumAppsPerRow; - // The specific icon heights that we use to calculate scroll - private int mPredictionIconHeight; - private int mIconHeight; + // The specific view heights that we use to calculate scroll + private SparseIntArray mViewHeights = new SparseIntArray(); + private SparseIntArray mCachedScrollPositions = new SparseIntArray(); // The empty-search result background private AllAppsBackgroundDrawable mEmptySearchBackground; @@ -109,11 +108,52 @@ public class AllAppsRecyclerView extends BaseRecyclerView } /** - * Sets the heights of the icons in this view (for scroll calculations). + * Ensures that we can present a stable scrollbar for views of varying types by pre-measuring + * all the different view types. */ - public void setPremeasuredIconHeights(int predictionIconHeight, int iconHeight) { - mPredictionIconHeight = predictionIconHeight; - mIconHeight = iconHeight; + public void preMeasureViews(AllAppsGridAdapter adapter) { + final int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec( + getResources().getDisplayMetrics().widthPixels, View.MeasureSpec.AT_MOST); + final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec( + getResources().getDisplayMetrics().heightPixels, View.MeasureSpec.AT_MOST); + + // Icons + BubbleTextView icon = (BubbleTextView) adapter.onCreateViewHolder(this, + AllAppsGridAdapter.VIEW_TYPE_ICON).mContent; + icon.applyDummyInfo(); + icon.measure(widthMeasureSpec, heightMeasureSpec); + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, icon.getMeasuredHeight()); + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON, icon.getMeasuredHeight()); + + // Search divider + View searchDivider = adapter.onCreateViewHolder(this, + AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER).mContent; + searchDivider.measure(widthMeasureSpec, heightMeasureSpec); + int searchDividerHeight = searchDivider.getMeasuredHeight(); + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER, searchDividerHeight); + + // Generic dividers + View divider = adapter.onCreateViewHolder(this, + AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER).mContent; + divider.measure(widthMeasureSpec, heightMeasureSpec); + int dividerHeight = divider.getMeasuredHeight(); + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER, dividerHeight); + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER, dividerHeight); + + // Search views + View emptySearch = adapter.onCreateViewHolder(this, + AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH).mContent; + emptySearch.measure(widthMeasureSpec, heightMeasureSpec); + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, + emptySearch.getMeasuredHeight()); + View searchMarket = adapter.onCreateViewHolder(this, + AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET).mContent; + searchMarket.measure(widthMeasureSpec, heightMeasureSpec); + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET, + searchMarket.getMeasuredHeight()); + + // Section breaks + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SECTION_BREAK, 0); } /** @@ -234,8 +274,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView } // Update the fast scroll - int scrollY = getScrollTop(mScrollPosState); - int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows()); + int scrollY = getCurrentScrollY(); + int availableScrollHeight = getAvailableScrollHeight(); mFastScrollHelper.smoothScrollToSection(scrollY, availableScrollHeight, lastInfo); return lastInfo.sectionName; } @@ -249,6 +289,11 @@ public class AllAppsRecyclerView extends BaseRecyclerView @Override public void setAdapter(Adapter adapter) { super.setAdapter(adapter); + adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { + public void onChanged() { + mCachedScrollPositions.clear(); + } + }); mFastScrollHelper.onSetAdapter((AllAppsGridAdapter) adapter); } @@ -265,17 +310,16 @@ public class AllAppsRecyclerView extends BaseRecyclerView return; } - // Find the index and height of the first visible row (all rows have the same height) - int rowCount = mApps.getNumAppRows(); - getCurScrollState(mScrollPosState, AllAppsGridAdapter.VIEW_TYPE_MASK_ICON); - if (mScrollPosState.rowIndex < 0) { + // Skip early if, there no child laid out in the container. + int scrollY = getCurrentScrollY(); + if (scrollY < 0) { mScrollbar.setThumbOffset(-1, -1); return; } // Only show the scrollbar if there is height to be scrolled int availableScrollBarHeight = getAvailableScrollBarHeight(); - int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows()); + int availableScrollHeight = getAvailableScrollHeight(); if (availableScrollHeight <= 0) { mScrollbar.setThumbOffset(-1, -1); return; @@ -284,7 +328,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView // 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 = getScrollTop(mScrollPosState); int scrollBarY = mBackgroundPadding.top + (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight); @@ -330,55 +373,74 @@ public class AllAppsRecyclerView extends BaseRecyclerView } } } else { - synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount); + synchronizeScrollBarThumbOffsetToViewScroll(scrollY, availableScrollHeight); } } - /** - * Returns the current scroll state of the apps rows. - */ - protected void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask) { - stateOut.rowIndex = -1; - stateOut.rowTopOffset = -1; - stateOut.itemPos = -1; + @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(); + } + @Override + protected int getCurrentScrollY() { // Return early if there are no items or we haven't been measured List<AlphabeticalAppsList.AdapterItem> 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<AlphabeticalAppsList.AdapterItem> 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() { |