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
This commit is contained in:
Steven Ng
2021-03-10 22:40:55 +00:00
parent 976bd24f57
commit 53d1364e8b
5 changed files with 71 additions and 12 deletions

View File

@@ -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">
<ImageView

View File

@@ -108,6 +108,8 @@
<dimen name="widget_cell_vertical_padding">8dp</dimen>
<dimen name="widget_cell_horizontal_padding">16dp</dimen>
<dimen name="widget_list_header_view_vertical_padding">20dp</dimen>
<dimen name="widget_preview_shadow_blur">0.5dp</dimen>
<dimen name="widget_preview_key_shadow_distance">1dp</dimen>
<dimen name="widget_preview_corner_radius">2dp</dimen>

View File

@@ -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;

View File

@@ -122,6 +122,11 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
return mVisibleEntries.size();
}
/** Returns all items that will be drawn in a recycler view. */
public List<WidgetsListBaseEntry> getItems() {
return mVisibleEntries;
}
/** Gets the section name for {@link com.android.launcher3.views.RecyclerViewFastScroller}. */
public String getSectionName(int pos) {
return mVisibleEntries.get(pos).mTitleSectionName;

View File

@@ -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.
*
* <p>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}.
*
* <p>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}.