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 }