diff options
-rw-r--r-- | src/com/android/launcher3/AppsContainerRecyclerView.java | 51 | ||||
-rw-r--r-- | src/com/android/launcher3/AppsContainerView.java | 208 | ||||
-rw-r--r-- | src/com/android/launcher3/BaseContainerRecyclerView.java | 31 |
3 files changed, 127 insertions, 163 deletions
diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index 9584aa283..bd7f645fc 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -122,24 +122,6 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { mApps = apps; } - @Override - public void setAdapter(Adapter adapter) { - // Register a change listener to update the scroll position state whenever the data set - // changes. - adapter.registerAdapterDataObserver(new AdapterDataObserver() { - @Override - public void onChanged() { - post(new Runnable() { - @Override - public void run() { - refreshCurScrollPosition(); - } - }); - } - }); - super.setAdapter(adapter); - } - /** * Sets the number of apps per row in this recycler view. */ @@ -186,7 +168,20 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { */ public void scrollToTop() { scrollToPosition(0); - updateScrollY(0); + } + + /** + * Returns the current scroll position. + */ + public int getScrollPosition() { + List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); + getCurScrollState(mScrollPosState, items); + if (mScrollPosState.rowIndex != -1) { + int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; + return getPaddingTop() + (mScrollPosState.rowIndex * mScrollPosState.rowHeight) + + predictionBarHeight - mScrollPosState.rowTopOffset; + } + return 0; } @Override @@ -357,7 +352,6 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { if (touchFraction <= predictionBarFraction) { // Scroll to the top of the view, where the prediction bar is layoutManager.scrollToPositionWithOffset(0, 0); - updateScrollY(0); return ""; } } @@ -376,9 +370,6 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { lastScrollSection = scrollSection; } - // We need to workaround the RecyclerView to get the right scroll position - refreshCurScrollPosition(); - // Scroll to the view at the position, anchored at the top of the screen. We call the scroll // method on the LayoutManager directly since it is not exposed by RecyclerView. layoutManager.scrollToPositionWithOffset(lastScrollSection.appItem.position, 0); @@ -490,20 +481,6 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { } /** - * Forces a refresh of the scroll position to any scroll listener. - */ - private void refreshCurScrollPosition() { - List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); - getCurScrollState(mScrollPosState, items); - if (mScrollPosState.rowIndex != -1) { - int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; - int scrollY = getPaddingTop() + (mScrollPosState.rowIndex * mScrollPosState.rowHeight) + - predictionBarHeight - mScrollPosState.rowTopOffset; - updateScrollY(scrollY); - } - } - - /** * Returns the current scroll state. */ private void getCurScrollState(ScrollPositionState stateOut, diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 6f7c31351..5ccb6c620 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -35,6 +35,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; @@ -47,12 +48,97 @@ import java.util.regex.Pattern; /** + * Interface for controlling the header elevation in response to RecyclerView scroll. + */ +interface HeaderElevationController { + void onScroll(int scrollY); + void disable(); +} + +/** + * Implementation of the header elevation mechanism for pre-L devices. It simulates elevation + * by drawing a gradient under the header bar. + */ +final class HeaderElevationControllerV16 implements HeaderElevationController { + + private final View mShadow; + + private final float mScrollToElevation; + + public HeaderElevationControllerV16(View header) { + Resources res = header.getContext().getResources(); + mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation); + + mShadow = new View(header.getContext()); + mShadow.setBackground(new GradientDrawable( + GradientDrawable.Orientation.TOP_BOTTOM, new int[] {0x44000000, 0x00000000})); + mShadow.setAlpha(0); + + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + res.getDimensionPixelSize(R.dimen.all_apps_header_shadow_height)); + lp.topMargin = ((FrameLayout.LayoutParams) header.getLayoutParams()).height; + + ((ViewGroup) header.getParent()).addView(mShadow, lp); + } + + @Override + public void onScroll(int scrollY) { + float elevationPct = (float) Math.min(scrollY, mScrollToElevation) / + mScrollToElevation; + mShadow.setAlpha(elevationPct); + } + + @Override + public void disable() { + ViewGroup parent = (ViewGroup) mShadow.getParent(); + if (parent != null) { + parent.removeView(mShadow); + } + } +} + +/** + * Implementation of the header elevation mechanism for L+ devices, which makes use of the native + * view elevation. + */ +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +final class HeaderElevationControllerVL implements HeaderElevationController { + + private final View mHeader; + private final float mMaxElevation; + private final float mScrollToElevation; + + public HeaderElevationControllerVL(View header) { + mHeader = header; + + Resources res = header.getContext().getResources(); + mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation); + mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation); + } + + @Override + public void onScroll(int scrollY) { + float elevationPct = (float) Math.min(scrollY, mScrollToElevation) / + mScrollToElevation; + float newElevation = mMaxElevation * elevationPct; + if (Float.compare(mHeader.getElevation(), newElevation) != 0) { + mHeader.setElevation(newElevation); + } + } + + @Override + public void disable() { } +} + +/** * The all apps view container. */ public class AppsContainerView extends BaseContainerView implements DragSource, Insettable, TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable, AlphabeticalAppsList.FilterChangedCallback, AppsGridAdapter.PredictionBarSpacerCallbacks, - View.OnTouchListener, View.OnClickListener, View.OnLongClickListener { + View.OnTouchListener, View.OnClickListener, View.OnLongClickListener, + ViewTreeObserver.OnPreDrawListener { public static final boolean GRID_MERGE_SECTIONS = true; @@ -96,8 +182,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource, // Normal container insets private int mContainerInset; private int mPredictionBarHeight; - // RecyclerView scroll position - @Thunk int mRecyclerViewScrollY; + private int mLastRecyclerViewScrollPos = -1; private CheckLongPressHelper mPredictionIconCheckForLongPress; private View mPredictionIconUnderTouch; @@ -266,14 +351,6 @@ public class AppsContainerView extends BaseContainerView implements DragSource, mAppsRecyclerView.setLayoutManager(mLayoutManager); mAppsRecyclerView.setAdapter(mAdapter); mAppsRecyclerView.setHasFixedSize(true); - mAppsRecyclerView.setOnScrollListenerProxy( - new BaseContainerRecyclerView.OnScrollToListener() { - @Override - public void onScrolledTo(int x, int y) { - mRecyclerViewScrollY = y; - onRecyclerViewScrolled(); - } - }); if (mItemDecoration != null) { mAppsRecyclerView.addItemDecoration(mItemDecoration); } @@ -283,9 +360,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource, @Override public void onBindPredictionBar() { - if (!updatePredictionBarVisibility()) { - return; - } + updatePredictionBarVisibility(); List<AppInfo> predictedApps = mApps.getPredictedApps(); int childCount = mPredictionBarView.getChildCount(); @@ -401,6 +476,12 @@ public class AppsContainerView extends BaseContainerView implements DragSource, } @Override + public boolean onPreDraw() { + synchronizeToRecyclerViewScrollPosition(mAppsRecyclerView.getScrollPosition()); + return true; + } + + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return handleTouchEvent(ev); } @@ -600,7 +681,11 @@ public class AppsContainerView extends BaseContainerView implements DragSource, @Override public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { - // Do nothing + // Register for a pre-draw listener to synchronize the recycler view scroll to other views + // in this container + if (!toWorkspace) { + getViewTreeObserver().addOnPreDrawListener(this); + } } @Override @@ -620,18 +705,26 @@ public class AppsContainerView extends BaseContainerView implements DragSource, hideSearchField(false, false); } } + if (toWorkspace) { + getViewTreeObserver().removeOnPreDrawListener(this); + mLastRecyclerViewScrollPos = -1; + } } /** * Updates the container when the recycler view is scrolled. */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private void onRecyclerViewScrolled() { - if (DYNAMIC_HEADER_ELEVATION) { - mElevationController.onScroll(mRecyclerViewScrollY); - } + private void synchronizeToRecyclerViewScrollPosition(int scrollY) { + if (mLastRecyclerViewScrollPos != scrollY) { + mLastRecyclerViewScrollPos = scrollY; + if (DYNAMIC_HEADER_ELEVATION) { + mElevationController.onScroll(scrollY); + } - mPredictionBarView.setTranslationY(-mRecyclerViewScrollY + mAppsRecyclerView.getPaddingTop()); + // Scroll the prediction bar with the contents of the recycler view + mPredictionBarView.setTranslationY(-scrollY + mAppsRecyclerView.getPaddingTop()); + } } @Override @@ -867,79 +960,4 @@ public class AppsContainerView extends BaseContainerView implements DragSource, private InputMethodManager getInputMethodManager() { return (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); } - - private static interface HeaderElevationController { - - public void onScroll(int scrollY); - - public void disable(); - } - - private static final class HeaderElevationControllerV16 implements HeaderElevationController { - - private final View mShadow; - - private final float mScrollToElevation; - - public HeaderElevationControllerV16(View header) { - Resources res = header.getContext().getResources(); - mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation); - - mShadow = new View(header.getContext()); - mShadow.setBackground(new GradientDrawable( - GradientDrawable.Orientation.TOP_BOTTOM, new int[] {0x44000000, 0x00000000})); - mShadow.setAlpha(0); - - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( - LayoutParams.MATCH_PARENT, - res.getDimensionPixelSize(R.dimen.all_apps_header_shadow_height)); - lp.topMargin = ((FrameLayout.LayoutParams) header.getLayoutParams()).height; - - ((ViewGroup) header.getParent()).addView(mShadow, lp); - } - - @Override - public void onScroll(int scrollY) { - float elevationPct = (float) Math.min(scrollY, mScrollToElevation) / - mScrollToElevation; - mShadow.setAlpha(elevationPct); - } - - @Override - public void disable() { - ViewGroup parent = (ViewGroup) mShadow.getParent(); - if (parent != null) { - parent.removeView(mShadow); - } - } - } - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private static final class HeaderElevationControllerVL implements HeaderElevationController { - - private final View mHeader; - private final float mMaxElevation; - private final float mScrollToElevation; - - public HeaderElevationControllerVL(View header) { - mHeader = header; - - Resources res = header.getContext().getResources(); - mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation); - mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation); - } - - @Override - public void onScroll(int scrollY) { - float elevationPct = (float) Math.min(scrollY, mScrollToElevation) / - mScrollToElevation; - float newElevation = mMaxElevation * elevationPct; - if (Float.compare(mHeader.getElevation(), newElevation) != 0) { - mHeader.setElevation(newElevation); - } - } - - @Override - public void disable() { } - } } diff --git a/src/com/android/launcher3/BaseContainerRecyclerView.java b/src/com/android/launcher3/BaseContainerRecyclerView.java index 59e20ca2f..e52d88708 100644 --- a/src/com/android/launcher3/BaseContainerRecyclerView.java +++ b/src/com/android/launcher3/BaseContainerRecyclerView.java @@ -29,20 +29,11 @@ import com.android.launcher3.util.Thunk; public class BaseContainerRecyclerView extends RecyclerView implements RecyclerView.OnItemTouchListener { - /** - * Listener to get notified when the absolute scroll changes. - */ - public interface OnScrollToListener { - void onScrolledTo(int x, int y); - } - private static final int SCROLL_DELTA_THRESHOLD_DP = 4; /** Keeps the last known scrolling delta/velocity along y-axis. */ @Thunk int mDy = 0; - @Thunk int mScrollY; private float mDeltaThreshold; - private OnScrollToListener mScrollToListener; public BaseContainerRecyclerView(Context context) { this(context, null); @@ -68,20 +59,9 @@ public class BaseContainerRecyclerView extends RecyclerView @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { mDy = dy; - mScrollY += dy; - if (mScrollToListener != null) { - mScrollToListener.onScrolledTo(0, mScrollY); - } } } - /** - * Sets an additional scroll listener, only needed for LMR1 version of the support lib. - */ - public void setOnScrollListenerProxy(OnScrollToListener listener) { - mScrollToListener = listener; - } - @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -106,17 +86,6 @@ public class BaseContainerRecyclerView extends RecyclerView } /** - * Updates the scroll position, used to workaround a RecyclerView issue with scrolling to - * position. - */ - protected void updateScrollY(int scrollY) { - mScrollY = scrollY; - if (mScrollToListener != null) { - mScrollToListener.onScrolledTo(0, mScrollY); - } - } - - /** * Returns whether this {@link MotionEvent} should trigger the scroll to be stopped. */ protected boolean shouldStopScroll(MotionEvent ev) { |