diff options
Diffstat (limited to 'src/com/android/launcher3/allapps/AllAppsRecyclerView.java')
-rw-r--r-- | src/com/android/launcher3/allapps/AllAppsRecyclerView.java | 166 |
1 files changed, 114 insertions, 52 deletions
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(); } /** |