From 7f7b2f7ee9172a2fb9af77c4ea02a6a00e47e8f9 Mon Sep 17 00:00:00 2001 From: Steven Ng Date: Wed, 3 Mar 2021 14:51:36 +0000 Subject: [PATCH] Fix scrolling bugs in full widgets sheet Bugs: 1. Layout margins were not taken into account 2. Fast scroller offset has a different scale to the recycler view 3. Update padding & margin in onMeasure rather than in onLayout TODO: fast scroller measurement doesn't take into account of expanded item. It naively use the same height for all items from the recycler view adapter. Test: Open full widgets sheet for both work profile and non-work profile setup. Scrollbar initial position is correct. Scroll up and down to confirm views vertical translation is correct. Video: https://drive.google.com/file/d/1kp3iSm23RVk_otBNeYalEPPkWjjWMOY0/view?usp=sharing Bug: 181629430 Change-Id: I398a94510751782e78aa8f426f37b03ecca8ec99 --- ...rchAndRecommendationsScrollController.java | 50 ++++++++++++++----- .../widget/picker/WidgetsFullSheet.java | 27 +++++++--- 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java index a5ed20a22e..95fa05f19e 100644 --- a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java +++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java @@ -16,10 +16,10 @@ package com.android.launcher3.widget.picker; import android.view.View; +import android.view.ViewGroup.MarginLayoutParams; import android.widget.RelativeLayout; import androidx.annotation.Nullable; -import androidx.recyclerview.widget.RecyclerView; import com.android.launcher3.views.RecyclerViewFastScroller; import com.android.launcher3.widget.picker.WidgetsFullSheet.SearchAndRecommendationViewHolder; @@ -33,20 +33,21 @@ final class SearchAndRecommendationsScrollController implements RecyclerViewFastScroller.OnFastScrollChangeListener { private final boolean mHasWorkProfile; private final SearchAndRecommendationViewHolder mViewHolder; - private final RecyclerView mPrimaryRecyclerView; + private final WidgetsRecyclerView mPrimaryRecyclerView; // The following are only non null if mHasWorkProfile is true. - @Nullable private final RecyclerView mWorkRecyclerView; + @Nullable private final WidgetsRecyclerView mWorkRecyclerView; @Nullable private final View mPrimaryWorkTabsView; @Nullable private final PersonalWorkPagedView mPrimaryWorkViewPager; + private WidgetsRecyclerView mCurrentRecyclerView; private int mMaxCollapsibleHeight = 0; SearchAndRecommendationsScrollController( boolean hasWorkProfile, SearchAndRecommendationViewHolder viewHolder, - RecyclerView primaryRecyclerView, - @Nullable RecyclerView workRecyclerView, + WidgetsRecyclerView primaryRecyclerView, + @Nullable WidgetsRecyclerView workRecyclerView, @Nullable View personalWorkTabsView, @Nullable PersonalWorkPagedView primaryWorkViewPager) { mHasWorkProfile = hasWorkProfile; @@ -55,6 +56,12 @@ final class SearchAndRecommendationsScrollController implements mWorkRecyclerView = workRecyclerView; mPrimaryWorkTabsView = personalWorkTabsView; mPrimaryWorkViewPager = primaryWorkViewPager; + mCurrentRecyclerView = mPrimaryRecyclerView; + } + + /** Sets the current active {@link WidgetsRecyclerView}. */ + public void setCurrentRecyclerView(WidgetsRecyclerView currentRecyclerView) { + mCurrentRecyclerView = currentRecyclerView; } /** @@ -64,10 +71,10 @@ final class SearchAndRecommendationsScrollController implements // The maximum vertical distance, in pixels, until the last collapsible element is not // visible from the screen when the user scrolls down the recycler view. mMaxCollapsibleHeight = mViewHolder.mContainer.getPaddingTop() - + mViewHolder.mCollapseHandle.getMeasuredHeight() - + mViewHolder.mHeaderTitle.getMeasuredHeight(); + + measureHeightWithVerticalMargins(mViewHolder.mCollapseHandle) + + measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle); - int topContainerHeight = mViewHolder.mContainer.getMeasuredHeight(); + int topContainerHeight = measureHeightWithVerticalMargins(mViewHolder.mContainer); if (mHasWorkProfile) { // In a work profile setup, the full widget sheet contains the following views: // ------- -| @@ -114,8 +121,8 @@ final class SearchAndRecommendationsScrollController implements // // When the views are first inflated, the sum of topOffsetAfterAllViewsCollapsed and // mMaxCollapsibleDistance should equal to the top container height. - int tabsViewActualHeight = - mPrimaryWorkTabsView.getMeasuredHeight() - mPrimaryWorkTabsView.getPaddingTop(); + int tabsViewActualHeight = measureHeightWithVerticalMargins(mPrimaryWorkTabsView) + - mPrimaryWorkTabsView.getPaddingTop(); int topOffsetAfterAllViewsCollapsed = topContainerHeight + tabsViewActualHeight - mMaxCollapsibleHeight; @@ -149,9 +156,12 @@ final class SearchAndRecommendationsScrollController implements * views (e.g. recycler views, tabs) upon scrolling. */ @Override - public void onThumbOffsetYChanged(int y) { + public void onThumbOffsetYChanged(int unused) { + // Always use the recycler view offset because fast scroller offset has a different scale. + int recyclerViewYOffset = mCurrentRecyclerView.getCurrentScrollY(); + if (recyclerViewYOffset < 0) return; if (mMaxCollapsibleHeight > 0) { - int yDisplacement = Math.max(-y, -mMaxCollapsibleHeight); + int yDisplacement = Math.max(-recyclerViewYOffset, -mMaxCollapsibleHeight); mViewHolder.mHeaderTitle.setTranslationY(yDisplacement); mViewHolder.mSearchBar.setTranslationY(yDisplacement); if (mHasWorkProfile) { @@ -159,4 +169,20 @@ final class SearchAndRecommendationsScrollController implements } } } + + /** Resets any previous view translation. */ + public void reset() { + mViewHolder.mHeaderTitle.setTranslationY(0); + mViewHolder.mSearchBar.setTranslationY(0); + if (mHasWorkProfile) { + mPrimaryWorkTabsView.setTranslationY(0); + } + } + + /** private the height, in pixel, + the vertical margins of a given view. */ + private static int measureHeightWithVerticalMargins(View view) { + MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams(); + return view.getMeasuredHeight() + marginLayoutParams.bottomMargin + + marginLayoutParams.topMargin; + } } diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index 91b79f99c3..52a2fc5ac0 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -149,7 +149,10 @@ public class WidgetsFullSheet extends BaseWidgetSheet @Override public void onActivePageChanged(int currentActivePage) { - mAdapters.get(currentActivePage).mWidgetsRecyclerView.bindFastScrollbar(); + WidgetsRecyclerView currentRecyclerView = + mAdapters.get(currentActivePage).mWidgetsRecyclerView; + currentRecyclerView.bindFastScrollbar(); + mSearchAndRecommendationsScrollController.setCurrentRecyclerView(currentRecyclerView); reset(); } @@ -159,6 +162,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet if (mHasWorkProfile) { mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView.scrollToTop(); } + mSearchAndRecommendationsScrollController.reset(); } @VisibleForTesting @@ -241,6 +245,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.setMaxHorizontalSpansPerRow( maxSpansPerRow); } + + if (mInitialTabsHeight == 0 && mTabsView != null) { + mInitialTabsHeight = measureHeightWithVerticalMargins(mTabsView); + } + + mSearchAndRecommendationsScrollController.updateMarginAndPadding(); } @Override @@ -255,12 +265,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet contentLeft + contentWidth, height); setTranslationShift(mTranslationShift); - - if (mInitialTabsHeight == 0 && mTabsView != null) { - mInitialTabsHeight = mTabsView.getMeasuredHeight(); - } - - mSearchAndRecommendationsScrollController.updateMarginAndPadding(); } @Override @@ -371,7 +375,14 @@ public class WidgetsFullSheet extends BaseWidgetSheet // No need to check work profile here because mInitialTabHeight is always 0 if there is no // work profile. return mInitialTabsHeight - + mSearchAndRecommendationViewHolder.mContainer.getMeasuredHeight(); + + measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mContainer); + } + + /** private the height, in pixel, + the vertical margins of a given view. */ + private static int measureHeightWithVerticalMargins(View view) { + MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams(); + return view.getMeasuredHeight() + marginLayoutParams.bottomMargin + + marginLayoutParams.topMargin; } /** A holder class for holding adapters & their corresponding recycler view. */