blob: cc132633828341b53f1f48b13f67eed81a207d44 [file] [log] [blame]
Winson Chung94804152015-05-08 13:06:44 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.launcher3;
18
19import android.content.Context;
20import android.support.v7.widget.RecyclerView;
21import android.util.AttributeSet;
22import android.view.MotionEvent;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070023import android.view.View;
Sunny Goyal5d9fb0e2016-10-08 17:43:48 -070024import android.view.ViewGroup;
25
Sunny Goyal89d5c5a2017-06-23 16:12:50 -070026import com.android.launcher3.views.RecyclerViewFastScroller;
Winson Chung94804152015-05-08 13:06:44 -070027
Winson Chungb1777442015-06-16 13:35:04 -070028
Winson Chung94804152015-05-08 13:06:44 -070029/**
Hyunyoung Songac5f6af2015-05-29 12:00:44 -070030 * A base {@link RecyclerView}, which does the following:
31 * <ul>
32 * <li> NOT intercept a touch unless the scrolling velocity is below a predefined threshold.
33 * <li> Enable fast scroller.
34 * </ul>
Winson Chung94804152015-05-08 13:06:44 -070035 */
Winson Chungb1777442015-06-16 13:35:04 -070036public abstract class BaseRecyclerView extends RecyclerView
Winson Chung94804152015-05-08 13:06:44 -070037 implements RecyclerView.OnItemTouchListener {
38
Sunny Goyal89d5c5a2017-06-23 16:12:50 -070039 protected RecyclerViewFastScroller mScrollbar;
Sunny Goyal9e4c3592017-06-07 14:05:08 -070040
Winson Chung5f4e0fd2015-05-22 11:12:27 -070041 public BaseRecyclerView(Context context) {
Winson Chung94804152015-05-08 13:06:44 -070042 this(context, null);
43 }
44
Winson Chung5f4e0fd2015-05-22 11:12:27 -070045 public BaseRecyclerView(Context context, AttributeSet attrs) {
Winson Chung94804152015-05-08 13:06:44 -070046 this(context, attrs, 0);
47 }
48
Winson Chung5f4e0fd2015-05-22 11:12:27 -070049 public BaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
Winson Chung94804152015-05-08 13:06:44 -070050 super(context, attrs, defStyleAttr);
Winson Chung94804152015-05-08 13:06:44 -070051 }
52
Winson Chung94804152015-05-08 13:06:44 -070053 @Override
54 protected void onFinishInflate() {
55 super.onFinishInflate();
56 addOnItemTouchListener(this);
57 }
58
Sunny Goyal5d9fb0e2016-10-08 17:43:48 -070059 @Override
60 protected void onAttachedToWindow() {
61 super.onAttachedToWindow();
Mario Bertschlerac9408a2017-10-18 10:31:36 -070062 bindFastScrollbar();
63 }
64
65 public void bindFastScrollbar() {
66 ViewGroup parent = (ViewGroup) getParent().getParent();
Sunny Goyal89d5c5a2017-06-23 16:12:50 -070067 mScrollbar = parent.findViewById(R.id.fast_scroller);
Mario Bertschlerac9408a2017-10-18 10:31:36 -070068 mScrollbar.setRecyclerView(this, parent.findViewById(R.id.fast_scroller_popup));
Mario Bertschler2d3157a2017-11-27 13:10:44 -080069 onUpdateScrollbar(0);
Sunny Goyal5d9fb0e2016-10-08 17:43:48 -070070 }
71
Hyunyoung Songac5f6af2015-05-29 12:00:44 -070072 /**
73 * We intercept the touch handling only to support fast scrolling when initiated from the
74 * scroll bar. Otherwise, we fall back to the default RecyclerView touch handling.
75 */
Winson Chung94804152015-05-08 13:06:44 -070076 @Override
77 public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) {
Hyunyoung Songac5f6af2015-05-29 12:00:44 -070078 return handleTouchEvent(ev);
Winson Chung94804152015-05-08 13:06:44 -070079 }
80
81 @Override
82 public void onTouchEvent(RecyclerView rv, MotionEvent ev) {
Hyunyoung Songac5f6af2015-05-29 12:00:44 -070083 handleTouchEvent(ev);
84 }
85
86 /**
87 * Handles the touch event and determines whether to show the fast scroller (or updates it if
88 * it is already showing).
89 */
90 private boolean handleTouchEvent(MotionEvent ev) {
Sunny Goyal89d5c5a2017-06-23 16:12:50 -070091 // Move to mScrollbar's coordinate system.
Mario Bertschlerea0eb4b2017-12-06 13:03:54 -080092 // We need to take parent into account (view pager's location)
93 ViewGroup parent = (ViewGroup) getParent();
Tony Mak5e489352018-01-16 17:29:50 +000094 int left = parent.getLeft() - mScrollbar.getLeft();
Mario Bertschlerea0eb4b2017-12-06 13:03:54 -080095 int top = parent.getTop() + getTop() - mScrollbar.getTop() - getScrollBarTop();
Sunny Goyal89d5c5a2017-06-23 16:12:50 -070096 ev.offsetLocation(left, top);
97 try {
98 return mScrollbar.handleTouchEvent(ev);
99 } finally {
100 ev.offsetLocation(-left, -top);
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700101 }
Winson Chung94804152015-05-08 13:06:44 -0700102 }
103
104 public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
105 // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS
106 }
107
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700108 public int getScrollBarTop() {
109 return getPaddingTop();
110 }
111
Winson Chung94804152015-05-08 13:06:44 -0700112 /**
Sunny Goyal00e10682016-10-18 14:23:46 +0100113 * Returns the height of the fast scroll bar
Winsonc0880492015-08-21 11:16:27 -0700114 */
Sunny Goyal89d5c5a2017-06-23 16:12:50 -0700115 public int getScrollbarTrackHeight() {
Mario Bertschler2d3157a2017-11-27 13:10:44 -0800116 return mScrollbar.getHeight() - getScrollBarTop() - getPaddingBottom();
Winsonc0880492015-08-21 11:16:27 -0700117 }
118
119 /**
Winson Chungb1777442015-06-16 13:35:04 -0700120 * Returns the available scroll height:
121 * AvailableScrollHeight = Total height of the all items - last page height
Winson Chungb1777442015-06-16 13:35:04 -0700122 */
Winsonb655b882016-07-11 18:59:18 -0700123 protected abstract int getAvailableScrollHeight();
Winson Chungb1777442015-06-16 13:35:04 -0700124
125 /**
126 * Returns the available scroll bar height:
127 * AvailableScrollBarHeight = Total height of the visible view - thumb height
128 */
129 protected int getAvailableScrollBarHeight() {
Sunny Goyal00e10682016-10-18 14:23:46 +0100130 int availableScrollBarHeight = getScrollbarTrackHeight() - mScrollbar.getThumbHeight();
Winson Chungb1777442015-06-16 13:35:04 -0700131 return availableScrollBarHeight;
132 }
133
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700134 /**
Winson Chungb1777442015-06-16 13:35:04 -0700135 * Updates the scrollbar thumb offset to match the visible scroll of the recycler view. It does
136 * this by mapping the available scroll area of the recycler view to the available space for the
137 * scroll bar.
138 *
Winsonb655b882016-07-11 18:59:18 -0700139 * @param scrollY the current scroll y
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700140 */
Winsonb655b882016-07-11 18:59:18 -0700141 protected void synchronizeScrollBarThumbOffsetToViewScroll(int scrollY,
142 int availableScrollHeight) {
Winson Chungb1777442015-06-16 13:35:04 -0700143 // Only show the scrollbar if there is height to be scrolled
144 if (availableScrollHeight <= 0) {
Sunny Goyal5d9fb0e2016-10-08 17:43:48 -0700145 mScrollbar.setThumbOffsetY(-1);
Winson Chungb1777442015-06-16 13:35:04 -0700146 return;
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700147 }
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700148
Winson Chungb1777442015-06-16 13:35:04 -0700149 // Calculate the current scroll position, the scrollY of the recycler view accounts for the
150 // view padding, while the scrollBarY is drawn right up to the background padding (ignoring
151 // padding)
Sunny Goyal00e10682016-10-18 14:23:46 +0100152 int scrollBarY =
153 (int) (((float) scrollY / availableScrollHeight) * getAvailableScrollBarHeight());
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700154
Winson Chungb1777442015-06-16 13:35:04 -0700155 // Calculate the position and size of the scroll bar
Sunny Goyal5d9fb0e2016-10-08 17:43:48 -0700156 mScrollbar.setThumbOffsetY(scrollBarY);
Winson Chung4b9f9792015-06-12 18:02:52 -0700157 }
158
159 /**
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700160 * Returns whether the view itself will handle the touch event or not.
161 * @param ev MotionEvent in {@param eventSource}
162 */
163 public boolean shouldContainerScroll(MotionEvent ev, View eventSource) {
164 int[] point = new int[2];
165 point[0] = (int) ev.getX();
166 point[1] = (int) ev.getY();
167 Utilities.mapCoordInSelfToDescendant(mScrollbar, eventSource, point);
168 // IF the MotionEvent is inside the thumb, container should not be pulled down.
169 if (mScrollbar.shouldBlockIntercept(point[0], point[1])) {
170 return false;
171 }
172
173 // IF scroller is at the very top OR there is no scroll bar because there is probably not
174 // enough items to scroll, THEN it's okay for the container to be pulled down.
175 if (getCurrentScrollY() == 0) {
176 return true;
177 }
178 return false;
179 }
180
181 /**
Winsonc0880492015-08-21 11:16:27 -0700182 * @return whether fast scrolling is supported in the current state.
Winson646c2362015-09-03 11:46:11 -0700183 */
Sunny Goyal89d5c5a2017-06-23 16:12:50 -0700184 public boolean supportsFastScrolling() {
Winson646c2362015-09-03 11:46:11 -0700185 return true;
186 }
187
188 /**
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700189 * Maps the touch (from 0..1) to the adapter position that should be visible.
190 * <p>Override in each subclass of this base class.
Winsonc0880492015-08-21 11:16:27 -0700191 *
192 * @return the scroll top of this recycler view.
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700193 */
Peter Schiller2e22b5d2016-07-12 12:45:10 -0700194 public abstract int getCurrentScrollY();
Winsonc0880492015-08-21 11:16:27 -0700195
196 /**
197 * Maps the touch (from 0..1) to the adapter position that should be visible.
198 * <p>Override in each subclass of this base class.
199 */
Sunny Goyal89d5c5a2017-06-23 16:12:50 -0700200 public abstract String scrollToPositionAtProgress(float touchFraction);
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700201
202 /**
203 * Updates the bounds for the scrollbar.
204 * <p>Override in each subclass of this base class.
205 */
Sunny Goyal89d5c5a2017-06-23 16:12:50 -0700206 public abstract void onUpdateScrollbar(int dy);
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700207
208 /**
Winson Chungb1777442015-06-16 13:35:04 -0700209 * <p>Override in each subclass of this base class.
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700210 */
Sunny Goyal89d5c5a2017-06-23 16:12:50 -0700211 public void onFastScrollCompleted() {}
Winson Chung94804152015-05-08 13:06:44 -0700212}