Merge "Updating fast scrollbar UI in Landscape" into ub-launcher3-dorval-polish
This commit is contained in:
commit
b647302a90
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<merge
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto">
|
||||
<!-- Fast scroller popup -->
|
||||
<TextView
|
||||
android:id="@+id/fast_scroller_popup"
|
||||
style="@style/FastScrollerPopup"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignTop="@+id/apps_list_view"
|
||||
android:layout_marginEnd="-5dp" />
|
||||
|
||||
<com.android.launcher3.allapps.LandscapeFastScroller
|
||||
android:id="@+id/fast_scroller"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignTop="@+id/apps_list_view"
|
||||
android:layout_marginEnd="-48dp"
|
||||
android:layout_marginTop="-8dp"
|
||||
launcher:canThumbDetach="true" />
|
||||
|
||||
</merge>
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<merge
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto">
|
||||
<!-- Fast scroller popup -->
|
||||
<TextView
|
||||
android:id="@+id/fast_scroller_popup"
|
||||
style="@style/FastScrollerPopup"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignTop="@+id/apps_list_view"
|
||||
android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
|
||||
|
||||
<com.android.launcher3.views.RecyclerViewFastScroller
|
||||
android:id="@+id/fast_scroller"
|
||||
android:layout_width="@dimen/fastscroll_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignTop="@+id/apps_list_view"
|
||||
android:layout_marginEnd="@dimen/fastscroll_end_margin"
|
||||
launcher:canThumbDetach="true" />
|
||||
|
||||
</merge>
|
|
@ -16,7 +16,8 @@
|
|||
<!-- The top and bottom paddings are defined in this container, but since we want
|
||||
the list view to span the full width (for touch interception purposes), we
|
||||
will bake the left/right padding into that view's background itself. -->
|
||||
<com.android.launcher3.allapps.AllAppsContainerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<com.android.launcher3.allapps.AllAppsContainerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/apps_view"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -39,6 +40,8 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:focusable="true"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:saveEnabled="false"
|
||||
android:visibility="gone">
|
||||
|
@ -55,20 +58,14 @@
|
|||
android:descendantFocusability="afterDescendants"
|
||||
android:focusable="true" />
|
||||
|
||||
<!-- Fast scroller popup -->
|
||||
<TextView
|
||||
style="@style/FastScrollerPopup"
|
||||
android:layout_alignTop="@+id/apps_list_view"
|
||||
android:id="@+id/fast_scroller_popup"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
|
||||
|
||||
<!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
|
||||
platform bug, which prevents using custom attributes in <include> tag -->
|
||||
<include
|
||||
layout="?android:attr/keyboardLayout"
|
||||
android:id="@+id/search_container" />
|
||||
|
||||
<include layout="@layout/all_apps_fast_scroller" />
|
||||
|
||||
</com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
|
||||
<View
|
||||
style="@style/AllAppsNavBarProtection"
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<merge
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto">
|
||||
<!-- Fast scroller popup -->
|
||||
<TextView
|
||||
android:id="@+id/fast_scroller_popup"
|
||||
style="@style/FastScrollerPopup"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignTop="@+id/apps_list_view"
|
||||
android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
|
||||
|
||||
<com.android.launcher3.views.RecyclerViewFastScroller
|
||||
android:id="@+id/fast_scroller"
|
||||
android:layout_width="@dimen/fastscroll_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignTop="@+id/apps_list_view"
|
||||
android:layout_marginEnd="@dimen/fastscroll_end_margin"
|
||||
launcher:canThumbDetach="true" />
|
||||
|
||||
</merge>
|
|
@ -20,13 +20,24 @@
|
|||
android:layout_height="@dimen/all_apps_search_bar_height"
|
||||
android:layout_gravity="center|top"
|
||||
android:gravity="center|bottom"
|
||||
android:saveEnabled="false">
|
||||
android:saveEnabled="false"
|
||||
android:paddingLeft="@dimen/dynamic_grid_edge_margin"
|
||||
android:paddingRight="@dimen/dynamic_grid_edge_margin"
|
||||
android:layout_marginBottom="-8dp" >
|
||||
|
||||
<!--
|
||||
Note: The following relation should always be true so that the shadows are aligned properly
|
||||
search_box_input.layout_marginBottom
|
||||
== search_divider.layout_marginBottom
|
||||
== - (search_container.layout_marginBottom)
|
||||
>= 5dp
|
||||
-->
|
||||
<com.android.launcher3.ExtendedEditText
|
||||
android:id="@+id/search_box_input"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/all_apps_search_bar_field_height"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:focusableInTouchMode="true"
|
||||
android:gravity="center"
|
||||
|
@ -39,4 +50,19 @@
|
|||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textColorHint="@drawable/all_apps_search_hint"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<!-- This needs to be a container with a view, 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 divider was scrolled out of the view. -->
|
||||
<FrameLayout
|
||||
android:id="@+id/search_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginBottom="8dp" >
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@drawable/all_apps_search_divider"/>
|
||||
</FrameLayout>
|
||||
</com.android.launcher3.allapps.search.AppsSearchContainerLayout>
|
|
@ -1,25 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2015 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.
|
||||
-->
|
||||
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:importantForAccessibility="no"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="@dimen/all_apps_divider_margin_vertical"
|
||||
android:paddingLeft="@dimen/dynamic_grid_edge_margin"
|
||||
android:paddingRight="@dimen/dynamic_grid_edge_margin"
|
||||
android:src="@drawable/all_apps_search_divider"
|
||||
android:scaleType="fitXY"
|
||||
android:focusable="false" />
|
|
@ -23,25 +23,25 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
launcher:revealBackground="@drawable/round_rect_primary"
|
||||
android:theme="?attr/widgetsTheme">
|
||||
android:theme="?attr/widgetsTheme"
|
||||
launcher:revealBackground="@drawable/round_rect_primary">
|
||||
|
||||
<View
|
||||
android:id="@+id/reveal_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:focusable="false"
|
||||
android:elevation="2dp"
|
||||
android:focusable="false"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/main_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:elevation="15dp"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:visibility="gone">
|
||||
|
||||
<com.android.launcher3.widget.WidgetsRecyclerView
|
||||
android:id="@+id/widgets_list_view"
|
||||
|
@ -50,17 +50,24 @@
|
|||
|
||||
<!-- Fast scroller popup -->
|
||||
<TextView
|
||||
style="@style/FastScrollerPopup"
|
||||
android:id="@+id/fast_scroller_popup"
|
||||
style="@style/FastScrollerPopup"
|
||||
android:layout_gravity="top|end"
|
||||
android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/loader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/loader"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.android.launcher3.views.RecyclerViewFastScroller
|
||||
android:id="@+id/fast_scroller"
|
||||
android:layout_width="@dimen/fastscroll_width"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginEnd="@dimen/fastscroll_end_margin" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
</com.android.launcher3.widget.WidgetsContainerView>
|
|
@ -138,4 +138,8 @@
|
|||
<attr name="android:elevation" />
|
||||
<attr name="darkTintColor" format="color"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="RecyclerViewFastScroller">
|
||||
<attr name="canThumbDetach" format="boolean" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
|
|
@ -65,7 +65,15 @@
|
|||
<dimen name="fastscroll_popup_text_size">32dp</dimen>
|
||||
<dimen name="fastscroll_popup_margin">19dp</dimen>
|
||||
|
||||
<!-- All Apps -->
|
||||
<!--
|
||||
Fast scroller draws the content horizontally centered. The end of the track should be
|
||||
aligned at the end of the container.
|
||||
fastscroll_end_margin = - (fastscroll_width - fastscroll_track_min_width) / 2
|
||||
-->
|
||||
<dimen name="fastscroll_width">58dp</dimen>
|
||||
<dimen name="fastscroll_end_margin">-26dp</dimen>
|
||||
|
||||
<!-- All Apps -->
|
||||
<dimen name="all_apps_button_scale_down">0dp</dimen>
|
||||
<dimen name="all_apps_search_bar_field_height">48dp</dimen>
|
||||
<dimen name="all_apps_search_bar_height">60dp</dimen>
|
||||
|
|
|
@ -22,8 +22,9 @@ import android.support.v7.widget.RecyclerView;
|
|||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.launcher3.util.Thunk;
|
||||
import com.android.launcher3.views.RecyclerViewFastScroller;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -36,19 +37,7 @@ import com.android.launcher3.util.Thunk;
|
|||
public abstract class BaseRecyclerView extends RecyclerView
|
||||
implements RecyclerView.OnItemTouchListener {
|
||||
|
||||
private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
|
||||
|
||||
/** Keeps the last known scrolling delta/velocity along y-axis. */
|
||||
@Thunk int mDy = 0;
|
||||
private float mDeltaThreshold;
|
||||
|
||||
protected final BaseRecyclerViewFastScrollBar mScrollbar;
|
||||
|
||||
private int mDownX;
|
||||
private int mDownY;
|
||||
private int mLastY;
|
||||
|
||||
private boolean mScrollBarVisible = true;
|
||||
protected RecyclerViewFastScroller mScrollbar;
|
||||
|
||||
public BaseRecyclerView(Context context) {
|
||||
this(context, null);
|
||||
|
@ -60,28 +49,6 @@ public abstract class BaseRecyclerView extends RecyclerView
|
|||
|
||||
public BaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;
|
||||
mScrollbar = new BaseRecyclerViewFastScrollBar(this, getResources());
|
||||
|
||||
ScrollListener listener = new ScrollListener();
|
||||
setOnScrollListener(listener);
|
||||
}
|
||||
|
||||
private class ScrollListener extends OnScrollListener {
|
||||
public ScrollListener() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
mDy = dy;
|
||||
|
||||
// TODO(winsonc): If we want to animate the section heads while scrolling, we can
|
||||
// initiate that here if the recycler view scroll state is not
|
||||
// RecyclerView.SCROLL_STATE_IDLE.
|
||||
|
||||
onUpdateScrollbar(dy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -93,7 +60,9 @@ public abstract class BaseRecyclerView extends RecyclerView
|
|||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
mScrollbar.setPopupView(((ViewGroup) getParent()).findViewById(R.id.fast_scroller_popup));
|
||||
ViewGroup parent = (ViewGroup) getParent();
|
||||
mScrollbar = parent.findViewById(R.id.fast_scroller);
|
||||
mScrollbar.setRecyclerView(this, (TextView) parent.findViewById(R.id.fast_scroller_popup));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -115,57 +84,25 @@ public abstract class BaseRecyclerView extends RecyclerView
|
|||
* it is already showing).
|
||||
*/
|
||||
private boolean handleTouchEvent(MotionEvent ev) {
|
||||
ev.offsetLocation(0, -getPaddingTop());
|
||||
int action = ev.getAction();
|
||||
int x = (int) ev.getX();
|
||||
int y = (int) ev.getY();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// Keep track of the down positions
|
||||
mDownX = x;
|
||||
mDownY = mLastY = y;
|
||||
if (shouldStopScroll(ev)) {
|
||||
stopScroll();
|
||||
}
|
||||
mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
mLastY = y;
|
||||
mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
onFastScrollCompleted();
|
||||
mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
|
||||
break;
|
||||
// Move to mScrollbar's coordinate system.
|
||||
int left = getLeft() - mScrollbar.getLeft();
|
||||
int top = getTop() - mScrollbar.getTop();
|
||||
ev.offsetLocation(left, top);
|
||||
try {
|
||||
return mScrollbar.handleTouchEvent(ev);
|
||||
} finally {
|
||||
ev.offsetLocation(-left, -top);
|
||||
}
|
||||
ev.offsetLocation(0, getPaddingTop());
|
||||
return mScrollbar.isDraggingThumb();
|
||||
}
|
||||
|
||||
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
|
||||
// DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this {@link MotionEvent} should trigger the scroll to be stopped.
|
||||
*/
|
||||
protected boolean shouldStopScroll(MotionEvent ev) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
if ((Math.abs(mDy) < mDeltaThreshold &&
|
||||
getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
|
||||
// now the touch events are being passed to the {@link WidgetCell} until the
|
||||
// touch sequence goes over the touch slop.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the fast scroll bar
|
||||
*/
|
||||
protected int getScrollbarTrackHeight() {
|
||||
public int getScrollbarTrackHeight() {
|
||||
return getHeight() - getPaddingTop() - getPaddingBottom();
|
||||
}
|
||||
|
||||
|
@ -187,25 +124,14 @@ public abstract class BaseRecyclerView extends RecyclerView
|
|||
/**
|
||||
* Returns the scrollbar for this recycler view.
|
||||
*/
|
||||
public BaseRecyclerViewFastScrollBar getScrollBar() {
|
||||
public RecyclerViewFastScroller getScrollBar() {
|
||||
return mScrollbar;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
onUpdateScrollbar(0);
|
||||
super.dispatchDraw(canvas);
|
||||
if (mScrollBarVisible) {
|
||||
onUpdateScrollbar(0);
|
||||
mScrollbar.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scrollbar visibility. The call does not refresh the UI, its the responsibility
|
||||
* of the caller to call {@link #invalidate()}.
|
||||
*/
|
||||
public void setScrollBarVisible(boolean visible) {
|
||||
mScrollBarVisible = visible;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,7 +162,7 @@ public abstract class BaseRecyclerView extends RecyclerView
|
|||
/**
|
||||
* @return whether fast scrolling is supported in the current state.
|
||||
*/
|
||||
protected boolean supportsFastScrolling() {
|
||||
public boolean supportsFastScrolling() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -252,16 +178,16 @@ public abstract class BaseRecyclerView extends RecyclerView
|
|||
* Maps the touch (from 0..1) to the adapter position that should be visible.
|
||||
* <p>Override in each subclass of this base class.
|
||||
*/
|
||||
protected abstract String scrollToPositionAtProgress(float touchFraction);
|
||||
public abstract String scrollToPositionAtProgress(float touchFraction);
|
||||
|
||||
/**
|
||||
* Updates the bounds for the scrollbar.
|
||||
* <p>Override in each subclass of this base class.
|
||||
*/
|
||||
protected abstract void onUpdateScrollbar(int dy);
|
||||
public abstract void onUpdateScrollbar(int dy);
|
||||
|
||||
/**
|
||||
* <p>Override in each subclass of this base class.
|
||||
*/
|
||||
protected void onFastScrollCompleted() {}
|
||||
public void onFastScrollCompleted() {}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
@ -136,8 +134,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
|
|||
putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
|
||||
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,
|
||||
|
@ -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();
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* 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.
|
||||
|
@ -13,21 +13,26 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3;
|
||||
|
||||
package com.android.launcher3.views;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Property;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.launcher3.BaseRecyclerView;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.graphics.FastScrollThumbDrawable;
|
||||
import com.android.launcher3.util.Themes;
|
||||
|
@ -35,18 +40,20 @@ import com.android.launcher3.util.Themes;
|
|||
/**
|
||||
* The track and scrollbar that shows when you scroll the list.
|
||||
*/
|
||||
public class BaseRecyclerViewFastScrollBar {
|
||||
public class RecyclerViewFastScroller extends View {
|
||||
|
||||
private static final Property<BaseRecyclerViewFastScrollBar, Integer> TRACK_WIDTH =
|
||||
new Property<BaseRecyclerViewFastScrollBar, Integer>(Integer.class, "width") {
|
||||
private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
|
||||
|
||||
private static final Property<RecyclerViewFastScroller, Integer> TRACK_WIDTH =
|
||||
new Property<RecyclerViewFastScroller, Integer>(Integer.class, "width") {
|
||||
|
||||
@Override
|
||||
public Integer get(BaseRecyclerViewFastScrollBar scrollBar) {
|
||||
public Integer get(RecyclerViewFastScroller scrollBar) {
|
||||
return scrollBar.mWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(BaseRecyclerViewFastScrollBar scrollBar, Integer value) {
|
||||
public void set(RecyclerViewFastScroller scrollBar, Integer value) {
|
||||
scrollBar.setTrackWidth(value);
|
||||
}
|
||||
};
|
||||
|
@ -55,93 +62,113 @@ public class BaseRecyclerViewFastScrollBar {
|
|||
private final static int SCROLL_BAR_VIS_DURATION = 150;
|
||||
private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 0.75f;
|
||||
|
||||
private final Rect mTmpRect = new Rect();
|
||||
private final BaseRecyclerView mRv;
|
||||
|
||||
private final boolean mIsRtl;
|
||||
|
||||
// The inset is the buffer around which a point will still register as a click on the scrollbar
|
||||
private final int mTouchInset;
|
||||
|
||||
private final int mMinWidth;
|
||||
private final int mMaxWidth;
|
||||
private final int mThumbPadding;
|
||||
|
||||
/** Keeps the last known scrolling delta/velocity along y-axis. */
|
||||
private int mDy = 0;
|
||||
private final float mDeltaThreshold;
|
||||
|
||||
private final ViewConfiguration mConfig;
|
||||
|
||||
// Current width of the track
|
||||
private int mWidth;
|
||||
private ObjectAnimator mWidthAnimator;
|
||||
|
||||
private final Paint mThumbPaint;
|
||||
private final int mThumbHeight;
|
||||
protected final int mThumbHeight;
|
||||
|
||||
private final Paint mTrackPaint;
|
||||
|
||||
private float mLastTouchY;
|
||||
private boolean mIsDragging;
|
||||
private boolean mIsThumbDetached;
|
||||
private boolean mCanThumbDetach;
|
||||
private final boolean mCanThumbDetach;
|
||||
private boolean mIgnoreDragGesture;
|
||||
|
||||
// This is the offset from the top of the scrollbar when the user first starts touching. To
|
||||
// prevent jumping, this offset is applied as the user scrolls.
|
||||
private int mTouchOffsetY;
|
||||
private int mThumbOffsetY;
|
||||
protected int mTouchOffsetY;
|
||||
protected int mThumbOffsetY;
|
||||
|
||||
// Fast scroller popup
|
||||
private TextView mPopupView;
|
||||
private boolean mPopupVisible;
|
||||
private String mPopupSectionName;
|
||||
|
||||
public BaseRecyclerViewFastScrollBar(BaseRecyclerView rv, Resources res) {
|
||||
mRv = rv;
|
||||
protected BaseRecyclerView mRv;
|
||||
|
||||
private int mDownX;
|
||||
private int mDownY;
|
||||
private int mLastY;
|
||||
|
||||
public RecyclerViewFastScroller(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public RecyclerViewFastScroller(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public RecyclerViewFastScroller(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
mTrackPaint = new Paint();
|
||||
mTrackPaint.setColor(Themes.getAttrColor(rv.getContext(), android.R.attr.textColorPrimary));
|
||||
mTrackPaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
|
||||
mTrackPaint.setAlpha(MAX_TRACK_ALPHA);
|
||||
|
||||
mThumbPaint = new Paint();
|
||||
mThumbPaint.setAntiAlias(true);
|
||||
mThumbPaint.setColor(Themes.getColorAccent(rv.getContext()));
|
||||
mThumbPaint.setColor(Themes.getColorAccent(context));
|
||||
mThumbPaint.setStyle(Paint.Style.FILL);
|
||||
|
||||
Resources res = getResources();
|
||||
mWidth = mMinWidth = res.getDimensionPixelSize(R.dimen.fastscroll_track_min_width);
|
||||
mMaxWidth = res.getDimensionPixelSize(R.dimen.fastscroll_track_max_width);
|
||||
|
||||
mThumbPadding = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_padding);
|
||||
mThumbHeight = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_height);
|
||||
|
||||
mTouchInset = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_touch_inset);
|
||||
mIsRtl = Utilities.isRtl(res);
|
||||
mConfig = ViewConfiguration.get(context);
|
||||
mDeltaThreshold = res.getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;
|
||||
|
||||
TypedArray ta =
|
||||
context.obtainStyledAttributes(attrs, R.styleable.RecyclerViewFastScroller, defStyleAttr, 0);
|
||||
mCanThumbDetach = ta.getBoolean(R.styleable.RecyclerViewFastScroller_canThumbDetach, false);
|
||||
ta.recycle();
|
||||
}
|
||||
|
||||
public void setPopupView(View popup) {
|
||||
mPopupView = (TextView) popup;
|
||||
mPopupView.setBackground(new FastScrollThumbDrawable(mThumbPaint, mIsRtl));
|
||||
}
|
||||
public void setRecyclerView(BaseRecyclerView rv, TextView popupView) {
|
||||
mRv = rv;
|
||||
mRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
mDy = dy;
|
||||
|
||||
public void setDetachThumbOnFastScroll() {
|
||||
mCanThumbDetach = true;
|
||||
// TODO(winsonc): If we want to animate the section heads while scrolling, we can
|
||||
// initiate that here if the recycler view scroll state is not
|
||||
// RecyclerView.SCROLL_STATE_IDLE.
|
||||
|
||||
mRv.onUpdateScrollbar(dy);
|
||||
}
|
||||
});
|
||||
|
||||
mPopupView = popupView;
|
||||
mPopupView.setBackground(
|
||||
new FastScrollThumbDrawable(mThumbPaint, Utilities.isRtl(getResources())));
|
||||
}
|
||||
|
||||
public void reattachThumbToScroll() {
|
||||
mIsThumbDetached = false;
|
||||
}
|
||||
|
||||
private int getDrawLeft() {
|
||||
return mIsRtl ? 0 : (mRv.getWidth() - mMaxWidth);
|
||||
}
|
||||
|
||||
public void setThumbOffsetY(int y) {
|
||||
if (mThumbOffsetY == y) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Invalidate the previous and new thumb area
|
||||
int drawLeft = getDrawLeft();
|
||||
mTmpRect.set(drawLeft, mThumbOffsetY, drawLeft + mMaxWidth, mThumbOffsetY + mThumbHeight);
|
||||
mThumbOffsetY = y;
|
||||
mTmpRect.union(drawLeft, mThumbOffsetY, drawLeft + mMaxWidth, mThumbOffsetY + mThumbHeight);
|
||||
mTmpRect.offset(0, mRv.getPaddingTop());
|
||||
mRv.invalidate(mTmpRect);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public int getThumbOffsetY() {
|
||||
|
@ -152,12 +179,8 @@ public class BaseRecyclerViewFastScrollBar {
|
|||
if (mWidth == width) {
|
||||
return;
|
||||
}
|
||||
int left = getDrawLeft();
|
||||
int top = mRv.getPaddingTop();
|
||||
// Invalidate the whole scroll bar area.
|
||||
mRv.invalidate(left, top, left + mMaxWidth, top + mRv.getScrollbarTrackHeight());
|
||||
|
||||
mWidth = width;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public int getThumbHeight() {
|
||||
|
@ -176,37 +199,48 @@ public class BaseRecyclerViewFastScrollBar {
|
|||
* Handles the touch event and determines whether to show the fast scroller (or updates it if
|
||||
* it is already showing).
|
||||
*/
|
||||
public void handleTouchEvent(MotionEvent ev, int downX, int downY, int lastY) {
|
||||
ViewConfiguration config = ViewConfiguration.get(mRv.getContext());
|
||||
|
||||
int action = ev.getAction();
|
||||
public boolean handleTouchEvent(MotionEvent ev) {
|
||||
int x = (int) ev.getX();
|
||||
int y = (int) ev.getY();
|
||||
switch (action) {
|
||||
switch (ev.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
if (isNearThumb(downX, downY)) {
|
||||
mTouchOffsetY = downY - mThumbOffsetY;
|
||||
// Keep track of the down positions
|
||||
mDownX = x;
|
||||
mDownY = mLastY = y;
|
||||
|
||||
if ((Math.abs(mDy) < mDeltaThreshold &&
|
||||
mRv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
|
||||
// now the touch events are being passed to the {@link WidgetCell} until the
|
||||
// touch sequence goes over the touch slop.
|
||||
mRv.stopScroll();
|
||||
}
|
||||
if (isNearThumb(x, y)) {
|
||||
mTouchOffsetY = mDownY - mThumbOffsetY;
|
||||
} else if (FeatureFlags.LAUNCHER3_DIRECT_SCROLL
|
||||
&& mRv.supportsFastScrolling()
|
||||
&& isNearScrollBar(downX)) {
|
||||
calcTouchOffsetAndPrepToFastScroll(downY, lastY);
|
||||
updateFastScrollSectionNameAndThumbOffset(lastY, y);
|
||||
&& isNearScrollBar(mDownX)) {
|
||||
calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
|
||||
updateFastScrollSectionNameAndThumbOffset(mLastY, y);
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
mLastY = y;
|
||||
|
||||
// Check if we should start scrolling, but ignore this fastscroll gesture if we have
|
||||
// exceeded some fixed movement
|
||||
mIgnoreDragGesture |= Math.abs(y - downY) > config.getScaledPagingTouchSlop();
|
||||
mIgnoreDragGesture |= Math.abs(y - mDownY) > mConfig.getScaledPagingTouchSlop();
|
||||
if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling() &&
|
||||
isNearThumb(downX, lastY) &&
|
||||
Math.abs(y - downY) > config.getScaledTouchSlop()) {
|
||||
calcTouchOffsetAndPrepToFastScroll(downY, lastY);
|
||||
isNearThumb(mDownX, mLastY) &&
|
||||
Math.abs(y - mDownY) > mConfig.getScaledTouchSlop()) {
|
||||
calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
|
||||
}
|
||||
if (mIsDragging) {
|
||||
updateFastScrollSectionNameAndThumbOffset(lastY, y);
|
||||
updateFastScrollSectionNameAndThumbOffset(mLastY, y);
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mRv.onFastScrollCompleted();
|
||||
mTouchOffsetY = 0;
|
||||
mLastTouchY = 0;
|
||||
mIgnoreDragGesture = false;
|
||||
|
@ -217,6 +251,7 @@ public class BaseRecyclerViewFastScrollBar {
|
|||
}
|
||||
break;
|
||||
}
|
||||
return mIsDragging;
|
||||
}
|
||||
|
||||
private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
|
||||
|
@ -245,25 +280,25 @@ public class BaseRecyclerViewFastScrollBar {
|
|||
setThumbOffsetY((int) mLastTouchY);
|
||||
}
|
||||
|
||||
public void draw(Canvas canvas) {
|
||||
public void onDraw(Canvas canvas) {
|
||||
if (mThumbOffsetY < 0) {
|
||||
return;
|
||||
}
|
||||
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
|
||||
if (!mIsRtl) {
|
||||
canvas.translate(mRv.getWidth() - mWidth, 0);
|
||||
}
|
||||
canvas.translate(0, mRv.getPaddingTop());
|
||||
canvas.translate(getWidth() / 2, mRv.getPaddingTop());
|
||||
// Draw the track
|
||||
canvas.drawRoundRect(0, 0, mWidth, mRv.getScrollbarTrackHeight(),
|
||||
float halfW = mWidth / 2;
|
||||
canvas.drawRoundRect(-halfW, 0, halfW, mRv.getScrollbarTrackHeight(),
|
||||
mWidth, mWidth, mTrackPaint);
|
||||
|
||||
canvas.translate(-mThumbPadding, mThumbOffsetY);
|
||||
canvas.translate(0, mThumbOffsetY);
|
||||
halfW += mThumbPadding;
|
||||
float r = mWidth + mThumbPadding + mThumbPadding;
|
||||
canvas.drawRoundRect(0, 0, r, mThumbHeight, r, r, mThumbPaint);
|
||||
canvas.drawRoundRect(-halfW, 0, halfW, mThumbHeight, r, r, mThumbPaint);
|
||||
canvas.restoreToCount(saveCount);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Animates the width of the scrollbar.
|
||||
*/
|
||||
|
@ -281,19 +316,25 @@ public class BaseRecyclerViewFastScrollBar {
|
|||
/**
|
||||
* Returns whether the specified point is inside the thumb bounds.
|
||||
*/
|
||||
public boolean isNearThumb(int x, int y) {
|
||||
int left = getDrawLeft();
|
||||
mTmpRect.set(left, mThumbOffsetY, left + mMaxWidth, mThumbOffsetY + mThumbHeight);
|
||||
mTmpRect.inset(mTouchInset, mTouchInset);
|
||||
return mTmpRect.contains(x, y);
|
||||
private boolean isNearThumb(int x, int y) {
|
||||
int offset = y - mRv.getPaddingTop() - mThumbOffsetY;
|
||||
|
||||
return x >= 0 && x < getWidth() && offset >= 0 && offset <= mThumbHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if AllAppsTransitionController can handle vertical motion
|
||||
* beginning at this point.
|
||||
*/
|
||||
public boolean shouldBlockIntercept(int x, int y) {
|
||||
return isNearThumb(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified x position is near the scroll bar.
|
||||
*/
|
||||
public boolean isNearScrollBar(int x) {
|
||||
int left = getDrawLeft();
|
||||
return x >= left && x <= left + mMaxWidth;
|
||||
return x >= (getWidth() - mMaxWidth) / 2 && x <= (getWidth() + mMaxWidth) / 2;
|
||||
}
|
||||
|
||||
private void animatePopupVisibility(boolean visible) {
|
||||
|
@ -306,7 +347,8 @@ public class BaseRecyclerViewFastScrollBar {
|
|||
|
||||
private void updatePopupY(int lastTouchY) {
|
||||
int height = mPopupView.getHeight();
|
||||
float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height);
|
||||
float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height)
|
||||
+ mRv.getPaddingTop();
|
||||
top = Utilities.boundToRange(top,
|
||||
mMaxWidth, mRv.getScrollbarTrackHeight() - mMaxWidth - height);
|
||||
mPopupView.setTranslationY(top);
|
Loading…
Reference in New Issue