summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/allapps/AllAppsRecyclerView.java')
-rw-r--r--src/com/android/launcher3/allapps/AllAppsRecyclerView.java166
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();
}
/**