From 53d1364e8bbd3cac5bbc7578d07c6ae6293957e9 Mon Sep 17 00:00:00 2001 From: Steven Ng Date: Wed, 10 Mar 2021 22:40:55 +0000 Subject: [PATCH] Better estimate the height of widget recycler view Test: Expand and collapse apps in the widgets picker. Then, observe the height of the fast scroller is correctly displayed. Bug: 181629430 Change-Id: I9efcf902f8548fc5c8a398609758d43123228e5e --- res/layout/widgets_list_row_header.xml | 2 +- res/values/dimens.xml | 2 + .../widget/picker/WidgetsFullSheet.java | 10 +-- .../widget/picker/WidgetsListAdapter.java | 5 ++ .../widget/picker/WidgetsRecyclerView.java | 64 +++++++++++++++++-- 5 files changed, 71 insertions(+), 12 deletions(-) diff --git a/res/layout/widgets_list_row_header.xml b/res/layout/widgets_list_row_header.xml index 041e0073fd..77b5a9a9b1 100644 --- a/res/layout/widgets_list_row_header.xml +++ b/res/layout/widgets_list_row_header.xml @@ -19,7 +19,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?android:attr/selectableItemBackground" - android:paddingVertical="20dp" + android:paddingVertical="@dimen/widget_list_header_view_vertical_padding" android:orientation="horizontal"> 8dp 16dp + 20dp + 0.5dp 1dp 2dp diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index 330175f653..978c6d82ce 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -385,14 +385,16 @@ public class WidgetsFullSheet extends BaseWidgetSheet @Override public int getHeaderViewHeight() { - // No need to check work profile here because mInitialTabHeight is always 0 if there is no - // work profile. - return mInitialTabsHeight - + measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mContainer); + return measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mCollapseHandle) + + measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mHeaderTitle) + + measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mSearchBar); } /** private the height, in pixel, + the vertical margins of a given view. */ private static int measureHeightWithVerticalMargins(View view) { + if (view.getVisibility() != VISIBLE) { + return 0; + } MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams(); return view.getMeasuredHeight() + marginLayoutParams.bottomMargin + marginLayoutParams.topMargin; diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java index 8b49d1ef15..00c1f8bd7a 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java @@ -122,6 +122,11 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC return mVisibleEntries.size(); } + /** Returns all items that will be drawn in a recycler view. */ + public List getItems() { + return mVisibleEntries; + } + /** Gets the section name for {@link com.android.launcher3.views.RecyclerViewFastScroller}. */ public String getSectionName(int pos) { return mVisibleEntries.get(pos).mTitleSectionName; diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java index d65a8098f9..9ab64247a7 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java @@ -21,13 +21,19 @@ import android.graphics.Point; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; +import android.widget.TableLayout; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener; import com.android.launcher3.BaseRecyclerView; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; +import com.android.launcher3.views.ActivityContext; +import com.android.launcher3.widget.model.WidgetsListBaseEntry; +import com.android.launcher3.widget.model.WidgetsListContentEntry; +import com.android.launcher3.widget.model.WidgetsListHeaderEntry; /** * The widgets recycler view. @@ -39,8 +45,10 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch private final int mScrollbarTop; private final Point mFastScrollerOffset = new Point(); + private final int mEstimatedWidgetListHeaderHeight; private boolean mTouchDownOnScroller; private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider; + private int mLastVisibleWidgetContentTableHeight = 0; public WidgetsRecyclerView(Context context) { this(context, null); @@ -55,6 +63,12 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch super(context, attrs, defStyleAttr); mScrollbarTop = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin); addOnItemTouchListener(this); + + ActivityContext activity = ActivityContext.lookupContext(getContext()); + DeviceProfile grid = activity.getDeviceProfile(); + mEstimatedWidgetListHeaderHeight = grid.iconSizePx + + 2 * context.getResources().getDimensionPixelSize( + R.dimen.widget_list_header_view_vertical_padding); } @Override @@ -123,21 +137,32 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch View child = getChildAt(0); int rowIndex = getChildPosition(child); - int y = (child.getMeasuredHeight() * rowIndex); + for (int i = 0; i < getChildCount(); i++) { + View view = getChildAt(i); + if (view instanceof TableLayout) { + // This assumes there is ever only one content shown in this recycler view. + mLastVisibleWidgetContentTableHeight = view.getMeasuredHeight(); + } + } + + int scrollPosition = getItemsHeight(rowIndex); int offset = getLayoutManager().getDecoratedTop(child); - return getPaddingTop() + y - offset; + return getPaddingTop() + scrollPosition - offset; } /** - * Returns the available scroll height: - * AvailableScrollHeight = Total height of the all items - last page height + * Returns the available scroll height, in pixel. + * + *

If the recycler view can't be scrolled, returns 0. */ @Override protected int getAvailableScrollHeight() { - View child = getChildAt(0); - return child.getMeasuredHeight() * mAdapter.getItemCount() + getScrollBarTop() - + getPaddingBottom() - mScrollbar.getHeight(); + // AvailableScrollHeight = Total height of the all items - first page height + int firstPageHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); + int totalHeightOfAllItems = getItemsHeight(/* untilIndex= */ mAdapter.getItemCount()); + int availableScrollHeight = totalHeightOfAllItems - firstPageHeight; + return Math.max(0, availableScrollHeight); } private boolean isModelNotReady() { @@ -180,6 +205,31 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch mHeaderViewDimensionsProvider = headerViewDimensionsProvider; } + /** + * Returns the sum of the height, in pixels, of this list adapter's items from index 0 until + * {@code untilIndex}. + * + *

If the untilIndex is larger than the total number of items in this adapter, returns the + * sum of all items' height. + */ + private int getItemsHeight(int untilIndex) { + if (untilIndex > mAdapter.getItems().size()) { + untilIndex = mAdapter.getItems().size(); + } + int totalItemsHeight = 0; + for (int i = 0; i < untilIndex; i++) { + WidgetsListBaseEntry entry = mAdapter.getItems().get(i); + if (entry instanceof WidgetsListHeaderEntry) { + totalItemsHeight += mEstimatedWidgetListHeaderHeight; + } else if (entry instanceof WidgetsListContentEntry) { + totalItemsHeight += mLastVisibleWidgetContentTableHeight; + } else { + throw new UnsupportedOperationException("Can't estimate height for " + entry); + } + } + return totalItemsHeight; + } + /** * Provides dimensions of the header view that is shown at the top of a * {@link WidgetsRecyclerView}.