blob: b315980e825d0f89b5d8bbfde5fd2e6ad1ca4f3a [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));
Sunny Goyal5d9fb0e2016-10-08 17:43:48 -070071 }
72
Hyunyoung Songac5f6af2015-05-29 12:00:44 -070073 /**
74 * We intercept the touch handling only to support fast scrolling when initiated from the
75 * scroll bar. Otherwise, we fall back to the default RecyclerView touch handling.
76 */
Winson Chung94804152015-05-08 13:06:44 -070077 @Override
78 public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) {
Hyunyoung Songac5f6af2015-05-29 12:00:44 -070079 return handleTouchEvent(ev);
Winson Chung94804152015-05-08 13:06:44 -070080 }
81
82 @Override
83 public void onTouchEvent(RecyclerView rv, MotionEvent ev) {
Hyunyoung Songac5f6af2015-05-29 12:00:44 -070084 handleTouchEvent(ev);
85 }
86
87 /**
88 * Handles the touch event and determines whether to show the fast scroller (or updates it if
89 * it is already showing).
90 */
91 private boolean handleTouchEvent(MotionEvent ev) {
Sunny Goyal89d5c5a2017-06-23 16:12:50 -070092 // Move to mScrollbar's coordinate system.
93 int left = getLeft() - mScrollbar.getLeft();
94 int top = getTop() - mScrollbar.getTop();
95 ev.offsetLocation(left, top);
96 try {
97 return mScrollbar.handleTouchEvent(ev);
98 } finally {
99 ev.offsetLocation(-left, -top);
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700100 }
Winson Chung94804152015-05-08 13:06:44 -0700101 }
102
103 public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
104 // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS
105 }
106
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700107 public int getScrollBarTop() {
108 return getPaddingTop();
109 }
110
Winson Chung94804152015-05-08 13:06:44 -0700111 /**
Sunny Goyal00e10682016-10-18 14:23:46 +0100112 * Returns the height of the fast scroll bar
Winsonc0880492015-08-21 11:16:27 -0700113 */
Sunny Goyal89d5c5a2017-06-23 16:12:50 -0700114 public int getScrollbarTrackHeight() {
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700115 return getHeight() - getScrollBarTop() - getPaddingBottom();
Winsonc0880492015-08-21 11:16:27 -0700116 }
117
118 /**
Winson Chungb1777442015-06-16 13:35:04 -0700119 * Returns the available scroll height:
120 * AvailableScrollHeight = Total height of the all items - last page height
Winson Chungb1777442015-06-16 13:35:04 -0700121 */
Winsonb655b882016-07-11 18:59:18 -0700122 protected abstract int getAvailableScrollHeight();
Winson Chungb1777442015-06-16 13:35:04 -0700123
124 /**
125 * Returns the available scroll bar height:
126 * AvailableScrollBarHeight = Total height of the visible view - thumb height
127 */
128 protected int getAvailableScrollBarHeight() {
Sunny Goyal00e10682016-10-18 14:23:46 +0100129 int availableScrollBarHeight = getScrollbarTrackHeight() - mScrollbar.getThumbHeight();
Winson Chungb1777442015-06-16 13:35:04 -0700130 return availableScrollBarHeight;
131 }
132
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700133 @Override
134 protected void dispatchDraw(Canvas canvas) {
Sunny Goyal89d5c5a2017-06-23 16:12:50 -0700135 onUpdateScrollbar(0);
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700136 super.dispatchDraw(canvas);
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700137 }
138
139 /**
Winson Chungb1777442015-06-16 13:35:04 -0700140 * Updates the scrollbar thumb offset to match the visible scroll of the recycler view. It does
141 * this by mapping the available scroll area of the recycler view to the available space for the
142 * scroll bar.
143 *
Winsonb655b882016-07-11 18:59:18 -0700144 * @param scrollY the current scroll y
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700145 */
Winsonb655b882016-07-11 18:59:18 -0700146 protected void synchronizeScrollBarThumbOffsetToViewScroll(int scrollY,
147 int availableScrollHeight) {
Winson Chungb1777442015-06-16 13:35:04 -0700148 // Only show the scrollbar if there is height to be scrolled
149 if (availableScrollHeight <= 0) {
Sunny Goyal5d9fb0e2016-10-08 17:43:48 -0700150 mScrollbar.setThumbOffsetY(-1);
Winson Chungb1777442015-06-16 13:35:04 -0700151 return;
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700152 }
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700153
Winson Chungb1777442015-06-16 13:35:04 -0700154 // Calculate the current scroll position, the scrollY of the recycler view accounts for the
155 // view padding, while the scrollBarY is drawn right up to the background padding (ignoring
156 // padding)
Sunny Goyal00e10682016-10-18 14:23:46 +0100157 int scrollBarY =
158 (int) (((float) scrollY / availableScrollHeight) * getAvailableScrollBarHeight());
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700159
Winson Chungb1777442015-06-16 13:35:04 -0700160 // Calculate the position and size of the scroll bar
Sunny Goyal5d9fb0e2016-10-08 17:43:48 -0700161 mScrollbar.setThumbOffsetY(scrollBarY);
Winson Chung4b9f9792015-06-12 18:02:52 -0700162 }
163
164 /**
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700165 * Returns whether the view itself will handle the touch event or not.
166 * @param ev MotionEvent in {@param eventSource}
167 */
168 public boolean shouldContainerScroll(MotionEvent ev, View eventSource) {
169 int[] point = new int[2];
170 point[0] = (int) ev.getX();
171 point[1] = (int) ev.getY();
172 Utilities.mapCoordInSelfToDescendant(mScrollbar, eventSource, point);
173 // IF the MotionEvent is inside the thumb, container should not be pulled down.
174 if (mScrollbar.shouldBlockIntercept(point[0], point[1])) {
175 return false;
176 }
177
178 // IF scroller is at the very top OR there is no scroll bar because there is probably not
179 // enough items to scroll, THEN it's okay for the container to be pulled down.
180 if (getCurrentScrollY() == 0) {
181 return true;
182 }
183 return false;
184 }
185
186 /**
Winsonc0880492015-08-21 11:16:27 -0700187 * @return whether fast scrolling is supported in the current state.
Winson646c2362015-09-03 11:46:11 -0700188 */
Sunny Goyal89d5c5a2017-06-23 16:12:50 -0700189 public boolean supportsFastScrolling() {
Winson646c2362015-09-03 11:46:11 -0700190 return true;
191 }
192
193 /**
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700194 * Maps the touch (from 0..1) to the adapter position that should be visible.
195 * <p>Override in each subclass of this base class.
Winsonc0880492015-08-21 11:16:27 -0700196 *
197 * @return the scroll top of this recycler view.
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700198 */
Peter Schiller2e22b5d2016-07-12 12:45:10 -0700199 public abstract int getCurrentScrollY();
Winsonc0880492015-08-21 11:16:27 -0700200
201 /**
202 * Maps the touch (from 0..1) to the adapter position that should be visible.
203 * <p>Override in each subclass of this base class.
204 */
Sunny Goyal89d5c5a2017-06-23 16:12:50 -0700205 public abstract String scrollToPositionAtProgress(float touchFraction);
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700206
207 /**
208 * Updates the bounds for the scrollbar.
209 * <p>Override in each subclass of this base class.
210 */
Sunny Goyal89d5c5a2017-06-23 16:12:50 -0700211 public abstract void onUpdateScrollbar(int dy);
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700212
213 /**
Winson Chungb1777442015-06-16 13:35:04 -0700214 * <p>Override in each subclass of this base class.
Hyunyoung Songac5f6af2015-05-29 12:00:44 -0700215 */
Sunny Goyal89d5c5a2017-06-23 16:12:50 -0700216 public void onFastScrollCompleted() {}
Winson Chung94804152015-05-08 13:06:44 -0700217}