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.
*/
*/
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
if (touchFraction <= predictionBarFraction) {
// Scroll to the top of the view, where the prediction bar is
layoutManager.scrollToPositionWithOffset(0, 0);
- updateScrollY(0);
return "";
}
}
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);
}
/**
- * 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,
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;
/**
+ * 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;
// 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;
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);
}
@Override
public void onBindPredictionBar() {
- if (!updatePredictionBarVisibility()) {
- return;
- }
+ updatePredictionBarVisibility();
List<AppInfo> predictedApps = mApps.getPredictedApps();
int childCount = mPredictionBarView.getChildCount();
}
@Override
+ public boolean onPreDraw() {
+ synchronizeToRecyclerViewScrollPosition(mAppsRecyclerView.getScrollPosition());
+ return true;
+ }
+
+ @Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return handleTouchEvent(ev);
}
@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
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
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() { }
- }
}