• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.launcher3;
18 
19 import android.content.Context;
20 import android.util.AttributeSet;
21 import android.view.MotionEvent;
22 import android.view.View;
23 import android.view.ViewGroup;
24 
25 import com.android.launcher3.views.RecyclerViewFastScroller;
26 
27 import androidx.recyclerview.widget.RecyclerView;
28 
29 
30 /**
31  * A base {@link RecyclerView}, which does the following:
32  * <ul>
33  *   <li> NOT intercept a touch unless the scrolling velocity is below a predefined threshold.
34  *   <li> Enable fast scroller.
35  * </ul>
36  */
37 public abstract class BaseRecyclerView extends RecyclerView  {
38 
39     protected RecyclerViewFastScroller mScrollbar;
40 
BaseRecyclerView(Context context)41     public BaseRecyclerView(Context context) {
42         this(context, null);
43     }
44 
BaseRecyclerView(Context context, AttributeSet attrs)45     public BaseRecyclerView(Context context, AttributeSet attrs) {
46         this(context, attrs, 0);
47     }
48 
BaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr)49     public BaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
50         super(context, attrs, defStyleAttr);
51     }
52 
53     @Override
onAttachedToWindow()54     protected void onAttachedToWindow() {
55         super.onAttachedToWindow();
56         bindFastScrollbar();
57     }
58 
bindFastScrollbar()59     public void bindFastScrollbar() {
60         ViewGroup parent = (ViewGroup) getParent().getParent();
61         mScrollbar = parent.findViewById(R.id.fast_scroller);
62         mScrollbar.setRecyclerView(this, parent.findViewById(R.id.fast_scroller_popup));
63         onUpdateScrollbar(0);
64     }
65 
getScrollbar()66     public RecyclerViewFastScroller getScrollbar() {
67         return mScrollbar;
68     }
69 
getScrollBarTop()70     public int getScrollBarTop() {
71         return getPaddingTop();
72     }
73 
74     /**
75      * Returns the height of the fast scroll bar
76      */
getScrollbarTrackHeight()77     public int getScrollbarTrackHeight() {
78         return mScrollbar.getHeight() - getScrollBarTop() - getPaddingBottom();
79     }
80 
81     /**
82      * Returns the available scroll height:
83      *   AvailableScrollHeight = Total height of the all items - last page height
84      */
getAvailableScrollHeight()85     protected abstract int getAvailableScrollHeight();
86 
87     /**
88      * Returns the available scroll bar height:
89      *   AvailableScrollBarHeight = Total height of the visible view - thumb height
90      */
getAvailableScrollBarHeight()91     protected int getAvailableScrollBarHeight() {
92         int availableScrollBarHeight = getScrollbarTrackHeight() - mScrollbar.getThumbHeight();
93         return availableScrollBarHeight;
94     }
95 
96     /**
97      * Updates the scrollbar thumb offset to match the visible scroll of the recycler view.  It does
98      * this by mapping the available scroll area of the recycler view to the available space for the
99      * scroll bar.
100      *
101      * @param scrollY the current scroll y
102      */
synchronizeScrollBarThumbOffsetToViewScroll(int scrollY, int availableScrollHeight)103     protected void synchronizeScrollBarThumbOffsetToViewScroll(int scrollY,
104             int availableScrollHeight) {
105         // Only show the scrollbar if there is height to be scrolled
106         if (availableScrollHeight <= 0) {
107             mScrollbar.setThumbOffsetY(-1);
108             return;
109         }
110 
111         // Calculate the current scroll position, the scrollY of the recycler view accounts for the
112         // view padding, while the scrollBarY is drawn right up to the background padding (ignoring
113         // padding)
114         int scrollBarY =
115                 (int) (((float) scrollY / availableScrollHeight) * getAvailableScrollBarHeight());
116 
117         // Calculate the position and size of the scroll bar
118         mScrollbar.setThumbOffsetY(scrollBarY);
119     }
120 
121     /**
122      * Returns whether the view itself will handle the touch event or not.
123      * @param ev MotionEvent in {@param eventSource}
124      */
shouldContainerScroll(MotionEvent ev, View eventSource)125     public boolean shouldContainerScroll(MotionEvent ev, View eventSource) {
126         float[] point = new float[2];
127         point[0] = ev.getX();
128         point[1] = ev.getY();
129         Utilities.mapCoordInSelfToDescendant(mScrollbar, eventSource, point);
130         // IF the MotionEvent is inside the thumb, container should not be pulled down.
131         if (mScrollbar.shouldBlockIntercept((int) point[0], (int) point[1])) {
132             return false;
133         }
134 
135         // IF scroller is at the very top OR there is no scroll bar because there is probably not
136         // enough items to scroll, THEN it's okay for the container to be pulled down.
137         if (getCurrentScrollY() == 0) {
138             return true;
139         }
140         return false;
141     }
142 
143     /**
144      * @return whether fast scrolling is supported in the current state.
145      */
supportsFastScrolling()146     public boolean supportsFastScrolling() {
147         return true;
148     }
149 
150     /**
151      * Maps the touch (from 0..1) to the adapter position that should be visible.
152      * <p>Override in each subclass of this base class.
153      *
154      * @return the scroll top of this recycler view.
155      */
getCurrentScrollY()156     public abstract int getCurrentScrollY();
157 
158     /**
159      * Maps the touch (from 0..1) to the adapter position that should be visible.
160      * <p>Override in each subclass of this base class.
161      */
scrollToPositionAtProgress(float touchFraction)162     public abstract String scrollToPositionAtProgress(float touchFraction);
163 
164     /**
165      * Updates the bounds for the scrollbar.
166      * <p>Override in each subclass of this base class.
167      */
onUpdateScrollbar(int dy)168     public abstract void onUpdateScrollbar(int dy);
169 
170     /**
171      * <p>Override in each subclass of this base class.
172      */
onFastScrollCompleted()173     public void onFastScrollCompleted() {}
174 }