blob: bce0e2e3cf742c7c6a57ab639ac551f9e590f67a [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;
Hyunyoung Songac5f6af2015-05-29 12:00:44 -070020import android.graphics.Canvas;
Winson Chung94804152015-05-08 13:06:44 -070021import android.support.v7.widget.RecyclerView;
22import android.util.AttributeSet;
23import android.view.MotionEvent;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070024import android.view.View;
Sunny Goyal5d9fb0e2016-10-08 17:43:48 -070025import android.view.ViewGroup;
Sunny Goyal89d5c5a2017-06-23 16:12:50 -070026import android.widget.TextView;
Sunny Goyal5d9fb0e2016-10-08 17:43:48 -070027
Sunny Goyal89d5c5a2017-06-23 16:12:50 -070028import com.android.launcher3.views.RecyclerViewFastScroller;
Winson Chung94804152015-05-08 13:06:44 -070029
Winson Chungb1777442015-06-16 13:35:04 -070030
Winson Chung94804152015-05-08 13:06:44 -070031/**
Hyunyoung Songac5f6af2015-05-29 12:00:44 -070032 * A base {@link RecyclerView}, which does the following:
33 * <ul>
34 * <li> NOT intercept a touch unless the scrolling velocity is below a predefined threshold.
35 * <li> Enable fast scroller.
36 * </ul>
Winson Chung94804152015-05-08 13:06:44 -070037 */
Winson Chungb1777442015-06-16 13:35:04 -070038public abstract class BaseRecyclerView extends RecyclerView
Winson Chung94804152015-05-08 13:06:44 -070039 implements RecyclerView.OnItemTouchListener {
40
Sunny Goyal89d5c5a2017-06-23 16:12:50 -070041 protected RecyclerViewFastScroller mScrollbar;
Sunny Goyal9e4c3592017-06-07 14:05:08 -070042
Winson Chung5f4e0fd2015-05-22 11:12:27 -070043 public BaseRecyclerView(Context context) {
Winson Chung94804152015-05-08 13:06:44 -070044 this(context, null);
45 }
46
Winson Chung5f4e0fd2015-05-22 11:12:27 -070047 public BaseRecyclerView(Context context, AttributeSet attrs) {
Winson Chung94804152015-05-08 13:06:44 -070048 this(context, attrs, 0);
49 }
50
Winson Chung5f4e0fd2015-05-22 11:12:27 -070051 public BaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
Winson Chung94804152015-05-08 13:06:44 -070052 super(context, attrs, defStyleAttr);
Winson Chung94804152015-05-08 13:06:44 -070053 }
54
Winson Chung94804152015-05-08 13:06:44 -070055 @Override
56 protected void onFinishInflate() {
57 super.onFinishInflate();
58 addOnItemTouchListener(this);
59 }
60
Sunny Goyal5d9fb0e2016-10-08 17:43:48 -070061 @Override
62 protected void onAttachedToWindow() {
63 super.onAttachedToWindow();
Mario Bertschlerac9408a2017-10-18 10:31:36 -070064 bindFastScrollbar();
65 }
66
67 public void bindFastScrollbar() {
68 ViewGroup parent = (ViewGroup) getParent().getParent();
Sunny Goyal89d5c5a2017-06-23 16:12:50 -070069 mScrollbar = parent.findViewById(R.id.fast_scroller);
Mario Bertschlerac9408a2017-10-18 10:31:36 -070070 mScrollbar.setRecyclerView(this, parent.findViewById(R.id.fast_scroller_popup));
Mario Bertschler2d3157a2017-11-27 13:10:44 -080071 onUpdateScrollbar(0);
Sunny Goyal5d9fb0e2016-10-08 17:43:48 -070072 }
73
Hyunyoung Songac5f6af2015-05-29 12:00:44 -070074 /**
75 * We intercept the touch handling only to support fast scrolling when initiated from the
76 * scroll bar. Otherwise, we fall back to the default RecyclerView touch handling.
77 */
Winson Chung94804152015-05-08 13:06:44 -070078 @Override
79 public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) {
Hyunyoung Songac5f6af2015-05-29 12:00:44 -070080 return handleTouchEvent(ev);
Winson Chung94804152015-05-08 13:06:44 -070081 }
82
83 @Override
84 public void onTouchEvent(RecyclerView rv, MotionEvent ev) {
Hyunyoung Songac5f6af2015-05-29 12:00:44 -070085 handleTouchEvent(ev);
86 }
87
88 /**
89 * Handles the touch event and determines whether to show the fast scroller (or updates it if
90 * it is already showing).
91 */
92 private boolean handleTouchEvent(MotionEvent ev) {
Sunny Goyal89d5c5a2017-06-23 16:12:50 -070093 // Move to mScrollbar's coordinate system.
94 int left = getLeft() - mScrollbar.getLeft();
95 int top = getTop() - mScrollbar.getTop();
96 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}