diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2017-06-23 16:12:50 -0700 |
---|---|---|
committer | Sunny Goyal <sunnygoyal@google.com> | 2017-06-26 14:56:36 -0700 |
commit | 89d5c5a31bd6cf4caf815b680ec670896b91803d (patch) | |
tree | b9d0a9a9fd6dec880fb6076bc2b8d4f38870839f /src/com/android/launcher3/allapps | |
parent | bbe504d24d5e0757d1a7772af822b7a6e274c9b4 (diff) | |
download | android_packages_apps_Trebuchet-89d5c5a31bd6cf4caf815b680ec670896b91803d.tar.gz android_packages_apps_Trebuchet-89d5c5a31bd6cf4caf815b680ec670896b91803d.tar.bz2 android_packages_apps_Trebuchet-89d5c5a31bd6cf4caf815b680ec670896b91803d.zip |
Updating fast scrollbar UI in Landscape
Creating a separate view for FastScrollBar and moving all the relavant logic in
the view.
For protrait, the touch handling is delegated by the recycler view just like before.
For landscape, the dcrollbar does not overlay with recyclerView and handles the touch
itself
Bug: 37015359
Change-Id: Ie1981326457ba739bdf0ac8063db1065f395f133
Diffstat (limited to 'src/com/android/launcher3/allapps')
7 files changed, 109 insertions, 52 deletions
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 4954e0c44..47b68a2ee 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -171,19 +171,19 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc * Returns whether the view itself will handle the touch event or not. */ public boolean shouldContainerScroll(MotionEvent ev) { - int[] point = new int[2]; - point[0] = (int) ev.getX(); - point[1] = (int) ev.getY(); - Utilities.mapCoordInSelfToDescendant(mAppsRecyclerView, this, point); - // IF the MotionEvent is inside the search box, and the container keeps on receiving // touch input, container should move down. if (mLauncher.getDragLayer().isEventOverView(mSearchContainer, ev)) { return true; } + int[] point = new int[2]; + point[0] = (int) ev.getX(); + point[1] = (int) ev.getY(); + Utilities.mapCoordInSelfToDescendant( + mAppsRecyclerView.getScrollBar(), mLauncher.getDragLayer(), point); // IF the MotionEvent is inside the thumb, container should not be pulled down. - if (mAppsRecyclerView.getScrollBar().isNearThumb(point[0], point[1])) { + if (mAppsRecyclerView.getScrollBar().shouldBlockIntercept(point[0], point[1])) { return false; } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index d6514a83f..1054a5633 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -69,16 +69,13 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. // A divider that separates the apps list and the search market button public static final int VIEW_TYPE_SEARCH_MARKET_DIVIDER = 1 << 5; - // The divider under the search field - public static final int VIEW_TYPE_SEARCH_DIVIDER = 1 << 6; // The divider that separates prediction icons from the app list - public static final int VIEW_TYPE_PREDICTION_DIVIDER = 1 << 7; - public static final int VIEW_TYPE_APPS_LOADING_DIVIDER = 1 << 8; - public static final int VIEW_TYPE_DISCOVERY_ITEM = 1 << 9; + public static final int VIEW_TYPE_PREDICTION_DIVIDER = 1 << 6; + public static final int VIEW_TYPE_APPS_LOADING_DIVIDER = 1 << 7; + public static final int VIEW_TYPE_DISCOVERY_ITEM = 1 << 8; // Common view type masks - public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_SEARCH_DIVIDER - | VIEW_TYPE_SEARCH_MARKET_DIVIDER + public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_SEARCH_MARKET_DIVIDER | VIEW_TYPE_PREDICTION_DIVIDER; public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON | VIEW_TYPE_PREDICTION_ICON; @@ -319,9 +316,6 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. } }); return new ViewHolder(searchMarketView); - case VIEW_TYPE_SEARCH_DIVIDER: - return new ViewHolder(mLayoutInflater.inflate( - R.layout.all_apps_search_divider, parent, false)); case VIEW_TYPE_APPS_LOADING_DIVIDER: View loadingDividerView = mLayoutInflater.inflate( R.layout.all_apps_discovery_loading_divider, parent, false); diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index 0607a1e5d..2b2fddcdd 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -72,7 +72,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView { super(context, attrs, defStyleAttr); Resources res = getResources(); addOnItemTouchListener(this); - mScrollbar.setDetachThumbOnFastScroll(); mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize( R.dimen.all_apps_empty_search_bg_top_offset); } @@ -110,7 +109,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView { RecyclerView.RecycledViewPool pool = getRecycledViewPool(); int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx); pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 1); - pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER, 1); pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER, 1); pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET, 1); pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows * mNumAppsPerRow); @@ -137,8 +135,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView { AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER, AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER); putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec, - AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER); - putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec, AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET); putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec, AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH); @@ -164,7 +160,9 @@ public class AllAppsRecyclerView extends BaseRecyclerView { */ public void scrollToTop() { // Ensure we reattach the scrollbar if it was previously detached while fast-scrolling - mScrollbar.reattachThumbToScroll(); + if (mScrollbar != null) { + mScrollbar.reattachThumbToScroll(); + } scrollToPosition(0); } @@ -356,7 +354,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView { } @Override - protected boolean supportsFastScrolling() { + public boolean supportsFastScrolling() { // Only allow fast scrolling when the user is not searching, since the results are not // grouped in a meaningful order return !mApps.hasFilter(); diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 7bf66510a..608e898ae 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -138,13 +138,6 @@ public class AlphabeticalAppsList { return item; } - public static AdapterItem asSearchDivider(int pos) { - AdapterItem item = new AdapterItem(); - item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER; - item.position = pos; - return item; - } - public static AdapterItem asMarketDivider(int pos) { AdapterItem item = new AdapterItem(); item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER; @@ -195,8 +188,6 @@ public class AlphabeticalAppsList { private int mNumPredictedAppsPerRow; private int mNumAppRowsInAdapter; - private boolean mHasSearchDivider = true; - public AlphabeticalAppsList(Context context) { mLauncher = Launcher.getLauncher(context); mIndexer = new AlphabeticIndexCompat(context); @@ -352,10 +343,6 @@ public class AlphabeticalAppsList { onAppsUpdated(); } - public void disableSearchDivider() { - mHasSearchDivider = false; - } - /** * Updates internals when the set of apps are updated. */ @@ -442,11 +429,6 @@ public class AlphabeticalAppsList { } } - if (mHasSearchDivider) { - // Add the search divider - mAdapterItems.add(AdapterItem.asSearchDivider(position++)); - } - // Process the predicted app components mPredictedApps.clear(); if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) { diff --git a/src/com/android/launcher3/allapps/LandscapeFastScroller.java b/src/com/android/launcher3/allapps/LandscapeFastScroller.java new file mode 100644 index 000000000..cdde65760 --- /dev/null +++ b/src/com/android/launcher3/allapps/LandscapeFastScroller.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.allapps; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; + +import com.android.launcher3.views.RecyclerViewFastScroller; + +/** + * Extension of {@link RecyclerViewFastScroller} to be used in landscape layout. + */ +public class LandscapeFastScroller extends RecyclerViewFastScroller { + + public LandscapeFastScroller(Context context) { + super(context); + } + + public LandscapeFastScroller(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LandscapeFastScroller(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public boolean handleTouchEvent(MotionEvent ev) { + // We handle our own touch event, no need to handle recycler view touch delegates. + return false; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + event.offsetLocation(0, -mRv.getPaddingTop()); + if (super.handleTouchEvent(event)) { + getParent().requestDisallowInterceptTouchEvent(true); + } + event.offsetLocation(0, mRv.getPaddingTop()); + return true; + } + + @Override + public boolean shouldBlockIntercept(int x, int y) { + // If the user touched the scroll bar area, block swipe + return x >= 0 && x < getWidth() && y >= 0 && y < getHeight(); + } +} diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java index 3f06ec9dd..5cb12d592 100644 --- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java +++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java @@ -54,12 +54,13 @@ public class AppsSearchContainerLayout extends FrameLayout private final int mSearchBoxHeight; private final AllAppsSearchBarController mSearchBarController; private final SpannableStringBuilder mSearchQueryBuilder; - private final HeaderElevationController mElevationController; private ExtendedEditText mSearchInput; private AlphabeticalAppsList mApps; private AllAppsRecyclerView mAppsRecyclerView; private AllAppsGridAdapter mAdapter; + private View mDivider; + private HeaderElevationController mElevationController; public AppsSearchContainerLayout(Context context) { this(context, null); @@ -77,7 +78,6 @@ public class AppsSearchContainerLayout extends FrameLayout mSearchBoxHeight = getResources() .getDimensionPixelSize(R.dimen.all_apps_search_bar_field_height); mSearchBarController = new AllAppsSearchBarController(); - mElevationController = new HeaderElevationController(this); mSearchQueryBuilder = new SpannableStringBuilder(); Selection.setSelection(mSearchQueryBuilder, 0); @@ -87,6 +87,8 @@ public class AppsSearchContainerLayout extends FrameLayout protected void onFinishInflate() { super.onFinishInflate(); mSearchInput = findViewById(R.id.search_box_input); + mDivider = findViewById(R.id.search_divider); + mElevationController = new HeaderElevationController(mDivider); // Update the hint to contain the icon. // Prefix the original hint with two spaces. The first space gets replaced by the icon @@ -96,6 +98,12 @@ public class AppsSearchContainerLayout extends FrameLayout spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search), 0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE); mSearchInput.setHint(spanned); + + DeviceProfile dp = mLauncher.getDeviceProfile(); + if (!dp.isVerticalBarLayout()) { + LayoutParams lp = (LayoutParams) mDivider.getLayoutParams(); + lp.leftMargin = lp.rightMargin = dp.edgeMarginPx; + } } @Override diff --git a/src/com/android/launcher3/allapps/search/HeaderElevationController.java b/src/com/android/launcher3/allapps/search/HeaderElevationController.java index ab4e88fc8..7cd32b26e 100644 --- a/src/com/android/launcher3/allapps/search/HeaderElevationController.java +++ b/src/com/android/launcher3/allapps/search/HeaderElevationController.java @@ -4,11 +4,11 @@ import android.content.res.Resources; import android.graphics.Outline; import android.support.v7.widget.RecyclerView; import android.view.View; +import android.view.ViewGroup; import android.view.ViewOutlineProvider; import com.android.launcher3.BaseRecyclerView; import com.android.launcher3.R; -import com.android.launcher3.Utilities; /** * Helper class for controlling the header elevation in response to RecyclerView scroll. @@ -16,6 +16,7 @@ import com.android.launcher3.Utilities; public class HeaderElevationController extends RecyclerView.OnScrollListener { private final View mHeader; + private final View mHeaderChild; private final float mMaxElevation; private final float mScrollToElevation; @@ -28,23 +29,27 @@ public class HeaderElevationController extends RecyclerView.OnScrollListener { mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation); // We need to provide a custom outline so the shadow only appears on the bottom edge. - // The top, left and right edges are all extended out, and the shadow is clipped - // by the parent. + // The top, left and right edges are all extended out to match parent's edge, so that + // the shadow is clipped by the parent. final ViewOutlineProvider vop = new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { - final View parent = (View) mHeader.getParent(); + // Set the left and top to be at the parents edge. Since the coordinates are + // relative to this view, + // (x = -view.getLeft()) for this view => (x = 0) for parent + final int left = -view.getLeft(); + final int top = -view.getTop(); - final int left = parent.getLeft(); // Use the parent to account for offsets - final int top = view.getTop(); - final int right = left + view.getWidth(); - final int bottom = view.getBottom(); - - final int offset = Utilities.pxFromDp(mMaxElevation, res.getDisplayMetrics()); + // Since the view is centered align, the spacing on left and right are same. + // Add same spacing on the right to reach parent's edge. + final int right = view.getWidth() - left; + final int bottom = view.getHeight(); + final int offset = (int) mMaxElevation; outline.setRect(left - offset, top - offset, right + offset, bottom); } }; mHeader.setOutlineProvider(vop); + mHeaderChild = ((ViewGroup) mHeader).getChildAt(0); } public void reset() { @@ -63,6 +68,13 @@ public class HeaderElevationController extends RecyclerView.OnScrollListener { float newElevation = mMaxElevation * elevationPct; if (Float.compare(mHeader.getElevation(), newElevation) != 0) { mHeader.setElevation(newElevation); + + // To simulate a scrolling effect for the header, we translate the header down, and + // its content up by the same amount, so that it gets clipped by the parent, making it + // look like the content was scrolled out of the view. + int shift = Math.min(mHeader.getHeight(), scrollY); + mHeader.setTranslationY(-shift); + mHeaderChild.setTranslationY(shift); } } |