summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWinson <winsonc@google.com>2016-07-11 18:59:18 -0700
committerPeter Schiller <peterschiller@google.com>2016-07-12 12:16:26 -0700
commitb655b8850064cc4f8e59652b4e3bffc09090769a (patch)
tree35903c193119dbea9f704ad0449f45398ffd1d3e
parent7ed42af3398c1764424ebbf2dd6e2a51e05eaf5f (diff)
downloadandroid_packages_apps_Trebuchet-b655b8850064cc4f8e59652b4e3bffc09090769a.tar.gz
android_packages_apps_Trebuchet-b655b8850064cc4f8e59652b4e3bffc09090769a.tar.bz2
android_packages_apps_Trebuchet-b655b8850064cc4f8e59652b4e3bffc09090769a.zip
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
-rw-r--r--res/drawable/all_apps_divider.xml20
-rw-r--r--res/drawable/all_apps_search_divider.xml20
-rw-r--r--res/layout/all_apps_divider.xml15
-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.xml1
-rw-r--r--src/com/android/launcher3/BaseRecyclerView.java46
-rw-r--r--src/com/android/launcher3/allapps/AllAppsContainerView.java19
-rw-r--r--src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java5
-rw-r--r--src/com/android/launcher3/allapps/AllAppsGridAdapter.java22
-rw-r--r--src/com/android/launcher3/allapps/AllAppsRecyclerView.java166
-rw-r--r--src/com/android/launcher3/widget/WidgetsRecyclerView.java48
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 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<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() {