1 /*
2  * Copyright 2018 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 
18 package androidx.recyclerview.widget;
19 
20 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
21 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
22 import static androidx.core.util.Preconditions.checkArgument;
23 import static androidx.core.view.ViewCompat.TYPE_NON_TOUCH;
24 import static androidx.core.view.ViewCompat.TYPE_TOUCH;
25 
26 import android.animation.LayoutTransition;
27 import android.annotation.SuppressLint;
28 import android.content.Context;
29 import android.content.res.Resources;
30 import android.content.res.TypedArray;
31 import android.database.Observable;
32 import android.graphics.Canvas;
33 import android.graphics.Matrix;
34 import android.graphics.PointF;
35 import android.graphics.Rect;
36 import android.graphics.RectF;
37 import android.graphics.drawable.Drawable;
38 import android.graphics.drawable.StateListDrawable;
39 import android.hardware.SensorManager;
40 import android.os.Build;
41 import android.os.Bundle;
42 import android.os.Parcel;
43 import android.os.Parcelable;
44 import android.os.SystemClock;
45 import android.os.Trace;
46 import android.util.AttributeSet;
47 import android.util.Log;
48 import android.util.SparseArray;
49 import android.view.Display;
50 import android.view.FocusFinder;
51 import android.view.InputDevice;
52 import android.view.KeyEvent;
53 import android.view.MotionEvent;
54 import android.view.VelocityTracker;
55 import android.view.View;
56 import android.view.ViewConfiguration;
57 import android.view.ViewGroup;
58 import android.view.ViewParent;
59 import android.view.accessibility.AccessibilityEvent;
60 import android.view.accessibility.AccessibilityManager;
61 import android.view.animation.Interpolator;
62 import android.widget.EdgeEffect;
63 import android.widget.LinearLayout;
64 import android.widget.OverScroller;
65 
66 import androidx.annotation.CallSuper;
67 import androidx.annotation.DoNotInline;
68 import androidx.annotation.IntDef;
69 import androidx.annotation.Px;
70 import androidx.annotation.RequiresApi;
71 import androidx.annotation.RestrictTo;
72 import androidx.annotation.VisibleForTesting;
73 import androidx.core.os.TraceCompat;
74 import androidx.core.util.Preconditions;
75 import androidx.core.view.AccessibilityDelegateCompat;
76 import androidx.core.view.DifferentialMotionFlingController;
77 import androidx.core.view.DifferentialMotionFlingTarget;
78 import androidx.core.view.InputDeviceCompat;
79 import androidx.core.view.MotionEventCompat;
80 import androidx.core.view.NestedScrollingChild2;
81 import androidx.core.view.NestedScrollingChild3;
82 import androidx.core.view.NestedScrollingChildHelper;
83 import androidx.core.view.ScrollFeedbackProviderCompat;
84 import androidx.core.view.ScrollingView;
85 import androidx.core.view.ViewCompat;
86 import androidx.core.view.ViewConfigurationCompat;
87 import androidx.core.view.accessibility.AccessibilityEventCompat;
88 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
89 import androidx.core.widget.EdgeEffectCompat;
90 import androidx.customview.poolingcontainer.PoolingContainer;
91 import androidx.customview.poolingcontainer.PoolingContainerListener;
92 import androidx.customview.view.AbsSavedState;
93 import androidx.recyclerview.R;
94 import androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
95 
96 import org.jspecify.annotations.NonNull;
97 import org.jspecify.annotations.Nullable;
98 
99 import java.lang.annotation.Retention;
100 import java.lang.annotation.RetentionPolicy;
101 import java.lang.ref.WeakReference;
102 import java.lang.reflect.Constructor;
103 import java.lang.reflect.InvocationTargetException;
104 import java.util.ArrayList;
105 import java.util.Collections;
106 import java.util.IdentityHashMap;
107 import java.util.List;
108 import java.util.Set;
109 
110 /**
111  * A flexible view for providing a limited window into a large data set.
112  *
113  * <h3>Glossary of terms:</h3>
114  *
115  * <ul>
116  *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
117  *     that represent items in a data set.</li>
118  *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
119  *     <li><em>Index:</em> The index of an attached child view as used in a call to
120  *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
121  *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
122  *     to a <em>position</em> within the adapter.</li>
123  *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
124  *     position may be placed in a cache for later reuse to display the same type of data again
125  *     later. This can drastically improve performance by skipping initial layout inflation
126  *     or construction.</li>
127  *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
128  *     state during layout. Scrap views may be reused without becoming fully detached
129  *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
130  *     by the adapter if the view was considered <em>dirty</em>.</li>
131  *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
132  *     being displayed.</li>
133  * </ul>
134  *
135  * <h3>Positions in RecyclerView:</h3>
136  * <p>
137  * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
138  * {@link LayoutManager} to be able to detect data set changes in batches during a layout
139  * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
140  * It also helps with performance because all view bindings happen at the same time and unnecessary
141  * bindings are avoided.
142  * <p>
143  * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
144  * <ul>
145  *     <li>layout position: Position of an item in the latest layout calculation. This is the
146  *     position from the LayoutManager's perspective.</li>
147  *     <li>adapter position: Position of an item in the adapter. This is the position from
148  *     the Adapter's perspective.</li>
149  * </ul>
150  * <p>
151  * These two positions are the same except the time between dispatching <code>adapter.notify*
152  * </code> events and calculating the updated layout.
153  * <p>
154  * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
155  * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
156  * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
157  * last layout calculation. You can rely on these positions to be consistent with what user is
158  * currently seeing on the screen. For example, if you have a list of items on the screen and user
159  * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
160  * is seeing.
161  * <p>
162  * The other set of position related methods are in the form of
163  * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAbsoluteAdapterPosition()},
164  * {@link ViewHolder#getBindingAdapterPosition()},
165  * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
166  * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
167  * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
168  * {@link ViewHolder#getBindingAdapterPosition()}. Beware that these methods may not be able to
169  * calculate adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new
170  * layout has not yet been calculated. For this reasons, you should carefully handle
171  * {@link #NO_POSITION} or <code>null</code> results from these methods.
172  * <p>
173  * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
174  * writing an {@link Adapter}, you probably want to use adapter positions.
175  * <p>
176  * <h3>Presenting Dynamic Data</h3>
177  * To display updatable data in a RecyclerView, your adapter needs to signal inserts, moves, and
178  * deletions to RecyclerView. You can build this yourself by manually calling
179  * {@code adapter.notify*} methods when content changes, or you can use one of the easier solutions
180  * RecyclerView provides:
181  * <p>
182  * <h4>List diffing with DiffUtil</h4>
183  * If your RecyclerView is displaying a list that is re-fetched from scratch for each update (e.g.
184  * from the network, or from a database), {@link DiffUtil} can calculate the difference between
185  * versions of the list. {@code DiffUtil} takes both lists as input and computes the difference,
186  * which can be passed to RecyclerView to trigger minimal animations and updates to keep your UI
187  * performant, and animations meaningful. This approach requires that each list is represented in
188  * memory with immutable content, and relies on receiving updates as new instances of lists. This
189  * approach is also ideal if your UI layer doesn't implement sorting, it just presents the data in
190  * the order it's given.
191  * <p>
192  * The best part of this approach is that it extends to any arbitrary changes - item updates,
193  * moves, addition and removal can all be computed and handled the same way. Though you do have
194  * to keep two copies of the list in memory while diffing, and must avoid mutating them, it's
195  * possible to share unmodified elements between list versions.
196  * <p>
197  * There are three primary ways to do this for RecyclerView. We recommend you start with
198  * {@link ListAdapter}, the higher-level API that builds in {@link List} diffing on a background
199  * thread, with minimal code. {@link AsyncListDiffer} also provides this behavior, but without
200  * defining an Adapter to subclass. If you want more control, {@link DiffUtil} is the lower-level
201  * API you can use to compute the diffs yourself. Each approach allows you to specify how diffs
202  * should be computed based on item data.
203  * <p>
204  * <h4>List mutation with SortedList</h4>
205  * If your RecyclerView receives updates incrementally, e.g. item X is inserted, or item Y is
206  * removed, you can use {@link SortedList} to manage your list. You define how to order items,
207  * and it will automatically trigger update signals that RecyclerView can use. SortedList works
208  * if you only need to handle insert and remove events, and has the benefit that you only ever
209  * need to have a single copy of the list in memory. It can also compute differences with
210  * {@link SortedList#replaceAll(Object[])}, but this method is more limited than the list diffing
211  * behavior above.
212  * <p>
213  * <h4>Paging Library</h4>
214  * The <a href="https://developer.android.com/topic/libraries/architecture/paging/">Paging
215  * library</a> extends the diff-based approach to additionally support paged loading. It provides
216  * the {@link androidx.paging.PagedList} class that operates as a self-loading list, provided a
217  * source of data like a database, or paginated network API. It provides convenient list diffing
218  * support out of the box, similar to {@code ListAdapter} and {@code AsyncListDiffer}. For more
219  * information about the Paging library, see the
220  * <a href="https://developer.android.com/topic/libraries/architecture/paging/">library
221  * documentation</a>.
222  *
223  * {@link androidx.recyclerview.R.attr#layoutManager}
224  */
225 public class RecyclerView extends ViewGroup implements ScrollingView,
226         NestedScrollingChild2, NestedScrollingChild3 {
227 
228     static final String TAG = "RecyclerView";
229 
230     static boolean sDebugAssertionsEnabled = false;
231     static boolean sVerboseLoggingEnabled = false;
232 
233     static final boolean VERBOSE_TRACING = false;
234 
235     private static final int[] NESTED_SCROLLING_ATTRS =
236             {16843830 /* android.R.attr.nestedScrollingEnabled */};
237 
238     /**
239      * The following are copied from OverScroller to determine how far a fling will go.
240      */
241     private static final float SCROLL_FRICTION = 0.015f;
242     private static final float INFLEXION = 0.35f; // Tension lines cross at (INFLEXION, 1)
243     private static final float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9));
244     private final float mPhysicalCoef;
245 
246     /**
247      * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
248      * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
249      * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
250      * recursively traverses itemView and invalidates display list for each ViewGroup that matches
251      * this criteria.
252      */
253     static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 19
254             || Build.VERSION.SDK_INT == 20;
255     /**
256      * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
257      * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
258      * 0 when mode is unspecified.
259      */
260     static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
261 
262     /**
263      * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
264      * RenderThread but before the next frame begins. We schedule prefetch work in this window.
265      */
266     static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
267 
268     /**
269      * When flinging the stretch towards scrolling content, it should destretch quicker than the
270      * fling would normally do. The visual effect of flinging the stretch looks strange as little
271      * appears to happen at first and then when the stretch disappears, the content starts
272      * scrolling quickly.
273      */
274     private static final float FLING_DESTRETCH_FACTOR = 4f;
275 
276     /**
277      * A {@link android.content.pm.PackageManager} feature specifying if a device's rotary encoder
278      * has low resolution. Low resolution rotary encoders produce small number of
279      * {@link MotionEvent}s per a 360 degree rotation, meaning that each {@link MotionEvent} has
280      * large scroll values, which make {@link #scrollBy(int, int)} calls feel broken (due to the
281      * fact that each event produces large scrolls, and scrolling with large pixels causes a visible
282      * jump that does not feel smooth). As such, we will try adjusting our handling of generic
283      * motion caused by such low resolution rotary encoders differently to make the scrolling
284      * experience smooth.
285      */
286     static final String LOW_RES_ROTARY_ENCODER_FEATURE = "android.hardware.rotaryencoder.lowres";
287 
288     static final boolean DISPATCH_TEMP_DETACH = false;
289 
290     @RestrictTo(LIBRARY_GROUP_PREFIX)
291     @IntDef({HORIZONTAL, VERTICAL})
292     @Retention(RetentionPolicy.SOURCE)
293     public @interface Orientation {
294     }
295 
296     public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
297     public static final int VERTICAL = LinearLayout.VERTICAL;
298 
299     static final int DEFAULT_ORIENTATION = VERTICAL;
300     public static final int NO_POSITION = -1;
301     public static final long NO_ID = -1;
302     public static final int INVALID_TYPE = -1;
303 
304     /**
305      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
306      * that the RecyclerView should use the standard touch slop for smooth,
307      * continuous scrolling.
308      */
309     public static final int TOUCH_SLOP_DEFAULT = 0;
310 
311     /**
312      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
313      * that the RecyclerView should use the standard touch slop for scrolling
314      * widgets that snap to a page or other coarse-grained barrier.
315      */
316     public static final int TOUCH_SLOP_PAGING = 1;
317 
318     /**
319      * Constant that represents that a duration has not been defined.
320      */
321     public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
322 
323     static final int MAX_SCROLL_DURATION = 2000;
324 
325     /**
326      * RecyclerView is calculating a scroll.
327      * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
328      * it. Try to avoid using EditText, focusable views or handle them with care.
329      */
330     static final String TRACE_SCROLL_TAG = "RV Scroll";
331 
332     /**
333      * OnLayout has been called by the View system.
334      * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
335      * update themselves directly. This will cause a full re-layout but when it happens via the
336      * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
337      */
338     private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
339 
340     /**
341      * NotifyDataSetChanged or equal has been called.
342      * If this is taking a long time, try sending granular notify adapter changes instead of just
343      * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
344      * might help.
345      */
346     private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
347 
348     /**
349      * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
350      * If this is taking a long time, you may have dispatched too many Adapter updates causing too
351      * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
352      * methods.
353      */
354     private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
355 
356 
357     /**
358      * RecyclerView is attempting to pre-populate off screen views.
359      */
360     static final String TRACE_PREFETCH_TAG = "RV Prefetch";
361 
362     /**
363      * RecyclerView is creating a new View.
364      * If too many of these present in Systrace:
365      * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
366      * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
367      * > Adapter#onFailedToRecycleView(ViewHolder)})
368      *
369      * - There might be too many item view types.
370      * > Try merging them
371      *
372      * - There might be too many itemChange animations and not enough space in RecyclerPool.
373      * >Try increasing your pool size and item cache size.
374      */
375     static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
376     private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
377             new Class<?>[]{Context.class, AttributeSet.class, int.class, int.class};
378 
379     /**
380      * Enable internal assertions about RecyclerView's state and throw exceptions if the
381      * assertions are violated.
382      * <p>
383      * This is primarily intended to diagnose problems with RecyclerView, and
384      * <strong>should not be enabled in production</strong> unless you have a specific reason to
385      * do so.
386      * <p>
387      * Enabling this may negatively affect performance and/or stability.
388      *
389      * @param debugAssertionsEnabled true to enable assertions; false to disable them
390      */
setDebugAssertionsEnabled(boolean debugAssertionsEnabled)391     public static void setDebugAssertionsEnabled(boolean debugAssertionsEnabled) {
392         RecyclerView.sDebugAssertionsEnabled = debugAssertionsEnabled;
393     }
394 
395     /**
396      * Enable verbose logging within RecyclerView itself.
397      * <p>
398      * Enabling this may negatively affect performance and reduce the utility of logcat due to
399      * high-volume logging.  This generally <strong>should not be enabled in production</strong>
400      * unless you have a specific reason for doing so.
401      *
402      * @param verboseLoggingEnabled true to enable logging; false to disable it
403      */
setVerboseLoggingEnabled(boolean verboseLoggingEnabled)404     public static void setVerboseLoggingEnabled(boolean verboseLoggingEnabled) {
405         RecyclerView.sVerboseLoggingEnabled = verboseLoggingEnabled;
406     }
407 
408     private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
409 
410     final Recycler mRecycler = new Recycler();
411 
412     SavedState mPendingSavedState;
413 
414     /**
415      * Handles adapter updates
416      */
417     AdapterHelper mAdapterHelper;
418 
419     /**
420      * Handles abstraction between LayoutManager children and RecyclerView children
421      */
422     ChildHelper mChildHelper;
423 
424     /**
425      * Keeps data about views to be used for animations
426      */
427     final ViewInfoStore mViewInfoStore = new ViewInfoStore();
428 
429     /**
430      * Prior to L, there is no way to query this variable which is why we override the setter and
431      * track it here.
432      */
433     boolean mClipToPadding;
434 
435     /**
436      * Note: this Runnable is only ever posted if:
437      * 1) We've been through first layout
438      * 2) We know we have a fixed size (mHasFixedSize)
439      * 3) We're attached
440      */
441     final Runnable mUpdateChildViewsRunnable = new Runnable() {
442         @Override
443         public void run() {
444             if (!mFirstLayoutComplete || isLayoutRequested()) {
445                 // a layout request will happen, we should not do layout here.
446                 return;
447             }
448             if (!mIsAttached) {
449                 requestLayout();
450                 // if we are not attached yet, mark us as requiring layout and skip
451                 return;
452             }
453             if (mLayoutSuppressed) {
454                 mLayoutWasDefered = true;
455                 return; //we'll process updates when ice age ends.
456             }
457             consumePendingUpdateOperations();
458         }
459     };
460 
461     final Rect mTempRect = new Rect();
462     private final Rect mTempRect2 = new Rect();
463     final RectF mTempRectF = new RectF();
464     Adapter mAdapter;
465     @VisibleForTesting
466     LayoutManager mLayout;
467     // TODO: Remove this once setRecyclerListener has been removed.
468     RecyclerListener mRecyclerListener;
469     // default access to avoid the need for synthetic accessors for Recycler inner class.
470     final List<RecyclerListener> mRecyclerListeners = new ArrayList<>();
471     final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
472     private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
473             new ArrayList<>();
474     private OnItemTouchListener mInterceptingOnItemTouchListener;
475     boolean mIsAttached;
476     boolean mHasFixedSize;
477     boolean mEnableFastScroller;
478     @VisibleForTesting
479     boolean mFirstLayoutComplete;
480 
481     /**
482      * The current depth of nested calls to {@link #startInterceptRequestLayout()} (number of
483      * calls to {@link #startInterceptRequestLayout()} - number of calls to
484      * {@link #stopInterceptRequestLayout(boolean)} .  This is used to signal whether we
485      * should defer layout operations caused by layout requests from children of
486      * {@link RecyclerView}.
487      */
488     private int mInterceptRequestLayoutDepth = 0;
489 
490     /**
491      * True if a call to requestLayout was intercepted and prevented from executing like normal and
492      * we plan on continuing with normal execution later.
493      */
494     boolean mLayoutWasDefered;
495 
496     boolean mLayoutSuppressed;
497     private boolean mIgnoreMotionEventTillDown;
498 
499     // binary OR of change events that were eaten during a layout or scroll.
500     private int mEatenAccessibilityChangeFlags;
501     boolean mAdapterUpdateDuringMeasure;
502 
503     private final AccessibilityManager mAccessibilityManager;
504     private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
505 
506     /**
507      * True after an event occurs that signals that the entire data set has changed. In that case,
508      * we cannot run any animations since we don't know what happened until layout.
509      *
510      * Attached items are invalid until next layout, at which point layout will animate/replace
511      * items as necessary, building up content from the (effectively) new adapter from scratch.
512      *
513      * Cached items must be discarded when setting this to true, so that the cache may be freely
514      * used by prefetching until the next layout occurs.
515      *
516      * @see #processDataSetCompletelyChanged(boolean)
517      */
518     boolean mDataSetHasChangedAfterLayout = false;
519 
520     /**
521      * True after the data set has completely changed and
522      * {@link LayoutManager#onItemsChanged(RecyclerView)} should be called during the subsequent
523      * measure/layout.
524      *
525      * @see #processDataSetCompletelyChanged(boolean)
526      */
527     boolean mDispatchItemsChangedEvent = false;
528 
529     /**
530      * This variable is incremented during a dispatchLayout and/or scroll.
531      * Some methods should not be called during these periods (e.g. adapter data change).
532      * Doing so will create hard to find bugs so we better check it and throw an exception.
533      *
534      * @see #assertInLayoutOrScroll(String)
535      * @see #assertNotInLayoutOrScroll(String)
536      */
537     private int mLayoutOrScrollCounter = 0;
538 
539     /**
540      * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
541      * (for API compatibility).
542      * <p>
543      * It is a bad practice for a developer to update the data in a scroll callback since it is
544      * potentially called during a layout.
545      */
546     private int mDispatchScrollCounter = 0;
547 
548     private @NonNull EdgeEffectFactory mEdgeEffectFactory = sDefaultEdgeEffectFactory;
549     private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
550 
551     ItemAnimator mItemAnimator = new DefaultItemAnimator();
552 
553     private static final int INVALID_POINTER = -1;
554 
555     /**
556      * The RecyclerView is not currently scrolling.
557      *
558      * @see #getScrollState()
559      */
560     public static final int SCROLL_STATE_IDLE = 0;
561 
562     /**
563      * The RecyclerView is currently being dragged by outside input such as user touch input.
564      *
565      * @see #getScrollState()
566      */
567     public static final int SCROLL_STATE_DRAGGING = 1;
568 
569     /**
570      * The RecyclerView is currently animating to a final position while not under
571      * outside control.
572      *
573      * @see #getScrollState()
574      */
575     public static final int SCROLL_STATE_SETTLING = 2;
576 
577     static final long FOREVER_NS = Long.MAX_VALUE;
578 
579     // Touch/scrolling handling
580 
581     private int mScrollState = SCROLL_STATE_IDLE;
582     private int mScrollPointerId = INVALID_POINTER;
583     private VelocityTracker mVelocityTracker;
584     private int mInitialTouchX;
585     private int mInitialTouchY;
586     private int mLastTouchX;
587     private int mLastTouchY;
588     private int mTouchSlop;
589     private OnFlingListener mOnFlingListener;
590     private final int mMinFlingVelocity;
591     private final int mMaxFlingVelocity;
592 
593     // This value is used when handling rotary encoder generic motion events.
594     float mScaledHorizontalScrollFactor = Float.MIN_VALUE;
595     float mScaledVerticalScrollFactor = Float.MIN_VALUE;
596 
597     private boolean mPreserveFocusAfterLayout = true;
598 
599     final ViewFlinger mViewFlinger = new ViewFlinger();
600 
601     GapWorker mGapWorker;
602     GapWorker.LayoutPrefetchRegistryImpl mPrefetchRegistry =
603             ALLOW_THREAD_GAP_WORK ? new GapWorker.LayoutPrefetchRegistryImpl() : null;
604 
605     final State mState = new State();
606 
607     private OnScrollListener mScrollListener;
608     private List<OnScrollListener> mScrollListeners;
609 
610     // For use in item animations
611     boolean mItemsAddedOrRemoved = false;
612     boolean mItemsChanged = false;
613     private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
614             new ItemAnimatorRestoreListener();
615     boolean mPostedAnimatorRunner = false;
616     RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
617     private ChildDrawingOrderCallback mChildDrawingOrderCallback;
618 
619     // simple array to keep min and max child position during a layout calculation
620     // preserved not to create a new one in each layout pass
621     private final int[] mMinMaxLayoutPositions = new int[2];
622 
623     private NestedScrollingChildHelper mScrollingChildHelper;
624     private final int[] mScrollOffset = new int[2];
625     private final int[] mNestedOffsets = new int[2];
626 
627     // Reusable int array to be passed to method calls that mutate it in order to "return" two ints.
628     final int[] mReusableIntPair = new int[2];
629 
630     /**
631      * These are views that had their a11y importance changed during a layout. We defer these events
632      * until the end of the layout because a11y service may make sync calls back to the RV while
633      * the View's state is undefined.
634      */
635     @VisibleForTesting
636     final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList<>();
637 
638     private Runnable mItemAnimatorRunner = new Runnable() {
639         @Override
640         public void run() {
641             if (mItemAnimator != null) {
642                 mItemAnimator.runPendingAnimations();
643             }
644             mPostedAnimatorRunner = false;
645         }
646     };
647 
648     static final Interpolator sQuinticInterpolator = new Interpolator() {
649         @Override
650         public float getInterpolation(float t) {
651             t -= 1.0f;
652             return t * t * t * t * t + 1.0f;
653         }
654     };
655 
656     static final StretchEdgeEffectFactory sDefaultEdgeEffectFactory =
657             new StretchEdgeEffectFactory();
658 
659     // These fields are only used to track whether we need to layout and measure RV children in
660     // onLayout.
661     //
662     // We track this information because there is an optimized path such that when
663     // LayoutManager#isAutoMeasureEnabled() returns true and we are measured with
664     // MeasureSpec.EXACTLY in both dimensions, we skip measuring and layout children till the
665     // layout phase.
666     //
667     // However, there are times when we are first measured with something other than
668     // MeasureSpec.EXACTLY in both dimensions, in which case we measure and layout children during
669     // onMeasure. Then if we are measured again with EXACTLY, and we skip measurement, we will
670     // get laid out with a different size than we were last aware of being measured with.  If
671     // that happens and we don't check for it, we may not remeasure children, which would be a bug.
672     //
673     // mLastAutoMeasureNonExactMeasureResult tracks our last known measurements in this case, and
674     // mLastAutoMeasureSkippedDueToExact tracks whether or not we skipped.  So, whenever we
675     // layout, we can see if our last known measurement information is different from our actual
676     // laid out size, and if it is, only then do we remeasure and relayout children.
677     private boolean mLastAutoMeasureSkippedDueToExact;
678     private int mLastAutoMeasureNonExactMeasuredWidth = 0;
679     private int mLastAutoMeasureNonExactMeasuredHeight = 0;
680 
681     /**
682      * Whether or not the device has {@link #LOW_RES_ROTARY_ENCODER_FEATURE}. Computed once and
683      * cached, since it's a static value that would not change on consecutive calls.
684      */
685     @VisibleForTesting boolean mLowResRotaryEncoderFeature;
686 
687     /**
688      * The callback to convert view info diffs into animations.
689      */
690     private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
691             new ViewInfoStore.ProcessCallback() {
692                 @Override
693                 public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
694                         @Nullable ItemHolderInfo postInfo) {
695                     mRecycler.unscrapView(viewHolder);
696                     animateDisappearance(viewHolder, info, postInfo);
697                 }
698 
699                 @Override
700                 public void processAppeared(ViewHolder viewHolder,
701                         ItemHolderInfo preInfo, ItemHolderInfo info) {
702                     animateAppearance(viewHolder, preInfo, info);
703                 }
704 
705                 @Override
706                 public void processPersistent(ViewHolder viewHolder,
707                         @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
708                     viewHolder.setIsRecyclable(false);
709                     if (mDataSetHasChangedAfterLayout) {
710                         // since it was rebound, use change instead as we'll be mapping them from
711                         // stable ids. If stable ids were false, we would not be running any
712                         // animations
713                         if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo,
714                                 postInfo)) {
715                             postAnimationRunner();
716                         }
717                     } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
718                         postAnimationRunner();
719                     }
720                 }
721 
722                 @Override
723                 public void unused(ViewHolder viewHolder) {
724                     mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
725                 }
726             };
727 
728     private final DifferentialMotionFlingTarget
729             mDifferentialMotionFlingTarget =
730             new DifferentialMotionFlingTarget() {
731                 @Override
732                 public boolean startDifferentialMotionFling(float velocity) {
733                     int vx = 0;
734                     int vy = 0;
735                     if (mLayout.canScrollVertically()) {
736                         vy = (int) velocity;
737                     } else if (mLayout.canScrollHorizontally()) {
738                         vx = (int) velocity;
739                     }
740 
741                     if (vx == 0 && vy == 0) {
742                         return false;
743                     }
744 
745                     stopScroll();
746                     // Fling with no threshold check, since the DifferentialMotionFlingHelper should
747                     //  have handled this already.
748                     return flingNoThresholdCheck(vx, vy);
749                 }
750 
751                 @Override
752                 public void stopDifferentialMotionFling() {
753                     stopScroll();
754                 }
755 
756                 @Override
757                 public float getScaledScrollFactor() {
758                     if (mLayout.canScrollVertically()) {
759                         return -mScaledVerticalScrollFactor;
760                     }
761                     if (mLayout.canScrollHorizontally()) {
762                         return -mScaledHorizontalScrollFactor;
763                     }
764                     return 0;
765                 }
766 
767             };
768 
769     @VisibleForTesting
770     DifferentialMotionFlingController mDifferentialMotionFlingController =
771             new DifferentialMotionFlingController(getContext(), mDifferentialMotionFlingTarget);
772 
773     @VisibleForTesting
774     @Nullable
775     ScrollFeedbackProviderCompat mScrollFeedbackProvider;
776 
RecyclerView(@onNull Context context)777     public RecyclerView(@NonNull Context context) {
778         this(context, null);
779     }
780 
RecyclerView(@onNull Context context, @Nullable AttributeSet attrs)781     public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
782         this(context, attrs, R.attr.recyclerViewStyle);
783     }
784 
RecyclerView(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)785     public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
786         super(context, attrs, defStyleAttr);
787         setScrollContainer(true);
788         setFocusableInTouchMode(true);
789 
790         final ViewConfiguration vc = ViewConfiguration.get(context);
791         mTouchSlop = vc.getScaledTouchSlop();
792         mScaledHorizontalScrollFactor =
793                 ViewConfigurationCompat.getScaledHorizontalScrollFactor(vc, context);
794         mScaledVerticalScrollFactor =
795                 ViewConfigurationCompat.getScaledVerticalScrollFactor(vc, context);
796         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
797         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
798         final float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
799         mPhysicalCoef = SensorManager.GRAVITY_EARTH // g (m/s^2)
800                 * 39.37f // inch/meter
801                 * ppi
802                 * 0.84f; // look and feel tuning
803         setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
804 
805         mItemAnimator.setListener(mItemAnimatorListener);
806         initAdapterManager();
807         initChildrenHelper();
808         initAutofill();
809         // If not explicitly specified this view is important for accessibility.
810         if (this.getImportantForAccessibility()
811                 == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
812             setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
813         }
814         mAccessibilityManager = (AccessibilityManager) getContext()
815                 .getSystemService(Context.ACCESSIBILITY_SERVICE);
816         setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
817 
818         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
819                 defStyleAttr, 0);
820 
821         ViewCompat.saveAttributeDataForStyleable(this, context, R.styleable.RecyclerView,
822                 attrs, a, defStyleAttr, 0);
823         String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
824         int descendantFocusability = a.getInt(
825                 R.styleable.RecyclerView_android_descendantFocusability, -1);
826         if (descendantFocusability == -1) {
827             setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
828         }
829         mClipToPadding = a.getBoolean(R.styleable.RecyclerView_android_clipToPadding, true);
830         mEnableFastScroller = a.getBoolean(R.styleable.RecyclerView_fastScrollEnabled, false);
831         if (mEnableFastScroller) {
832             StateListDrawable verticalThumbDrawable = (StateListDrawable) a
833                     .getDrawable(R.styleable.RecyclerView_fastScrollVerticalThumbDrawable);
834             Drawable verticalTrackDrawable = a
835                     .getDrawable(R.styleable.RecyclerView_fastScrollVerticalTrackDrawable);
836             StateListDrawable horizontalThumbDrawable = (StateListDrawable) a
837                     .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalThumbDrawable);
838             Drawable horizontalTrackDrawable = a
839                     .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalTrackDrawable);
840             initFastScroller(verticalThumbDrawable, verticalTrackDrawable,
841                     horizontalThumbDrawable, horizontalTrackDrawable);
842         }
843         a.recycle();
844 
845         mLowResRotaryEncoderFeature =
846                 context.getPackageManager().hasSystemFeature(LOW_RES_ROTARY_ENCODER_FEATURE);
847 
848         // Create the layoutManager if specified.
849         createLayoutManager(context, layoutManagerName, attrs, defStyleAttr, 0);
850 
851         boolean nestedScrollingEnabled = true;
852         if (Build.VERSION.SDK_INT >= 21) {
853             a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
854                     defStyleAttr, 0);
855             ViewCompat.saveAttributeDataForStyleable(this,
856                     context, NESTED_SCROLLING_ATTRS, attrs, a, defStyleAttr, 0);
857             nestedScrollingEnabled = a.getBoolean(0, true);
858             a.recycle();
859         }
860         // Re-set whether nested scrolling is enabled so that it is set on all API levels
861         setNestedScrollingEnabled(nestedScrollingEnabled);
862         PoolingContainer.setPoolingContainer(this, true);
863     }
864 
865     /**
866      * Label appended to all public exception strings, used to help find which RV in an app is
867      * hitting an exception.
868      */
exceptionLabel()869     String exceptionLabel() {
870         return " " + super.toString()
871                 + ", adapter:" + mAdapter
872                 + ", layout:" + mLayout
873                 + ", context:" + getContext();
874     }
875 
876     /**
877      * If not explicitly specified, this view and its children don't support autofill.
878      * <p>
879      * This is done because autofill's means of uniquely identifying views doesn't work out of the
880      * box with View recycling.
881      */
882     @SuppressLint("InlinedApi")
initAutofill()883     private void initAutofill() {
884         if (ViewCompat.getImportantForAutofill(this) == View.IMPORTANT_FOR_AUTOFILL_AUTO) {
885             ViewCompat.setImportantForAutofill(this,
886                     View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
887         }
888     }
889 
890     /**
891      * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
892      *
893      * @return An instance of AccessibilityDelegateCompat used by RecyclerView
894      */
getCompatAccessibilityDelegate()895     public @Nullable RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
896         return mAccessibilityDelegate;
897     }
898 
899     /**
900      * Sets the accessibility delegate compatibility implementation used by RecyclerView.
901      *
902      * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
903      */
setAccessibilityDelegateCompat( @ullable RecyclerViewAccessibilityDelegate accessibilityDelegate)904     public void setAccessibilityDelegateCompat(
905             @Nullable RecyclerViewAccessibilityDelegate accessibilityDelegate) {
906         mAccessibilityDelegate = accessibilityDelegate;
907         ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
908     }
909 
910     @Override
getAccessibilityClassName()911     public CharSequence getAccessibilityClassName() {
912         return "androidx.recyclerview.widget.RecyclerView";
913     }
914 
915     /**
916      * Instantiate and set a LayoutManager, if specified in the attributes.
917      */
createLayoutManager(Context context, String className, AttributeSet attrs, int defStyleAttr, int defStyleRes)918     private void createLayoutManager(Context context, String className, AttributeSet attrs,
919             int defStyleAttr, int defStyleRes) {
920         if (className != null) {
921             className = className.trim();
922             if (!className.isEmpty()) {
923                 className = getFullClassName(context, className);
924                 try {
925                     ClassLoader classLoader;
926                     if (isInEditMode()) {
927                         // Stupid layoutlib cannot handle simple class loaders.
928                         classLoader = this.getClass().getClassLoader();
929                     } else {
930                         classLoader = context.getClassLoader();
931                     }
932                     Class<? extends LayoutManager> layoutManagerClass =
933                             Class.forName(className, false, classLoader)
934                                     .asSubclass(LayoutManager.class);
935                     Constructor<? extends LayoutManager> constructor;
936                     Object[] constructorArgs = null;
937                     try {
938                         constructor = layoutManagerClass
939                                 .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
940                         constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
941                     } catch (NoSuchMethodException e) {
942                         try {
943                             constructor = layoutManagerClass.getConstructor();
944                         } catch (NoSuchMethodException e1) {
945                             e1.initCause(e);
946                             throw new IllegalStateException(attrs.getPositionDescription()
947                                     + ": Error creating LayoutManager " + className, e1);
948                         }
949                     }
950                     constructor.setAccessible(true);
951                     setLayoutManager(constructor.newInstance(constructorArgs));
952                 } catch (ClassNotFoundException e) {
953                     throw new IllegalStateException(attrs.getPositionDescription()
954                             + ": Unable to find LayoutManager " + className, e);
955                 } catch (InvocationTargetException e) {
956                     throw new IllegalStateException(attrs.getPositionDescription()
957                             + ": Could not instantiate the LayoutManager: " + className, e);
958                 } catch (InstantiationException e) {
959                     throw new IllegalStateException(attrs.getPositionDescription()
960                             + ": Could not instantiate the LayoutManager: " + className, e);
961                 } catch (IllegalAccessException e) {
962                     throw new IllegalStateException(attrs.getPositionDescription()
963                             + ": Cannot access non-public constructor " + className, e);
964                 } catch (ClassCastException e) {
965                     throw new IllegalStateException(attrs.getPositionDescription()
966                             + ": Class is not a LayoutManager " + className, e);
967                 }
968             }
969         }
970     }
971 
getFullClassName(Context context, String className)972     private String getFullClassName(Context context, String className) {
973         if (className.charAt(0) == '.') {
974             return context.getPackageName() + className;
975         }
976         if (className.contains(".")) {
977             return className;
978         }
979         return RecyclerView.class.getPackage().getName() + '.' + className;
980     }
981 
initChildrenHelper()982     private void initChildrenHelper() {
983         mChildHelper = new ChildHelper(new ChildHelper.Callback() {
984             @Override
985             public int getChildCount() {
986                 return RecyclerView.this.getChildCount();
987             }
988 
989             @Override
990             public void addView(View child, int index) {
991                 if (VERBOSE_TRACING) {
992                     Trace.beginSection("RV addView");
993                 }
994                 RecyclerView.this.addView(child, index);
995                 if (VERBOSE_TRACING) {
996                     Trace.endSection();
997                 }
998                 dispatchChildAttached(child);
999             }
1000 
1001             @Override
1002             public int indexOfChild(View view) {
1003                 return RecyclerView.this.indexOfChild(view);
1004             }
1005 
1006             @Override
1007             public void removeViewAt(int index) {
1008                 final View child = RecyclerView.this.getChildAt(index);
1009                 if (child != null) {
1010                     dispatchChildDetached(child);
1011 
1012                     // Clear any android.view.animation.Animation that may prevent the item from
1013                     // detaching when being removed. If a child is re-added before the
1014                     // lazy detach occurs, it will receive invalid attach/detach sequencing.
1015                     child.clearAnimation();
1016                 }
1017                 if (VERBOSE_TRACING) {
1018                     Trace.beginSection("RV removeViewAt");
1019                 }
1020                 RecyclerView.this.removeViewAt(index);
1021                 if (VERBOSE_TRACING) {
1022                     Trace.endSection();
1023                 }
1024             }
1025 
1026             @Override
1027             public View getChildAt(int offset) {
1028                 return RecyclerView.this.getChildAt(offset);
1029             }
1030 
1031             @Override
1032             public void removeAllViews() {
1033                 final int count = getChildCount();
1034                 for (int i = 0; i < count; i++) {
1035                     View child = getChildAt(i);
1036                     dispatchChildDetached(child);
1037 
1038                     // Clear any android.view.animation.Animation that may prevent the item from
1039                     // detaching when being removed. If a child is re-added before the
1040                     // lazy detach occurs, it will receive invalid attach/detach sequencing.
1041                     child.clearAnimation();
1042                 }
1043                 RecyclerView.this.removeAllViews();
1044             }
1045 
1046             @Override
1047             public ViewHolder getChildViewHolder(View view) {
1048                 return getChildViewHolderInt(view);
1049             }
1050 
1051             @Override
1052             public void attachViewToParent(View child, int index,
1053                     ViewGroup.LayoutParams layoutParams) {
1054                 final ViewHolder vh = getChildViewHolderInt(child);
1055                 if (vh != null) {
1056                     if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
1057                         throw new IllegalArgumentException("Called attach on a child which is not"
1058                                 + " detached: " + vh + exceptionLabel());
1059                     }
1060                     if (sVerboseLoggingEnabled) {
1061                         Log.d(TAG, "reAttach " + vh);
1062                     }
1063                     vh.clearTmpDetachFlag();
1064                 } else {
1065                     if (sDebugAssertionsEnabled) {
1066                         throw new IllegalArgumentException(
1067                                 "No ViewHolder found for child: " + child + ", index: " + index
1068                                         + exceptionLabel());
1069                     }
1070                 }
1071                 RecyclerView.this.attachViewToParent(child, index, layoutParams);
1072             }
1073 
1074             @Override
1075             public void detachViewFromParent(int offset) {
1076                 final View view = getChildAt(offset);
1077                 if (view != null) {
1078                     final ViewHolder vh = getChildViewHolderInt(view);
1079                     if (vh != null) {
1080                         if (vh.isTmpDetached() && !vh.shouldIgnore()) {
1081                             throw new IllegalArgumentException("called detach on an already"
1082                                     + " detached child " + vh + exceptionLabel());
1083                         }
1084                         if (sVerboseLoggingEnabled) {
1085                             Log.d(TAG, "tmpDetach " + vh);
1086                         }
1087                         vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
1088                     }
1089                 } else {
1090                     if (sDebugAssertionsEnabled) {
1091                         throw new IllegalArgumentException(
1092                                 "No view at offset " + offset + exceptionLabel());
1093                     }
1094                 }
1095                 RecyclerView.this.detachViewFromParent(offset);
1096             }
1097 
1098             @Override
1099             public void onEnteredHiddenState(View child) {
1100                 final ViewHolder vh = getChildViewHolderInt(child);
1101                 if (vh != null) {
1102                     vh.onEnteredHiddenState(RecyclerView.this);
1103                 }
1104             }
1105 
1106             @Override
1107             public void onLeftHiddenState(View child) {
1108                 final ViewHolder vh = getChildViewHolderInt(child);
1109                 if (vh != null) {
1110                     vh.onLeftHiddenState(RecyclerView.this);
1111                 }
1112             }
1113         });
1114     }
1115 
initAdapterManager()1116     void initAdapterManager() {
1117         mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
1118             @Override
1119             public ViewHolder findViewHolder(int position) {
1120                 final ViewHolder vh = findViewHolderForPosition(position, true);
1121                 if (vh == null) {
1122                     return null;
1123                 }
1124                 // ensure it is not hidden because for adapter helper, the only thing matter is that
1125                 // LM thinks view is a child.
1126                 if (mChildHelper.isHidden(vh.itemView)) {
1127                     if (sVerboseLoggingEnabled) {
1128                         Log.d(TAG, "assuming view holder cannot be find because it is hidden");
1129                     }
1130                     return null;
1131                 }
1132                 return vh;
1133             }
1134 
1135             @Override
1136             public void offsetPositionsForRemovingInvisible(int start, int count) {
1137                 offsetPositionRecordsForRemove(start, count, true);
1138                 mItemsAddedOrRemoved = true;
1139                 mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
1140             }
1141 
1142             @Override
1143             public void offsetPositionsForRemovingLaidOutOrNewView(
1144                     int positionStart, int itemCount) {
1145                 offsetPositionRecordsForRemove(positionStart, itemCount, false);
1146                 mItemsAddedOrRemoved = true;
1147             }
1148 
1149 
1150             @Override
1151             public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
1152                 viewRangeUpdate(positionStart, itemCount, payload);
1153                 mItemsChanged = true;
1154             }
1155 
1156             @Override
1157             public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
1158                 dispatchUpdate(op);
1159             }
1160 
1161             void dispatchUpdate(AdapterHelper.UpdateOp op) {
1162                 switch (op.cmd) {
1163                     case AdapterHelper.UpdateOp.ADD:
1164                         mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
1165                         break;
1166                     case AdapterHelper.UpdateOp.REMOVE:
1167                         mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
1168                         break;
1169                     case AdapterHelper.UpdateOp.UPDATE:
1170                         mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
1171                                 op.payload);
1172                         break;
1173                     case AdapterHelper.UpdateOp.MOVE:
1174                         mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
1175                         break;
1176                 }
1177             }
1178 
1179             @Override
1180             public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
1181                 dispatchUpdate(op);
1182             }
1183 
1184             @Override
1185             public void offsetPositionsForAdd(int positionStart, int itemCount) {
1186                 offsetPositionRecordsForInsert(positionStart, itemCount);
1187                 mItemsAddedOrRemoved = true;
1188             }
1189 
1190             @Override
1191             public void offsetPositionsForMove(int from, int to) {
1192                 offsetPositionRecordsForMove(from, to);
1193                 // should we create mItemsMoved ?
1194                 mItemsAddedOrRemoved = true;
1195             }
1196         });
1197     }
1198 
1199     /**
1200      * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
1201      * size is not affected by the adapter contents. RecyclerView can still change its size based
1202      * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
1203      * size of its children or contents of its adapter (except the number of items in the adapter).
1204      * <p>
1205      * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
1206      * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
1207      *
1208      * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
1209      */
setHasFixedSize(boolean hasFixedSize)1210     public void setHasFixedSize(boolean hasFixedSize) {
1211         mHasFixedSize = hasFixedSize;
1212     }
1213 
1214     /**
1215      * @return true if the app has specified that changes in adapter content cannot change
1216      * the size of the RecyclerView itself.
1217      */
hasFixedSize()1218     public boolean hasFixedSize() {
1219         return mHasFixedSize;
1220     }
1221 
1222     @Override
setClipToPadding(boolean clipToPadding)1223     public void setClipToPadding(boolean clipToPadding) {
1224         if (clipToPadding != mClipToPadding) {
1225             invalidateGlows();
1226         }
1227         mClipToPadding = clipToPadding;
1228         super.setClipToPadding(clipToPadding);
1229         if (mFirstLayoutComplete) {
1230             requestLayout();
1231         }
1232     }
1233 
1234     /**
1235      * Returns whether this RecyclerView will clip its children to its padding, and resize (but
1236      * not clip) any EdgeEffect to the padded region, if padding is present.
1237      * <p>
1238      * By default, children are clipped to the padding of their parent
1239      * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
1240      *
1241      * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
1242      * clip) any EdgeEffect to the padded region, false otherwise.
1243      * @attr name android:clipToPadding
1244      */
1245     @Override
getClipToPadding()1246     public boolean getClipToPadding() {
1247         return mClipToPadding;
1248     }
1249 
1250     /**
1251      * Configure the scrolling touch slop for a specific use case.
1252      *
1253      * Set up the RecyclerView's scrolling motion threshold based on common usages.
1254      * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
1255      *
1256      * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
1257      *                     the intended usage of this RecyclerView
1258      */
setScrollingTouchSlop(int slopConstant)1259     public void setScrollingTouchSlop(int slopConstant) {
1260         final ViewConfiguration vc = ViewConfiguration.get(getContext());
1261         switch (slopConstant) {
1262             default:
1263                 Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
1264                         + slopConstant + "; using default value");
1265                 // fall-through
1266             case TOUCH_SLOP_DEFAULT:
1267                 mTouchSlop = vc.getScaledTouchSlop();
1268                 break;
1269 
1270             case TOUCH_SLOP_PAGING:
1271                 mTouchSlop = vc.getScaledPagingTouchSlop();
1272                 break;
1273         }
1274     }
1275 
1276     /**
1277      * Swaps the current adapter with the provided one. It is similar to
1278      * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
1279      * {@link ViewHolder} and does not clear the RecycledViewPool.
1280      * <p>
1281      * Note that it still calls onAdapterChanged callbacks.
1282      *
1283      * @param adapter                       The new adapter to set, or null to set no adapter.
1284      * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
1285      *                                      Views. If adapters have stable ids and/or you want to
1286      *                                      animate the disappearing views, you may prefer to set
1287      *                                      this to false.
1288      * @see #setAdapter(Adapter)
1289      */
swapAdapter(@ullable Adapter adapter, boolean removeAndRecycleExistingViews)1290     public void swapAdapter(@Nullable Adapter adapter, boolean removeAndRecycleExistingViews) {
1291         // bail out if layout is frozen
1292         setLayoutFrozen(false);
1293         setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
1294         processDataSetCompletelyChanged(true);
1295         requestLayout();
1296     }
1297 
1298     /**
1299      * Set a new adapter to provide child views on demand.
1300      * <p>
1301      * When adapter is changed, all existing views are recycled back to the pool. If the pool has
1302      * only one adapter, it will be cleared.
1303      *
1304      * @param adapter The new adapter to set, or null to set no adapter.
1305      * @see #swapAdapter(Adapter, boolean)
1306      */
setAdapter(@ullable Adapter adapter)1307     public void setAdapter(@Nullable Adapter adapter) {
1308         // bail out if layout is frozen
1309         setLayoutFrozen(false);
1310         setAdapterInternal(adapter, false, true);
1311         processDataSetCompletelyChanged(false);
1312         requestLayout();
1313     }
1314 
1315     /**
1316      * Removes and recycles all views - both those currently attached, and those in the Recycler.
1317      */
removeAndRecycleViews()1318     void removeAndRecycleViews() {
1319         // end all running animations
1320         if (mItemAnimator != null) {
1321             mItemAnimator.endAnimations();
1322         }
1323         // Since animations are ended, mLayout.children should be equal to
1324         // recyclerView.children. This may not be true if item animator's end does not work as
1325         // expected. (e.g. not release children instantly). It is safer to use mLayout's child
1326         // count.
1327         if (mLayout != null) {
1328             mLayout.removeAndRecycleAllViews(mRecycler);
1329             mLayout.removeAndRecycleScrapInt(mRecycler);
1330         }
1331         // we should clear it here before adapters are swapped to ensure correct callbacks.
1332         mRecycler.clear();
1333     }
1334 
1335     /**
1336      * Replaces the current adapter with the new one and triggers listeners.
1337      *
1338      * @param adapter                The new adapter
1339      * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
1340      *                               item types with the current adapter (helps us avoid cache
1341      *                               invalidation).
1342      * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
1343      *                               compatibleWithPrevious is false, this parameter is ignored.
1344      */
setAdapterInternal(@ullable Adapter<?> adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews)1345     private void setAdapterInternal(@Nullable Adapter<?> adapter, boolean compatibleWithPrevious,
1346             boolean removeAndRecycleViews) {
1347         if (mAdapter != null) {
1348             mAdapter.unregisterAdapterDataObserver(mObserver);
1349             mAdapter.onDetachedFromRecyclerView(this);
1350         }
1351         if (!compatibleWithPrevious || removeAndRecycleViews) {
1352             removeAndRecycleViews();
1353         }
1354         mAdapterHelper.reset();
1355         final Adapter<?> oldAdapter = mAdapter;
1356         mAdapter = adapter;
1357         if (adapter != null) {
1358             adapter.registerAdapterDataObserver(mObserver);
1359             adapter.onAttachedToRecyclerView(this);
1360         }
1361         if (mLayout != null) {
1362             mLayout.onAdapterChanged(oldAdapter, mAdapter);
1363         }
1364         mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
1365         mState.mStructureChanged = true;
1366     }
1367 
1368     /**
1369      * Retrieves the previously set adapter or null if no adapter is set.
1370      *
1371      * @return The previously set adapter
1372      * @see #setAdapter(Adapter)
1373      */
getAdapter()1374     public @Nullable Adapter getAdapter() {
1375         return mAdapter;
1376     }
1377 
1378     /**
1379      * Register a listener that will be notified whenever a child view is recycled.
1380      *
1381      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1382      * that a child view is no longer needed. If an application associates expensive
1383      * or heavyweight data with item views, this may be a good place to release
1384      * or free those resources.</p>
1385      *
1386      * @param listener Listener to register, or null to clear
1387      * @deprecated Use {@link #addRecyclerListener(RecyclerListener)} and
1388      *     {@link #removeRecyclerListener(RecyclerListener)}
1389      */
1390     @Deprecated
setRecyclerListener(@ullable RecyclerListener listener)1391     public void setRecyclerListener(@Nullable RecyclerListener listener) {
1392         mRecyclerListener = listener;
1393     }
1394 
1395     /**
1396      * Register a listener that will be notified whenever a child view is recycled.
1397      *
1398      * <p>The listeners will be called when a LayoutManager or the RecyclerView decides
1399      * that a child view is no longer needed. If an application associates data with
1400      * the item views being recycled, this may be a good place to release
1401      * or free those resources.</p>
1402      *
1403      * @param listener Listener to register.
1404      */
addRecyclerListener(@onNull RecyclerListener listener)1405     public void addRecyclerListener(@NonNull RecyclerListener listener) {
1406         checkArgument(listener != null, "'listener' arg cannot "
1407                 + "be null.");
1408         mRecyclerListeners.add(listener);
1409     }
1410 
1411     /**
1412      * Removes the provided listener from RecyclerListener list.
1413      *
1414      * @param listener Listener to unregister.
1415      */
removeRecyclerListener(@onNull RecyclerListener listener)1416     public void removeRecyclerListener(@NonNull RecyclerListener listener) {
1417         mRecyclerListeners.remove(listener);
1418     }
1419 
1420     /**
1421      * <p>Return the offset of the RecyclerView's text baseline from the its top
1422      * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
1423      * this method returns -1.</p>
1424      *
1425      * @return the offset of the baseline within the RecyclerView's bounds or -1
1426      * if baseline alignment is not supported
1427      */
1428     @Override
getBaseline()1429     public int getBaseline() {
1430         if (mLayout != null) {
1431             return mLayout.getBaseline();
1432         } else {
1433             return super.getBaseline();
1434         }
1435     }
1436 
1437     /**
1438      * Register a listener that will be notified whenever a child view is attached to or detached
1439      * from RecyclerView.
1440      *
1441      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1442      * that a child view is no longer needed. If an application associates expensive
1443      * or heavyweight data with item views, this may be a good place to release
1444      * or free those resources.</p>
1445      *
1446      * @param listener Listener to register
1447      */
addOnChildAttachStateChangeListener( @onNull OnChildAttachStateChangeListener listener)1448     public void addOnChildAttachStateChangeListener(
1449             @NonNull OnChildAttachStateChangeListener listener) {
1450         if (mOnChildAttachStateListeners == null) {
1451             mOnChildAttachStateListeners = new ArrayList<>();
1452         }
1453         mOnChildAttachStateListeners.add(listener);
1454     }
1455 
1456     /**
1457      * Removes the provided listener from child attached state listeners list.
1458      *
1459      * @param listener Listener to unregister
1460      */
removeOnChildAttachStateChangeListener( @onNull OnChildAttachStateChangeListener listener)1461     public void removeOnChildAttachStateChangeListener(
1462             @NonNull OnChildAttachStateChangeListener listener) {
1463         if (mOnChildAttachStateListeners == null) {
1464             return;
1465         }
1466         mOnChildAttachStateListeners.remove(listener);
1467     }
1468 
1469     /**
1470      * Removes all listeners that were added via
1471      * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
1472      */
clearOnChildAttachStateChangeListeners()1473     public void clearOnChildAttachStateChangeListeners() {
1474         if (mOnChildAttachStateListeners != null) {
1475             mOnChildAttachStateListeners.clear();
1476         }
1477     }
1478 
1479     /**
1480      * Set the {@link LayoutManager} that this RecyclerView will use.
1481      *
1482      * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
1483      * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
1484      * layout arrangements for child views. These arrangements are controlled by the
1485      * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
1486      *
1487      * <p>Several default strategies are provided for common uses such as lists and grids.</p>
1488      *
1489      * @param layout LayoutManager to use
1490      */
setLayoutManager(@ullable LayoutManager layout)1491     public void setLayoutManager(@Nullable LayoutManager layout) {
1492         if (layout == mLayout) {
1493             return;
1494         }
1495         stopScroll();
1496         // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
1497         // chance that LayoutManagers will re-use views.
1498         if (mLayout != null) {
1499             // end all running animations
1500             if (mItemAnimator != null) {
1501                 mItemAnimator.endAnimations();
1502             }
1503             mLayout.removeAndRecycleAllViews(mRecycler);
1504             mLayout.removeAndRecycleScrapInt(mRecycler);
1505             mRecycler.clear();
1506 
1507             if (mIsAttached) {
1508                 mLayout.dispatchDetachedFromWindow(this, mRecycler);
1509             }
1510             mLayout.setRecyclerView(null);
1511             mLayout = null;
1512         } else {
1513             mRecycler.clear();
1514         }
1515         // this is just a defensive measure for faulty item animators.
1516         mChildHelper.removeAllViewsUnfiltered();
1517         mLayout = layout;
1518         if (layout != null) {
1519             if (layout.mRecyclerView != null) {
1520                 throw new IllegalArgumentException("LayoutManager " + layout
1521                         + " is already attached to a RecyclerView:"
1522                         + layout.mRecyclerView.exceptionLabel());
1523             }
1524             mLayout.setRecyclerView(this);
1525             if (mIsAttached) {
1526                 mLayout.dispatchAttachedToWindow(this);
1527             }
1528         }
1529         mRecycler.updateViewCacheSize();
1530         requestLayout();
1531     }
1532 
1533     /**
1534      * Set a {@link OnFlingListener} for this {@link RecyclerView}.
1535      * <p>
1536      * If the {@link OnFlingListener} is set then it will receive
1537      * calls to {@link #fling(int, int)} and will be able to intercept them.
1538      *
1539      * @param onFlingListener The {@link OnFlingListener} instance.
1540      */
setOnFlingListener(@ullable OnFlingListener onFlingListener)1541     public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
1542         mOnFlingListener = onFlingListener;
1543     }
1544 
1545     /**
1546      * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
1547      *
1548      * @return The {@link OnFlingListener} instance currently set (can be null).
1549      */
getOnFlingListener()1550     public @Nullable OnFlingListener getOnFlingListener() {
1551         return mOnFlingListener;
1552     }
1553 
1554     @Override
onSaveInstanceState()1555     protected Parcelable onSaveInstanceState() {
1556         SavedState state = new SavedState(super.onSaveInstanceState());
1557         if (mPendingSavedState != null) {
1558             state.copyFrom(mPendingSavedState);
1559         } else if (mLayout != null) {
1560             state.mLayoutState = mLayout.onSaveInstanceState();
1561         } else {
1562             state.mLayoutState = null;
1563         }
1564 
1565         return state;
1566     }
1567 
1568     @Override
onRestoreInstanceState(Parcelable state)1569     protected void onRestoreInstanceState(Parcelable state) {
1570         if (!(state instanceof SavedState)) {
1571             super.onRestoreInstanceState(state);
1572             return;
1573         }
1574 
1575         mPendingSavedState = (SavedState) state;
1576         super.onRestoreInstanceState(mPendingSavedState.getSuperState());
1577         // Historically, some app developers have used onRestoreInstanceState(State) in ways it
1578         // was never intended. For example, some devs have used it to manually set a state they
1579         // updated themselves such that passing the state here would cause a LayoutManager to
1580         // receive it and update its internal state accordingly, even if state was already
1581         // previously restored. Therefore, it is necessary to always call requestLayout to retain
1582         // the functionality even if it otherwise seems like a strange thing to do.
1583         // ¯\_(ツ)_/¯
1584         requestLayout();
1585     }
1586 
1587     /**
1588      * Override to prevent freezing of any views created by the adapter.
1589      */
1590     @Override
dispatchSaveInstanceState(SparseArray<Parcelable> container)1591     protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1592         dispatchFreezeSelfOnly(container);
1593     }
1594 
1595     /**
1596      * Override to prevent thawing of any views created by the adapter.
1597      */
1598     @Override
dispatchRestoreInstanceState(SparseArray<Parcelable> container)1599     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1600         dispatchThawSelfOnly(container);
1601     }
1602 
1603     /**
1604      * Adds a view to the animatingViews list.
1605      * mAnimatingViews holds the child views that are currently being kept around
1606      * purely for the purpose of being animated out of view. They are drawn as a regular
1607      * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
1608      * as they are managed separately from the regular child views.
1609      *
1610      * @param viewHolder The ViewHolder to be removed
1611      */
addAnimatingView(ViewHolder viewHolder)1612     private void addAnimatingView(ViewHolder viewHolder) {
1613         final View view = viewHolder.itemView;
1614         final boolean alreadyParented = view.getParent() == this;
1615         mRecycler.unscrapView(getChildViewHolder(view));
1616         if (viewHolder.isTmpDetached()) {
1617             // re-attach
1618             mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
1619         } else if (!alreadyParented) {
1620             mChildHelper.addView(view, true);
1621         } else {
1622             mChildHelper.hide(view);
1623         }
1624     }
1625 
1626     /**
1627      * Removes a view from the animatingViews list.
1628      *
1629      * @param view The view to be removed
1630      * @return true if an animating view is removed
1631      * @see #addAnimatingView(RecyclerView.ViewHolder)
1632      */
removeAnimatingView(View view)1633     boolean removeAnimatingView(View view) {
1634         startInterceptRequestLayout();
1635         final boolean removed = mChildHelper.removeViewIfHidden(view);
1636         if (removed) {
1637             final ViewHolder viewHolder = getChildViewHolderInt(view);
1638             mRecycler.unscrapView(viewHolder);
1639             mRecycler.recycleViewHolderInternal(viewHolder);
1640             if (sVerboseLoggingEnabled) {
1641                 Log.d(TAG, "after removing animated view: " + view + ", " + this);
1642             }
1643         }
1644         // only clear request eaten flag if we removed the view.
1645         stopInterceptRequestLayout(!removed);
1646         return removed;
1647     }
1648 
1649     /**
1650      * Return the {@link LayoutManager} currently responsible for
1651      * layout policy for this RecyclerView.
1652      *
1653      * @return The currently bound LayoutManager
1654      */
getLayoutManager()1655     public @Nullable LayoutManager getLayoutManager() {
1656         return mLayout;
1657     }
1658 
1659     /**
1660      * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
1661      * if no pool is set for this view a new one will be created. See
1662      * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
1663      *
1664      * @return The pool used to store recycled item views for reuse.
1665      * @see #setRecycledViewPool(RecycledViewPool)
1666      */
getRecycledViewPool()1667     public @NonNull RecycledViewPool getRecycledViewPool() {
1668         return mRecycler.getRecycledViewPool();
1669     }
1670 
1671     /**
1672      * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
1673      * This can be useful if you have multiple RecyclerViews with adapters that use the same
1674      * view types, for example if you have several data sets with the same kinds of item views
1675      * displayed by a {@link androidx.viewpager.widget.ViewPager}.
1676      *
1677      * @param pool Pool to set. If this parameter is null a new pool will be created and used.
1678      */
setRecycledViewPool(@ullable RecycledViewPool pool)1679     public void setRecycledViewPool(@Nullable RecycledViewPool pool) {
1680         mRecycler.setRecycledViewPool(pool);
1681     }
1682 
1683     /**
1684      * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
1685      *
1686      * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
1687      * @see ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)
1688      */
setViewCacheExtension(@ullable ViewCacheExtension extension)1689     public void setViewCacheExtension(@Nullable ViewCacheExtension extension) {
1690         mRecycler.setViewCacheExtension(extension);
1691     }
1692 
1693     /**
1694      * Set the number of offscreen views to retain before adding them to the potentially shared
1695      * {@link #getRecycledViewPool() recycled view pool}.
1696      *
1697      * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
1698      * a LayoutManager to reuse those views unmodified without needing to return to the adapter
1699      * to rebind them.</p>
1700      *
1701      * @param size Number of views to cache offscreen before returning them to the general
1702      *             recycled view pool
1703      */
setItemViewCacheSize(int size)1704     public void setItemViewCacheSize(int size) {
1705         mRecycler.setViewCacheSize(size);
1706     }
1707 
1708     /**
1709      * Return the current scrolling state of the RecyclerView.
1710      *
1711      * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
1712      * {@link #SCROLL_STATE_SETTLING}
1713      */
getScrollState()1714     public int getScrollState() {
1715         return mScrollState;
1716     }
1717 
setScrollState(int state)1718     void setScrollState(int state) {
1719         if (state == mScrollState) {
1720             return;
1721         }
1722         if (sVerboseLoggingEnabled) {
1723             Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
1724                     new Exception());
1725         }
1726         mScrollState = state;
1727         if (state != SCROLL_STATE_SETTLING) {
1728             stopScrollersInternal();
1729         }
1730         dispatchOnScrollStateChanged(state);
1731     }
1732 
1733     /**
1734      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1735      * affect both measurement and drawing of individual item views.
1736      *
1737      * <p>Item decorations are ordered. Decorations placed earlier in the list will
1738      * be run/queried/drawn first for their effects on item views. Padding added to views
1739      * will be nested; a padding added by an earlier decoration will mean further
1740      * item decorations in the list will be asked to draw/pad within the previous decoration's
1741      * given area.</p>
1742      *
1743      * @param decor Decoration to add
1744      * @param index Position in the decoration chain to insert this decoration at. If this value
1745      *              is negative the decoration will be added at the end.
1746      */
addItemDecoration(@onNull ItemDecoration decor, int index)1747     public void addItemDecoration(@NonNull ItemDecoration decor, int index) {
1748         if (mLayout != null) {
1749             mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
1750                     + " layout");
1751         }
1752         if (mItemDecorations.isEmpty()) {
1753             setWillNotDraw(false);
1754         }
1755         if (index < 0) {
1756             mItemDecorations.add(decor);
1757         } else {
1758             mItemDecorations.add(index, decor);
1759         }
1760         markItemDecorInsetsDirty();
1761         requestLayout();
1762     }
1763 
1764     /**
1765      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1766      * affect both measurement and drawing of individual item views.
1767      *
1768      * <p>Item decorations are ordered. Decorations placed earlier in the list will
1769      * be run/queried/drawn first for their effects on item views. Padding added to views
1770      * will be nested; a padding added by an earlier decoration will mean further
1771      * item decorations in the list will be asked to draw/pad within the previous decoration's
1772      * given area.</p>
1773      *
1774      * @param decor Decoration to add
1775      */
addItemDecoration(@onNull ItemDecoration decor)1776     public void addItemDecoration(@NonNull ItemDecoration decor) {
1777         addItemDecoration(decor, -1);
1778     }
1779 
1780     /**
1781      * Returns an {@link ItemDecoration} previously added to this RecyclerView.
1782      *
1783      * @param index The index position of the desired ItemDecoration.
1784      * @return the ItemDecoration at index position
1785      * @throws IndexOutOfBoundsException on invalid index
1786      */
getItemDecorationAt(int index)1787     public @NonNull ItemDecoration getItemDecorationAt(int index) {
1788         final int size = getItemDecorationCount();
1789         if (index < 0 || index >= size) {
1790             throw new IndexOutOfBoundsException(index + " is an invalid index for size " + size);
1791         }
1792 
1793         return mItemDecorations.get(index);
1794     }
1795 
1796     /**
1797      * Returns the number of {@link ItemDecoration} currently added to this RecyclerView.
1798      *
1799      * @return number of ItemDecorations currently added added to this RecyclerView.
1800      */
getItemDecorationCount()1801     public int getItemDecorationCount() {
1802         return mItemDecorations.size();
1803     }
1804 
1805     /**
1806      * Removes the {@link ItemDecoration} associated with the supplied index position.
1807      *
1808      * @param index The index position of the ItemDecoration to be removed.
1809      */
removeItemDecorationAt(int index)1810     public void removeItemDecorationAt(int index) {
1811         final int size = getItemDecorationCount();
1812         if (index < 0 || index >= size) {
1813             throw new IndexOutOfBoundsException(index + " is an invalid index for size " + size);
1814         }
1815 
1816         removeItemDecoration(getItemDecorationAt(index));
1817     }
1818 
1819     /**
1820      * Remove an {@link ItemDecoration} from this RecyclerView.
1821      *
1822      * <p>The given decoration will no longer impact the measurement and drawing of
1823      * item views.</p>
1824      *
1825      * @param decor Decoration to remove
1826      * @see #addItemDecoration(ItemDecoration)
1827      */
removeItemDecoration(@onNull ItemDecoration decor)1828     public void removeItemDecoration(@NonNull ItemDecoration decor) {
1829         if (mLayout != null) {
1830             mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
1831                     + " layout");
1832         }
1833         mItemDecorations.remove(decor);
1834         if (mItemDecorations.isEmpty()) {
1835             setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
1836         }
1837         markItemDecorInsetsDirty();
1838         requestLayout();
1839     }
1840 
1841     /**
1842      * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
1843      * <p>
1844      * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
1845      * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
1846      * true if childDrawingOrderCallback is not null, false otherwise.
1847      * <p>
1848      * Note that child drawing order may be overridden by View's elevation.
1849      *
1850      * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
1851      *                                  system.
1852      */
setChildDrawingOrderCallback( @ullable ChildDrawingOrderCallback childDrawingOrderCallback)1853     public void setChildDrawingOrderCallback(
1854             @Nullable ChildDrawingOrderCallback childDrawingOrderCallback) {
1855         if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
1856             return;
1857         }
1858         mChildDrawingOrderCallback = childDrawingOrderCallback;
1859         setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
1860     }
1861 
1862     /**
1863      * Set a listener that will be notified of any changes in scroll state or position.
1864      *
1865      * @param listener Listener to set or null to clear
1866      * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
1867      * {@link #removeOnScrollListener(OnScrollListener)}
1868      */
1869     @Deprecated
setOnScrollListener(@ullable OnScrollListener listener)1870     public void setOnScrollListener(@Nullable OnScrollListener listener) {
1871         mScrollListener = listener;
1872     }
1873 
1874     /**
1875      * Add a listener that will be notified of any changes in scroll state or position.
1876      *
1877      * <p>Components that add a listener should take care to remove it when finished.
1878      * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
1879      * to remove all attached listeners.</p>
1880      *
1881      * @param listener listener to set
1882      */
addOnScrollListener(@onNull OnScrollListener listener)1883     public void addOnScrollListener(@NonNull OnScrollListener listener) {
1884         if (mScrollListeners == null) {
1885             mScrollListeners = new ArrayList<>();
1886         }
1887         mScrollListeners.add(listener);
1888     }
1889 
1890     /**
1891      * Remove a listener that was notified of any changes in scroll state or position.
1892      *
1893      * @param listener listener to set or null to clear
1894      */
removeOnScrollListener(@onNull OnScrollListener listener)1895     public void removeOnScrollListener(@NonNull OnScrollListener listener) {
1896         if (mScrollListeners != null) {
1897             mScrollListeners.remove(listener);
1898         }
1899     }
1900 
1901     /**
1902      * Remove all secondary listener that were notified of any changes in scroll state or position.
1903      */
clearOnScrollListeners()1904     public void clearOnScrollListeners() {
1905         if (mScrollListeners != null) {
1906             mScrollListeners.clear();
1907         }
1908     }
1909 
1910     /**
1911      * Convenience method to scroll to a certain position.
1912      *
1913      * RecyclerView does not implement scrolling logic, rather forwards the call to
1914      * {@link RecyclerView.LayoutManager#scrollToPosition(int)}
1915      *
1916      * @param position Scroll to this adapter position
1917      * @see RecyclerView.LayoutManager#scrollToPosition(int)
1918      */
scrollToPosition(int position)1919     public void scrollToPosition(int position) {
1920         if (mLayoutSuppressed) {
1921             return;
1922         }
1923         stopScroll();
1924         if (mLayout == null) {
1925             Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
1926                     + "Call setLayoutManager with a non-null argument.");
1927             return;
1928         }
1929         mLayout.scrollToPosition(position);
1930         awakenScrollBars();
1931     }
1932 
jumpToPositionForSmoothScroller(int position)1933     void jumpToPositionForSmoothScroller(int position) {
1934         if (mLayout == null) {
1935             return;
1936         }
1937 
1938         // If we are jumping to a position, we are in fact scrolling the contents of the RV, so
1939         // we should be sure that we are in the settling state.
1940         setScrollState(SCROLL_STATE_SETTLING);
1941         mLayout.scrollToPosition(position);
1942         awakenScrollBars();
1943     }
1944 
1945     /**
1946      * Starts a smooth scroll to an adapter position.
1947      * <p>
1948      * To support smooth scrolling, you must override
1949      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
1950      * {@link SmoothScroller}.
1951      * <p>
1952      * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
1953      * provide a custom smooth scroll logic, override
1954      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
1955      * LayoutManager.
1956      *
1957      * @param position The adapter position to scroll to
1958      * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
1959      */
smoothScrollToPosition(int position)1960     public void smoothScrollToPosition(int position) {
1961         if (mLayoutSuppressed) {
1962             return;
1963         }
1964         if (mLayout == null) {
1965             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
1966                     + "Call setLayoutManager with a non-null argument.");
1967             return;
1968         }
1969         mLayout.smoothScrollToPosition(this, mState, position);
1970     }
1971 
1972     @Override
scrollTo(int x, int y)1973     public void scrollTo(int x, int y) {
1974         Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
1975                 + "Use scrollToPosition instead");
1976     }
1977 
1978     @Override
dispatchKeyEvent(@ullable KeyEvent event)1979     public boolean dispatchKeyEvent(@Nullable KeyEvent event) {
1980         // Let child to dispatch first, then handle ours if child didn't do it.
1981         if (super.dispatchKeyEvent(event)) {
1982             return true;
1983         }
1984 
1985         LayoutManager layoutManager = getLayoutManager();
1986         // If there is no layout manager, then there is nothing to handle key events for.
1987         if (layoutManager == null) {
1988             return false;
1989         }
1990 
1991         if (layoutManager.canScrollVertically()) {
1992             final int keyCode = event.getKeyCode();
1993             switch (keyCode) {
1994                 case KeyEvent.KEYCODE_PAGE_DOWN:
1995                 case KeyEvent.KEYCODE_PAGE_UP:
1996                     int height = getMeasuredHeight();
1997                     if (keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
1998                         smoothScrollBy(0, height, null, UNDEFINED_DURATION);
1999                     } else {
2000                         smoothScrollBy(0, -height, null, UNDEFINED_DURATION);
2001                     }
2002                     return true;
2003 
2004                 case KeyEvent.KEYCODE_MOVE_HOME:
2005                 case KeyEvent.KEYCODE_MOVE_END:
2006                     final boolean isReversed = layoutManager.isLayoutReversed();
2007 
2008                     final int targetOffset;
2009                     if (keyCode == KeyEvent.KEYCODE_MOVE_HOME) {
2010                         targetOffset = isReversed ? getAdapter().getItemCount() : 0;
2011                     } else {
2012                         targetOffset = isReversed ? 0 : getAdapter().getItemCount();
2013                     }
2014 
2015                     smoothScrollToPosition(targetOffset);
2016                     return true;
2017             }
2018         } else if (layoutManager.canScrollHorizontally()) {
2019             final int keyCode = event.getKeyCode();
2020             switch (keyCode) {
2021                 case KeyEvent.KEYCODE_PAGE_DOWN:
2022                 case KeyEvent.KEYCODE_PAGE_UP:
2023                     int width = getMeasuredWidth();
2024                     if (keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
2025                         smoothScrollBy(width, 0, null, UNDEFINED_DURATION);
2026                     } else {
2027                         smoothScrollBy(-width, 0, null, UNDEFINED_DURATION);
2028                     }
2029                     return true;
2030 
2031                 case KeyEvent.KEYCODE_MOVE_HOME:
2032                 case KeyEvent.KEYCODE_MOVE_END:
2033                     final boolean isReversed = layoutManager.isLayoutReversed();
2034 
2035                     final int targetOffset;
2036                     if (keyCode == KeyEvent.KEYCODE_MOVE_HOME) {
2037                         targetOffset = isReversed ? getAdapter().getItemCount() : 0;
2038                     } else {
2039                         targetOffset = isReversed ? 0 : getAdapter().getItemCount();
2040                     }
2041 
2042                     smoothScrollToPosition(targetOffset);
2043                     return true;
2044             }
2045         }
2046 
2047         return false;
2048     }
2049 
2050     @Override
scrollBy(int x, int y)2051     public void scrollBy(int x, int y) {
2052         if (mLayout == null) {
2053             Log.e(TAG, "Cannot scroll without a LayoutManager set. "
2054                     + "Call setLayoutManager with a non-null argument.");
2055             return;
2056         }
2057         if (mLayoutSuppressed) {
2058             return;
2059         }
2060         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
2061         final boolean canScrollVertical = mLayout.canScrollVertically();
2062         if (canScrollHorizontal || canScrollVertical) {
2063             scrollByInternal(
2064                     canScrollHorizontal ? x : 0,
2065                     canScrollVertical ? y : 0,
2066                     /* horizontalAxis= */ -1,
2067                     /* verticalAxis= */ -1,
2068                     /* ev= */ null,
2069                     TYPE_TOUCH);
2070         }
2071     }
2072 
2073     /**
2074      * Same as {@link RecyclerView#scrollBy(int, int)}, but also participates in nested scrolling.
2075      * @param x  The amount of horizontal scroll requested
2076      * @param y  The amount of vertical scroll requested
2077      * @see androidx.core.view.NestedScrollingChild
2078      */
nestedScrollBy(int x, int y)2079     public void nestedScrollBy(int x, int y) {
2080         nestedScrollByInternal(x, y, -1, -1, null, TYPE_NON_TOUCH);
2081     }
2082 
2083     /**
2084      * Similar to {@link RecyclerView#scrollByInternal(int, int, int, int, MotionEvent, int)}, but
2085      * fully participates in nested scrolling "end to end", meaning that it will start nested
2086      * scrolling, participate in nested scrolling, and then end nested scrolling all within one
2087      * call.
2088      *
2089      * @param x The amount of horizontal scroll requested.
2090      * @param y The amount of vertical scroll requested.
2091      * @param horizontalAxis the {@link MotionEvent} axis that caused the {@code x} scroll, or -1 if
2092      *      not known.
2093      * @param verticalAxis the {@link MotionEvent} axis that caused the {@code y} scroll, or -1 if
2094      *      not known.
2095      * @param motionEvent The originating MotionEvent if any.
2096      * @param type The type of nested scrolling to engage in (TYPE_TOUCH or TYPE_NON_TOUCH).
2097      */
2098     @SuppressWarnings("SameParameterValue")
nestedScrollByInternal( int x, int y, int horizontalAxis, int verticalAxis, @Nullable MotionEvent motionEvent, int type)2099     private void nestedScrollByInternal(
2100             int x,
2101             int y,
2102             int horizontalAxis,
2103             int verticalAxis,
2104             @Nullable MotionEvent motionEvent,
2105             int type) {
2106         if (mLayout == null) {
2107             Log.e(TAG, "Cannot scroll without a LayoutManager set. "
2108                     + "Call setLayoutManager with a non-null argument.");
2109             return;
2110         }
2111         if (mLayoutSuppressed) {
2112             return;
2113         }
2114         mReusableIntPair[0] = 0;
2115         mReusableIntPair[1] = 0;
2116         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
2117         final boolean canScrollVertical = mLayout.canScrollVertically();
2118 
2119         int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2120         if (canScrollHorizontal) {
2121             nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2122         }
2123         if (canScrollVertical) {
2124             nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2125         }
2126 
2127         // If there is no MotionEvent, treat it as center-aligned edge effect:
2128         float verticalDisplacement = motionEvent == null ? getHeight() / 2f : motionEvent.getY();
2129         float horizontalDisplacement = motionEvent == null ? getWidth() / 2f : motionEvent.getX();
2130         x -= releaseHorizontalGlow(x, verticalDisplacement);
2131         y -= releaseVerticalGlow(y, horizontalDisplacement);
2132         startNestedScroll(nestedScrollAxis, type);
2133         if (dispatchNestedPreScroll(
2134                 canScrollHorizontal ? x : 0,
2135                 canScrollVertical ? y : 0,
2136                 mReusableIntPair, mScrollOffset, type
2137         )) {
2138             x -= mReusableIntPair[0];
2139             y -= mReusableIntPair[1];
2140         }
2141 
2142         scrollByInternal(
2143                 canScrollHorizontal ? x : 0,
2144                 canScrollVertical ? y : 0,
2145                 horizontalAxis,
2146                 verticalAxis,
2147                 motionEvent, type);
2148         if (mGapWorker != null && (x != 0 || y != 0)) {
2149             mGapWorker.postFromTraversal(this, x, y);
2150         }
2151         stopNestedScroll(type);
2152     }
2153 
2154     /**
2155      * Scrolls the RV by 'dx' and 'dy' via calls to
2156      * {@link LayoutManager#scrollHorizontallyBy(int, Recycler, State)} and
2157      * {@link LayoutManager#scrollVerticallyBy(int, Recycler, State)}.
2158      *
2159      * Also sets how much of the scroll was actually consumed in 'consumed' parameter (indexes 0 and
2160      * 1 for the x axis and y axis, respectively).
2161      *
2162      * This method should only be called in the context of an existing scroll operation such that
2163      * any other necessary operations (such as a call to {@link #consumePendingUpdateOperations()})
2164      * is already handled.
2165      */
scrollStep(int dx, int dy, int @Nullable [] consumed)2166     void scrollStep(int dx, int dy, int @Nullable [] consumed) {
2167         startInterceptRequestLayout();
2168         onEnterLayoutOrScroll();
2169 
2170         Trace.beginSection(TRACE_SCROLL_TAG);
2171         fillRemainingScrollValues(mState);
2172 
2173         int consumedX = 0;
2174         int consumedY = 0;
2175         if (dx != 0) {
2176             consumedX = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
2177         }
2178         if (dy != 0) {
2179             consumedY = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
2180         }
2181 
2182         Trace.endSection();
2183         repositionShadowingViews();
2184 
2185         onExitLayoutOrScroll();
2186         stopInterceptRequestLayout(false);
2187 
2188         if (consumed != null) {
2189             consumed[0] = consumedX;
2190             consumed[1] = consumedY;
2191         }
2192     }
2193 
2194     /**
2195      * Helper method reflect data changes to the state.
2196      * <p>
2197      * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
2198      * but data actually changed.
2199      * <p>
2200      * This method consumes all deferred changes to avoid that case.
2201      */
consumePendingUpdateOperations()2202     void consumePendingUpdateOperations() {
2203         if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
2204             Trace.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
2205             dispatchLayout();
2206             Trace.endSection();
2207             return;
2208         }
2209         if (!mAdapterHelper.hasPendingUpdates()) {
2210             return;
2211         }
2212 
2213         // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
2214         // of the visible items is affected and if not, just ignore the change.
2215         if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
2216                 .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
2217                         | AdapterHelper.UpdateOp.MOVE)) {
2218             Trace.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
2219             startInterceptRequestLayout();
2220             onEnterLayoutOrScroll();
2221             mAdapterHelper.preProcess();
2222             if (!mLayoutWasDefered) {
2223                 if (hasUpdatedView()) {
2224                     dispatchLayout();
2225                 } else {
2226                     // no need to layout, clean state
2227                     mAdapterHelper.consumePostponedUpdates();
2228                 }
2229             }
2230             stopInterceptRequestLayout(true);
2231             onExitLayoutOrScroll();
2232             Trace.endSection();
2233         } else if (mAdapterHelper.hasPendingUpdates()) {
2234             Trace.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
2235             dispatchLayout();
2236             Trace.endSection();
2237         }
2238     }
2239 
2240     /**
2241      * @return True if an existing view holder needs to be updated
2242      */
hasUpdatedView()2243     private boolean hasUpdatedView() {
2244         final int childCount = mChildHelper.getChildCount();
2245         for (int i = 0; i < childCount; i++) {
2246             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
2247             if (holder == null || holder.shouldIgnore()) {
2248                 continue;
2249             }
2250             if (holder.isUpdated()) {
2251                 return true;
2252             }
2253         }
2254         return false;
2255     }
2256 
2257     /**
2258      * Does not perform bounds checking. Used by internal methods that have already validated input.
2259      * <p>
2260      * It also reports any unused scroll request to the related EdgeEffect.
2261      *
2262      * @param x  The amount of horizontal scroll request
2263      * @param y  The amount of vertical scroll request
2264      * @param horizontalAxis the {@link MotionEvent} axis that caused the {@code x} scroll, or -1 if
2265      *      not known.
2266      * @param verticalAxis the {@link MotionEvent} axis that caused the {@code y} scroll, or -1 if
2267      *      not known.
2268      * @param ev The originating MotionEvent, or null if unknown.
2269      * @param type NestedScrollType, TOUCH or NON_TOUCH.
2270      * @return Whether any scroll was consumed in either direction.
2271      */
scrollByInternal( int x, int y, int horizontalAxis, int verticalAxis, @Nullable MotionEvent ev, int type)2272     boolean scrollByInternal(
2273             int x,
2274             int y,
2275             int horizontalAxis,
2276             int verticalAxis,
2277             @Nullable MotionEvent ev,
2278             int type) {
2279         int unconsumedX = 0;
2280         int unconsumedY = 0;
2281         int consumedX = 0;
2282         int consumedY = 0;
2283 
2284         consumePendingUpdateOperations();
2285         if (mAdapter != null) {
2286             mReusableIntPair[0] = 0;
2287             mReusableIntPair[1] = 0;
2288             scrollStep(x, y, mReusableIntPair);
2289             consumedX = mReusableIntPair[0];
2290             consumedY = mReusableIntPair[1];
2291             unconsumedX = x - consumedX;
2292             unconsumedY = y - consumedY;
2293         }
2294         if (!mItemDecorations.isEmpty()) {
2295             invalidate();
2296         }
2297 
2298         mReusableIntPair[0] = 0;
2299         mReusableIntPair[1] = 0;
2300         dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset,
2301                 type, mReusableIntPair);
2302         unconsumedX -= mReusableIntPair[0];
2303         unconsumedY -= mReusableIntPair[1];
2304         boolean consumedNestedScroll = mReusableIntPair[0] != 0 || mReusableIntPair[1] != 0;
2305 
2306         // Update the last touch co-ords, taking any scroll offset into account
2307         mLastTouchX -= mScrollOffset[0];
2308         mLastTouchY -= mScrollOffset[1];
2309         mNestedOffsets[0] += mScrollOffset[0];
2310         mNestedOffsets[1] += mScrollOffset[1];
2311 
2312         if (ev != null) {
2313             if (consumedX != 0) {
2314                 getScrollFeedbackProvider().onScrollProgress(
2315                         ev.getDeviceId(), ev.getSource(), horizontalAxis, consumedX);
2316             }
2317             if (consumedY != 0) {
2318                 getScrollFeedbackProvider().onScrollProgress(
2319                         ev.getDeviceId(), ev.getSource(), verticalAxis, consumedY);
2320             }
2321         }
2322 
2323         if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
2324             if (ev != null && !MotionEventCompat.isFromSource(ev, InputDevice.SOURCE_MOUSE)) {
2325                 pullGlows(ev, ev.getX(), horizontalAxis, unconsumedX, ev.getY(), verticalAxis,
2326                         unconsumedY);
2327                 // For rotary encoders, we release stretch EdgeEffects after they are pulled, to
2328                 // avoid the effects being stuck pulled.
2329                 if (Build.VERSION.SDK_INT >= 31
2330                         && MotionEventCompat.isFromSource(ev, InputDevice.SOURCE_ROTARY_ENCODER)) {
2331                     releaseGlows();
2332                 }
2333             }
2334             considerReleasingGlowsOnScroll(x, y);
2335         }
2336         if (consumedX != 0 || consumedY != 0) {
2337             dispatchOnScrolled(consumedX, consumedY);
2338         }
2339         if (!awakenScrollBars()) {
2340             invalidate();
2341         }
2342         return consumedNestedScroll || consumedX != 0 || consumedY != 0;
2343     }
2344 
2345     /**
2346      * If either of the horizontal edge glows are currently active, this consumes part or all of
2347      * deltaX on the edge glow.
2348      *
2349      * @param deltaX The pointer motion, in pixels, in the horizontal direction, positive
2350      *                         for moving down and negative for moving up.
2351      * @param y The vertical position of the pointer.
2352      * @return The amount of <code>deltaX</code> that has been consumed by the
2353      * edge glow.
2354      */
releaseHorizontalGlow(int deltaX, float y)2355     private int releaseHorizontalGlow(int deltaX, float y) {
2356         // First allow releasing existing overscroll effect:
2357         float consumed = 0;
2358         float displacement = y / getHeight();
2359         float pullDistance = (float) deltaX / getWidth();
2360         if (mLeftGlow != null && EdgeEffectCompat.getDistance(mLeftGlow) != 0) {
2361             if (canScrollHorizontally(-1)) {
2362                 mLeftGlow.onRelease();
2363             } else {
2364                 consumed = -EdgeEffectCompat.onPullDistance(mLeftGlow, -pullDistance,
2365                         1 - displacement);
2366                 if (EdgeEffectCompat.getDistance(mLeftGlow) == 0) {
2367                     mLeftGlow.onRelease();
2368                 }
2369             }
2370             invalidate();
2371         } else if (mRightGlow != null && EdgeEffectCompat.getDistance(mRightGlow) != 0) {
2372             if (canScrollHorizontally(1)) {
2373                 mRightGlow.onRelease();
2374             } else {
2375                 consumed = EdgeEffectCompat.onPullDistance(mRightGlow, pullDistance, displacement);
2376                 if (EdgeEffectCompat.getDistance(mRightGlow) == 0) {
2377                     mRightGlow.onRelease();
2378                 }
2379             }
2380             invalidate();
2381         }
2382         return Math.round(consumed * getWidth());
2383     }
2384 
2385     /**
2386      * If either of the vertical edge glows are currently active, this consumes part or all of
2387      * deltaY on the edge glow.
2388      *
2389      * @param deltaY The pointer motion, in pixels, in the vertical direction, positive
2390      *                         for moving down and negative for moving up.
2391      * @param x The vertical position of the pointer.
2392      * @return The amount of <code>deltaY</code> that has been consumed by the
2393      * edge glow.
2394      */
releaseVerticalGlow(int deltaY, float x)2395     private int releaseVerticalGlow(int deltaY, float x) {
2396         // First allow releasing existing overscroll effect:
2397         float consumed = 0;
2398         float displacement = x / getWidth();
2399         float pullDistance = (float) deltaY / getHeight();
2400         if (mTopGlow != null && EdgeEffectCompat.getDistance(mTopGlow) != 0) {
2401             if (canScrollVertically(-1)) {
2402                 mTopGlow.onRelease();
2403             } else {
2404                 consumed = -EdgeEffectCompat.onPullDistance(mTopGlow, -pullDistance, displacement);
2405                 if (EdgeEffectCompat.getDistance(mTopGlow) == 0) {
2406                     mTopGlow.onRelease();
2407                 }
2408             }
2409             invalidate();
2410         } else if (mBottomGlow != null && EdgeEffectCompat.getDistance(mBottomGlow) != 0) {
2411             if (canScrollVertically(1)) {
2412                 mBottomGlow.onRelease();
2413             } else {
2414                 consumed = EdgeEffectCompat.onPullDistance(mBottomGlow, pullDistance,
2415                         1 - displacement);
2416                 if (EdgeEffectCompat.getDistance(mBottomGlow) == 0) {
2417                     mBottomGlow.onRelease();
2418                 }
2419             }
2420             invalidate();
2421         }
2422         return Math.round(consumed * getHeight());
2423     }
2424 
2425     /**
2426      * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
2427      * range. This value is used to compute the position of the thumb within the scrollbar's track.
2428      * </p>
2429      *
2430      * <p>The range is expressed in arbitrary units that must be the same as the units used by
2431      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
2432      *
2433      * <p>Default implementation returns 0.</p>
2434      *
2435      * <p>If you want to support scroll bars, override
2436      * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
2437      * LayoutManager. </p>
2438      *
2439      * @return The horizontal offset of the scrollbar's thumb
2440      * @see RecyclerView.LayoutManager#computeHorizontalScrollOffset
2441      * (RecyclerView.State)
2442      */
2443     @Override
computeHorizontalScrollOffset()2444     public int computeHorizontalScrollOffset() {
2445         if (mLayout == null) {
2446             return 0;
2447         }
2448         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
2449     }
2450 
2451     /**
2452      * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
2453      * horizontal range. This value is used to compute the length of the thumb within the
2454      * scrollbar's track.</p>
2455      *
2456      * <p>The range is expressed in arbitrary units that must be the same as the units used by
2457      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
2458      *
2459      * <p>Default implementation returns 0.</p>
2460      *
2461      * <p>If you want to support scroll bars, override
2462      * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
2463      * LayoutManager.</p>
2464      *
2465      * @return The horizontal extent of the scrollbar's thumb
2466      * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
2467      */
2468     @Override
computeHorizontalScrollExtent()2469     public int computeHorizontalScrollExtent() {
2470         if (mLayout == null) {
2471             return 0;
2472         }
2473         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
2474     }
2475 
2476     /**
2477      * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
2478      *
2479      * <p>The range is expressed in arbitrary units that must be the same as the units used by
2480      * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
2481      *
2482      * <p>Default implementation returns 0.</p>
2483      *
2484      * <p>If you want to support scroll bars, override
2485      * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
2486      * LayoutManager.</p>
2487      *
2488      * @return The total horizontal range represented by the horizontal scrollbar
2489      * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
2490      */
2491     @Override
computeHorizontalScrollRange()2492     public int computeHorizontalScrollRange() {
2493         if (mLayout == null) {
2494             return 0;
2495         }
2496         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
2497     }
2498 
2499     /**
2500      * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
2501      * This value is used to compute the position of the thumb within the scrollbar's track. </p>
2502      *
2503      * <p>The range is expressed in arbitrary units that must be the same as the units used by
2504      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
2505      *
2506      * <p>Default implementation returns 0.</p>
2507      *
2508      * <p>If you want to support scroll bars, override
2509      * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
2510      * LayoutManager.</p>
2511      *
2512      * @return The vertical offset of the scrollbar's thumb
2513      * @see RecyclerView.LayoutManager#computeVerticalScrollOffset
2514      * (RecyclerView.State)
2515      */
2516     @Override
computeVerticalScrollOffset()2517     public int computeVerticalScrollOffset() {
2518         if (mLayout == null) {
2519             return 0;
2520         }
2521         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
2522     }
2523 
2524     /**
2525      * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
2526      * This value is used to compute the length of the thumb within the scrollbar's track.</p>
2527      *
2528      * <p>The range is expressed in arbitrary units that must be the same as the units used by
2529      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
2530      *
2531      * <p>Default implementation returns 0.</p>
2532      *
2533      * <p>If you want to support scroll bars, override
2534      * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
2535      * LayoutManager.</p>
2536      *
2537      * @return The vertical extent of the scrollbar's thumb
2538      * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
2539      */
2540     @Override
computeVerticalScrollExtent()2541     public int computeVerticalScrollExtent() {
2542         if (mLayout == null) {
2543             return 0;
2544         }
2545         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
2546     }
2547 
2548     /**
2549      * <p>Compute the vertical range that the vertical scrollbar represents.</p>
2550      *
2551      * <p>The range is expressed in arbitrary units that must be the same as the units used by
2552      * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
2553      *
2554      * <p>Default implementation returns 0.</p>
2555      *
2556      * <p>If you want to support scroll bars, override
2557      * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
2558      * LayoutManager.</p>
2559      *
2560      * @return The total vertical range represented by the vertical scrollbar
2561      * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
2562      */
2563     @Override
computeVerticalScrollRange()2564     public int computeVerticalScrollRange() {
2565         if (mLayout == null) {
2566             return 0;
2567         }
2568         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
2569     }
2570 
2571     /**
2572      * This method should be called before any code that may trigger a child view to cause a call to
2573      * {@link RecyclerView#requestLayout()}.  Doing so enables {@link RecyclerView} to avoid
2574      * reacting to additional redundant calls to {@link #requestLayout()}.
2575      * <p>
2576      * A call to this method must always be accompanied by a call to
2577      * {@link #stopInterceptRequestLayout(boolean)} that follows the code that may trigger a
2578      * child View to cause a call to {@link RecyclerView#requestLayout()}.
2579      *
2580      * @see #stopInterceptRequestLayout(boolean)
2581      */
startInterceptRequestLayout()2582     void startInterceptRequestLayout() {
2583         mInterceptRequestLayoutDepth++;
2584         if (mInterceptRequestLayoutDepth == 1 && !mLayoutSuppressed) {
2585             mLayoutWasDefered = false;
2586         }
2587     }
2588 
2589     /**
2590      * This method should be called after any code that may trigger a child view to cause a call to
2591      * {@link RecyclerView#requestLayout()}.
2592      * <p>
2593      * A call to this method must always be accompanied by a call to
2594      * {@link #startInterceptRequestLayout()} that precedes the code that may trigger a child
2595      * View to cause a call to {@link RecyclerView#requestLayout()}.
2596      *
2597      * @see #startInterceptRequestLayout()
2598      */
stopInterceptRequestLayout(boolean performLayoutChildren)2599     void stopInterceptRequestLayout(boolean performLayoutChildren) {
2600         if (mInterceptRequestLayoutDepth < 1) {
2601             //noinspection PointlessBooleanExpression
2602             if (sDebugAssertionsEnabled) {
2603                 throw new IllegalStateException("stopInterceptRequestLayout was called more "
2604                         + "times than startInterceptRequestLayout."
2605                         + exceptionLabel());
2606             }
2607             mInterceptRequestLayoutDepth = 1;
2608         }
2609         if (!performLayoutChildren && !mLayoutSuppressed) {
2610             // Reset the layout request eaten counter.
2611             // This is necessary since eatRequest calls can be nested in which case the other
2612             // call will override the inner one.
2613             // for instance:
2614             // eat layout for process adapter updates
2615             //   eat layout for dispatchLayout
2616             //     a bunch of req layout calls arrive
2617 
2618             mLayoutWasDefered = false;
2619         }
2620         if (mInterceptRequestLayoutDepth == 1) {
2621             // when layout is frozen we should delay dispatchLayout()
2622             if (performLayoutChildren && mLayoutWasDefered && !mLayoutSuppressed
2623                     && mLayout != null && mAdapter != null) {
2624                 dispatchLayout();
2625             }
2626             if (!mLayoutSuppressed) {
2627                 mLayoutWasDefered = false;
2628             }
2629         }
2630         mInterceptRequestLayoutDepth--;
2631     }
2632 
2633     /**
2634      * Tells this RecyclerView to suppress all layout and scroll calls until layout
2635      * suppression is disabled with a later call to suppressLayout(false).
2636      * When layout suppression is disabled, a requestLayout() call is sent
2637      * if requestLayout() was attempted while layout was being suppressed.
2638      * <p>
2639      * In addition to the layout suppression {@link #smoothScrollBy(int, int)},
2640      * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
2641      * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
2642      * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
2643      * called.
2644      *
2645      * <p>
2646      * <code>suppressLayout(true)</code> does not prevent app from directly calling {@link
2647      * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
2648      *RecyclerView, State, int)}.
2649      * <p>
2650      * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
2651      * stop suppressing.
2652      * <p>
2653      * Note: Running ItemAnimator is not stopped automatically,  it's caller's
2654      * responsibility to call ItemAnimator.end().
2655      *
2656      * @param suppress true to suppress layout and scroll, false to re-enable.
2657      */
2658     @Override
suppressLayout(boolean suppress)2659     public final void suppressLayout(boolean suppress) {
2660         if (suppress != mLayoutSuppressed) {
2661             assertNotInLayoutOrScroll("Do not suppressLayout in layout or scroll");
2662             if (!suppress) {
2663                 mLayoutSuppressed = false;
2664                 if (mLayoutWasDefered && mLayout != null && mAdapter != null) {
2665                     requestLayout();
2666                 }
2667                 mLayoutWasDefered = false;
2668             } else {
2669                 final long now = SystemClock.uptimeMillis();
2670                 MotionEvent cancelEvent = MotionEvent.obtain(now, now,
2671                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2672                 onTouchEvent(cancelEvent);
2673                 mLayoutSuppressed = true;
2674                 mIgnoreMotionEventTillDown = true;
2675                 stopScroll();
2676             }
2677         }
2678     }
2679 
2680     /**
2681      * Returns whether layout and scroll calls on this container are currently being
2682      * suppressed, due to an earlier call to {@link #suppressLayout(boolean)}.
2683      *
2684      * @return true if layout and scroll are currently suppressed, false otherwise.
2685      */
2686     @Override
isLayoutSuppressed()2687     public final boolean isLayoutSuppressed() {
2688         return mLayoutSuppressed;
2689     }
2690 
2691     /**
2692      * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
2693      * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
2694      * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
2695      * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
2696      * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
2697      * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
2698      * called.
2699      *
2700      * <p>
2701      * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
2702      * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
2703      *RecyclerView, State, int)}.
2704      * <p>
2705      * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
2706      * stop frozen.
2707      * <p>
2708      * Note: Running ItemAnimator is not stopped automatically,  it's caller's
2709      * responsibility to call ItemAnimator.end().
2710      *
2711      * @param frozen true to freeze layout and scroll, false to re-enable.
2712      * @deprecated Use {@link #suppressLayout(boolean)}.
2713      */
2714     @Deprecated
setLayoutFrozen(boolean frozen)2715     public void setLayoutFrozen(boolean frozen) {
2716         suppressLayout(frozen);
2717     }
2718 
2719     /**
2720      * @return true if layout and scroll are frozen
2721      * @deprecated Use {@link #isLayoutSuppressed()}.
2722      */
2723     @Deprecated
isLayoutFrozen()2724     public boolean isLayoutFrozen() {
2725         return isLayoutSuppressed();
2726     }
2727 
2728     /**
2729      * @deprecated Use {@link #setItemAnimator(ItemAnimator)} ()}.
2730      */
2731     @Deprecated
2732     @Override
setLayoutTransition(LayoutTransition transition)2733     public void setLayoutTransition(LayoutTransition transition) {
2734         if (transition == null) {
2735             super.setLayoutTransition(null);
2736         } else {
2737             throw new IllegalArgumentException("Providing a LayoutTransition into RecyclerView is "
2738                     + "not supported. Please use setItemAnimator() instead for animating changes "
2739                     + "to the items in this RecyclerView");
2740         }
2741     }
2742 
2743     /**
2744      * Animate a scroll by the given amount of pixels along either axis.
2745      *
2746      * @param dx Pixels to scroll horizontally
2747      * @param dy Pixels to scroll vertically
2748      */
smoothScrollBy(@x int dx, @Px int dy)2749     public void smoothScrollBy(@Px int dx, @Px int dy) {
2750         smoothScrollBy(dx, dy, null);
2751     }
2752 
2753     /**
2754      * Animate a scroll by the given amount of pixels along either axis.
2755      *
2756      * @param dx           Pixels to scroll horizontally
2757      * @param dy           Pixels to scroll vertically
2758      * @param interpolator {@link Interpolator} to be used for scrolling. If it is
2759      *                     {@code null}, RecyclerView will use an internal default interpolator.
2760      */
smoothScrollBy(@x int dx, @Px int dy, @Nullable Interpolator interpolator)2761     public void smoothScrollBy(@Px int dx, @Px int dy, @Nullable Interpolator interpolator) {
2762         smoothScrollBy(dx, dy, interpolator, UNDEFINED_DURATION);
2763     }
2764 
2765     /**
2766      * Smooth scrolls the RecyclerView by a given distance.
2767      *
2768      * @param dx           x distance in pixels.
2769      * @param dy           y distance in pixels.
2770      * @param interpolator {@link Interpolator} to be used for scrolling. If it is {@code null},
2771      *                     RecyclerView will use an internal default interpolator.
2772      * @param duration     Duration of the animation in milliseconds. Set to
2773      *                     {@link #UNDEFINED_DURATION}
2774      *                     to have the duration be automatically calculated based on an internally
2775      *                     defined standard initial velocity. A duration less than 1 (that does not
2776      *                     equal UNDEFINED_DURATION), will result in a call to
2777      *                     {@link #scrollBy(int, int)}.
2778      */
smoothScrollBy(@x int dx, @Px int dy, @Nullable Interpolator interpolator, int duration)2779     public void smoothScrollBy(@Px int dx, @Px int dy, @Nullable Interpolator interpolator,
2780             int duration) {
2781         smoothScrollBy(dx, dy, interpolator, duration, false);
2782     }
2783 
2784     /**
2785      * Internal smooth scroll by implementation that currently has some tricky logic related to it's
2786      * parameters.
2787      * <ul>
2788      *   <li>For scrolling to occur, on either dimension, dx or dy must not be equal to 0 and the
2789      *   {@link LayoutManager} must support scrolling in a direction for which the value is not 0.
2790      *   <li>For smooth scrolling to occur, scrolling must occur and the duration must be equal to
2791      *   {@link #UNDEFINED_DURATION} or greater than 0.
2792      *   <li>For scrolling to occur with nested scrolling, smooth scrolling must occur and
2793      *   {@code withNestedScrolling} must be {@code true}.  This could be updated, but it would
2794      *   require that {@link #scrollBy(int, int)} be implemented such that it too can handle nested
2795      *   scrolling.
2796      * </ul>
2797      *
2798      * @param dx                  x distance in pixels.
2799      * @param dy                  y distance in pixels.
2800      * @param interpolator        {@link Interpolator} to be used for scrolling. If it is {@code
2801      *                            null},
2802      *                            RecyclerView will use an internal default interpolator.
2803      * @param duration            Duration of the animation in milliseconds. Set to
2804      *                            {@link #UNDEFINED_DURATION}
2805      *                            to have the duration be automatically calculated based on an
2806      *                            internally
2807      *                            defined standard initial velocity. A duration less than 1 (that
2808      *                            does not
2809      *                            equal UNDEFINED_DURATION), will result in a call to
2810      *                            {@link #scrollBy(int, int)}.
2811      * @param withNestedScrolling True to perform the smooth scroll with nested scrolling. If
2812      *                            {@code duration} is less than 0 and not equal to
2813      *                            {@link #UNDEFINED_DURATION}, smooth scrolling will not occur and
2814      *                            thus no nested scrolling will occur.
2815      */
2816     // Should be considered private. Not private to avoid synthetic accessor.
smoothScrollBy(@x int dx, @Px int dy, @Nullable Interpolator interpolator, int duration, boolean withNestedScrolling)2817     void smoothScrollBy(@Px int dx, @Px int dy, @Nullable Interpolator interpolator,
2818             int duration, boolean withNestedScrolling) {
2819         if (mLayout == null) {
2820             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
2821                     + "Call setLayoutManager with a non-null argument.");
2822             return;
2823         }
2824         if (mLayoutSuppressed) {
2825             return;
2826         }
2827         if (!mLayout.canScrollHorizontally()) {
2828             dx = 0;
2829         }
2830         if (!mLayout.canScrollVertically()) {
2831             dy = 0;
2832         }
2833         if (dx != 0 || dy != 0) {
2834             boolean durationSuggestsAnimation = duration == UNDEFINED_DURATION || duration > 0;
2835             if (durationSuggestsAnimation) {
2836                 if (withNestedScrolling) {
2837                     int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2838                     if (dx != 0) {
2839                         nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2840                     }
2841                     if (dy != 0) {
2842                         nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2843                     }
2844                     startNestedScroll(nestedScrollAxis, TYPE_NON_TOUCH);
2845                 }
2846                 mViewFlinger.smoothScrollBy(dx, dy, duration, interpolator);
2847             } else {
2848                 scrollBy(dx, dy);
2849             }
2850         }
2851     }
2852 
2853     /**
2854      * Begin a standard fling with an initial velocity along each axis in pixels per second.
2855      * If the velocity given is below the system-defined minimum this method will return false
2856      * and no fling will occur.
2857      *
2858      * @param velocityX Initial horizontal velocity in pixels per second
2859      * @param velocityY Initial vertical velocity in pixels per second
2860      * @return true if the fling was started, false if the velocity was too low to fling or
2861      * LayoutManager does not support scrolling in the axis fling is issued.
2862      * @see LayoutManager#canScrollVertically()
2863      * @see LayoutManager#canScrollHorizontally()
2864      */
fling(int velocityX, int velocityY)2865     public boolean fling(int velocityX, int velocityY) {
2866         return fling(velocityX, velocityY, mMinFlingVelocity, mMaxFlingVelocity);
2867     }
2868 
2869     /** Flings without checking fling velocity thresholds. */
flingNoThresholdCheck(int velocityX, int velocityY)2870     boolean flingNoThresholdCheck(int velocityX, int velocityY) {
2871         return fling(velocityX, velocityY, 0, Integer.MAX_VALUE);
2872     }
2873 
fling(int velocityX, int velocityY, int minFlingVelocity, int maxFlingVelocity)2874     private boolean fling(int velocityX, int velocityY, int minFlingVelocity,
2875             int maxFlingVelocity) {
2876         if (mLayout == null) {
2877             Log.e(TAG, "Cannot fling without a LayoutManager set. "
2878                     + "Call setLayoutManager with a non-null argument.");
2879             return false;
2880         }
2881         if (mLayoutSuppressed) {
2882             return false;
2883         }
2884 
2885         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
2886         final boolean canScrollVertical = mLayout.canScrollVertically();
2887 
2888         if (!canScrollHorizontal || Math.abs(velocityX) < minFlingVelocity) {
2889             velocityX = 0;
2890         }
2891         if (!canScrollVertical || Math.abs(velocityY) < minFlingVelocity) {
2892             velocityY = 0;
2893         }
2894         if (velocityX == 0 && velocityY == 0) {
2895             // If we don't have any velocity, return false
2896             return false;
2897         }
2898 
2899         // Flinging while the edge effect is active should affect the edge effect,
2900         // not scrolling.
2901         int flingX = 0;
2902         int flingY = 0;
2903         if (velocityX != 0) {
2904             if (mLeftGlow != null && EdgeEffectCompat.getDistance(mLeftGlow) != 0) {
2905                 if (shouldAbsorb(mLeftGlow, -velocityX, getWidth())) {
2906                     mLeftGlow.onAbsorb(-velocityX);
2907                 } else {
2908                     flingX = velocityX;
2909                 }
2910                 velocityX = 0;
2911             } else if (mRightGlow != null && EdgeEffectCompat.getDistance(mRightGlow) != 0) {
2912                 if (shouldAbsorb(mRightGlow, velocityX, getWidth())) {
2913                     mRightGlow.onAbsorb(velocityX);
2914                 } else {
2915                     flingX = velocityX;
2916                 }
2917                 velocityX = 0;
2918             }
2919         }
2920         if (velocityY != 0) {
2921             if (mTopGlow != null && EdgeEffectCompat.getDistance(mTopGlow) != 0) {
2922                 if (shouldAbsorb(mTopGlow, -velocityY, getHeight())) {
2923                     mTopGlow.onAbsorb(-velocityY);
2924                 } else {
2925                     flingY = velocityY;
2926                 }
2927                 velocityY = 0;
2928             } else if (mBottomGlow != null && EdgeEffectCompat.getDistance(mBottomGlow) != 0) {
2929                 if (shouldAbsorb(mBottomGlow, velocityY, getHeight())) {
2930                     mBottomGlow.onAbsorb(velocityY);
2931                 } else {
2932                     flingY = velocityY;
2933                 }
2934                 velocityY = 0;
2935             }
2936         }
2937         if (flingX != 0 || flingY != 0) {
2938             flingX = Math.max(-maxFlingVelocity, Math.min(flingX, maxFlingVelocity));
2939             flingY = Math.max(-maxFlingVelocity, Math.min(flingY, maxFlingVelocity));
2940             startNestedScrollForType(TYPE_NON_TOUCH);
2941             mViewFlinger.fling(flingX, flingY);
2942         }
2943         if (velocityX == 0 && velocityY == 0) {
2944             return flingX != 0 || flingY != 0;
2945         }
2946 
2947         if (!dispatchNestedPreFling(velocityX, velocityY)) {
2948             final boolean canScroll = canScrollHorizontal || canScrollVertical;
2949             dispatchNestedFling(velocityX, velocityY, canScroll);
2950 
2951             if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
2952                 return true;
2953             }
2954 
2955             if (canScroll) {
2956                 startNestedScrollForType(TYPE_NON_TOUCH);
2957                 velocityX = Math.max(-maxFlingVelocity, Math.min(velocityX, maxFlingVelocity));
2958                 velocityY = Math.max(-maxFlingVelocity, Math.min(velocityY, maxFlingVelocity));
2959                 mViewFlinger.fling(velocityX, velocityY);
2960                 return true;
2961             }
2962         }
2963         return false;
2964     }
2965 
startNestedScrollForType(int type)2966     private void startNestedScrollForType(int type) {
2967         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
2968         final boolean canScrollVertical = mLayout.canScrollVertically();
2969         int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2970         if (canScrollHorizontal) {
2971             nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2972         }
2973         if (canScrollVertical) {
2974             nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2975         }
2976         startNestedScroll(nestedScrollAxis, type);
2977     }
2978 
2979     /**
2980      * Returns true if edgeEffect should call onAbsorb() with veclocity or false if it should
2981      * animate with a fling. It will animate with a fling if the velocity will remove the
2982      * EdgeEffect through its normal operation.
2983      *
2984      * @param edgeEffect The EdgeEffect that might absorb the velocity.
2985      * @param velocity The velocity of the fling motion
2986      * @param size The width or height of the RecyclerView, depending on the edge that the
2987      *             EdgeEffect is on.
2988      * @return true if the velocity should be absorbed or false if it should be flung.
2989      */
shouldAbsorb(@onNull EdgeEffect edgeEffect, int velocity, int size)2990     private boolean shouldAbsorb(@NonNull EdgeEffect edgeEffect, int velocity, int size) {
2991         if (velocity > 0) {
2992             return true;
2993         }
2994         float distance = EdgeEffectCompat.getDistance(edgeEffect) * size;
2995 
2996         // This is flinging without the spring, so let's see if it will fling past the overscroll
2997         float flingDistance = getSplineFlingDistance(-velocity);
2998 
2999         return flingDistance < distance;
3000     }
3001 
3002     /**
3003      * If mLeftGlow or mRightGlow is currently active and the motion will remove some of the
3004      * stretch, this will consume any of unconsumedX that the glow can. If the motion would
3005      * increase the stretch, or the EdgeEffect isn't a stretch, then nothing will be consumed.
3006      *
3007      * @param unconsumedX The horizontal delta that might be consumed by the horizontal EdgeEffects
3008      * @return The remaining unconsumed delta after the edge effects have consumed.
3009      */
consumeFlingInHorizontalStretch(int unconsumedX)3010     int consumeFlingInHorizontalStretch(int unconsumedX) {
3011         return consumeFlingInStretch(unconsumedX, mLeftGlow, mRightGlow, getWidth());
3012     }
3013 
3014     /**
3015      * If mTopGlow or mBottomGlow is currently active and the motion will remove some of the
3016      * stretch, this will consume any of unconsumedY that the glow can. If the motion would
3017      * increase the stretch, or the EdgeEffect isn't a stretch, then nothing will be consumed.
3018      *
3019      * @param unconsumedY The vertical delta that might be consumed by the vertical EdgeEffects
3020      * @return The remaining unconsumed delta after the edge effects have consumed.
3021      */
consumeFlingInVerticalStretch(int unconsumedY)3022     int consumeFlingInVerticalStretch(int unconsumedY) {
3023         return consumeFlingInStretch(unconsumedY, mTopGlow, mBottomGlow, getHeight());
3024     }
3025 
3026     /**
3027      * Used by consumeFlingInHorizontalStretch() and consumeFlinInVerticalStretch() for
3028      * consuming deltas from EdgeEffects
3029      * @param unconsumed The unconsumed delta that the EdgeEffets may consume
3030      * @param startGlow The start (top or left) EdgeEffect
3031      * @param endGlow The end (bottom or right) EdgeEffect
3032      * @param size The width or height of the container, depending on whether this is for
3033      *             horizontal or vertical EdgeEffects
3034      * @return The unconsumed delta after the EdgeEffects have had an opportunity to consume.
3035      */
consumeFlingInStretch( int unconsumed, EdgeEffect startGlow, EdgeEffect endGlow, int size )3036     private int consumeFlingInStretch(
3037             int unconsumed,
3038             EdgeEffect startGlow,
3039             EdgeEffect endGlow,
3040             int size
3041     ) {
3042         if (unconsumed > 0 && startGlow != null && EdgeEffectCompat.getDistance(startGlow) != 0f) {
3043             float deltaDistance = -unconsumed * FLING_DESTRETCH_FACTOR / size;
3044             int consumed = Math.round(-size / FLING_DESTRETCH_FACTOR
3045                     * EdgeEffectCompat.onPullDistance(startGlow, deltaDistance, 0.5f));
3046             if (consumed != unconsumed) {
3047                 startGlow.finish();
3048             }
3049             return unconsumed - consumed;
3050         }
3051         if (unconsumed < 0 && endGlow != null && EdgeEffectCompat.getDistance(endGlow) != 0f) {
3052             float deltaDistance = unconsumed * FLING_DESTRETCH_FACTOR / size;
3053             int consumed = Math.round(size / FLING_DESTRETCH_FACTOR
3054                     * EdgeEffectCompat.onPullDistance(endGlow, deltaDistance, 0.5f));
3055             if (consumed != unconsumed) {
3056                 endGlow.finish();
3057             }
3058             return unconsumed - consumed;
3059         }
3060         return unconsumed;
3061     }
3062 
3063     /**
3064      * Stop any current scroll in progress, such as one started by
3065      * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
3066      */
stopScroll()3067     public void stopScroll() {
3068         setScrollState(SCROLL_STATE_IDLE);
3069         stopScrollersInternal();
3070     }
3071 
3072     /**
3073      * Similar to {@link #stopScroll()} but does not set the state.
3074      */
stopScrollersInternal()3075     private void stopScrollersInternal() {
3076         mViewFlinger.stop();
3077         if (mLayout != null) {
3078             mLayout.stopSmoothScroller();
3079         }
3080     }
3081 
3082     /**
3083      * Returns the minimum velocity to start a fling.
3084      *
3085      * @return The minimum velocity to start a fling
3086      */
getMinFlingVelocity()3087     public int getMinFlingVelocity() {
3088         return mMinFlingVelocity;
3089     }
3090 
3091 
3092     /**
3093      * Returns the maximum fling velocity used by this RecyclerView.
3094      *
3095      * @return The maximum fling velocity used by this RecyclerView.
3096      */
getMaxFlingVelocity()3097     public int getMaxFlingVelocity() {
3098         return mMaxFlingVelocity;
3099     }
3100 
3101     /**
3102      * Apply a pull to relevant overscroll glow effects
3103      */
pullGlows( MotionEvent ev, float x, int horizontalAxis, float overscrollX, float y, int verticalAxis, float overscrollY)3104     private void pullGlows(
3105             MotionEvent ev,
3106             float x,
3107             int horizontalAxis,
3108             float overscrollX,
3109             float y,
3110             int verticalAxis,
3111             float overscrollY) {
3112         boolean invalidate = false;
3113         if (overscrollX < 0) {
3114             ensureLeftGlow();
3115             EdgeEffectCompat.onPullDistance(mLeftGlow, -overscrollX / getWidth(),
3116                     1f - y / getHeight());
3117             if (ev != null) {
3118                 getScrollFeedbackProvider().onScrollLimit(
3119                         ev.getDeviceId(), ev.getSource(), horizontalAxis, /* isStart= */ true);
3120             }
3121             invalidate = true;
3122         } else if (overscrollX > 0) {
3123             ensureRightGlow();
3124             EdgeEffectCompat.onPullDistance(mRightGlow, overscrollX / getWidth(), y / getHeight());
3125             if (ev != null) {
3126                 getScrollFeedbackProvider().onScrollLimit(
3127                         ev.getDeviceId(), ev.getSource(), horizontalAxis, /* isStart= */ false);
3128             }
3129             invalidate = true;
3130         }
3131 
3132         if (overscrollY < 0) {
3133             ensureTopGlow();
3134             EdgeEffectCompat.onPullDistance(mTopGlow, -overscrollY / getHeight(), x / getWidth());
3135             if (ev != null) {
3136                 getScrollFeedbackProvider().onScrollLimit(
3137                         ev.getDeviceId(), ev.getSource(), verticalAxis, /* isStart= */ true);
3138             }
3139             invalidate = true;
3140         } else if (overscrollY > 0) {
3141             ensureBottomGlow();
3142             EdgeEffectCompat.onPullDistance(mBottomGlow, overscrollY / getHeight(),
3143                     1f - x / getWidth());
3144             if (ev != null) {
3145                 getScrollFeedbackProvider().onScrollLimit(
3146                         ev.getDeviceId(), ev.getSource(), verticalAxis, /* isStart= */ false);
3147             }
3148             invalidate = true;
3149         }
3150 
3151         if (invalidate || overscrollX != 0 || overscrollY != 0) {
3152             postInvalidateOnAnimation();
3153         }
3154     }
3155 
releaseGlows()3156     private void releaseGlows() {
3157         boolean needsInvalidate = false;
3158         if (mLeftGlow != null) {
3159             mLeftGlow.onRelease();
3160             needsInvalidate = mLeftGlow.isFinished();
3161         }
3162         if (mTopGlow != null) {
3163             mTopGlow.onRelease();
3164             needsInvalidate |= mTopGlow.isFinished();
3165         }
3166         if (mRightGlow != null) {
3167             mRightGlow.onRelease();
3168             needsInvalidate |= mRightGlow.isFinished();
3169         }
3170         if (mBottomGlow != null) {
3171             mBottomGlow.onRelease();
3172             needsInvalidate |= mBottomGlow.isFinished();
3173         }
3174         if (needsInvalidate) {
3175             postInvalidateOnAnimation();
3176         }
3177     }
3178 
considerReleasingGlowsOnScroll(int dx, int dy)3179     void considerReleasingGlowsOnScroll(int dx, int dy) {
3180         boolean needsInvalidate = false;
3181         if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
3182             mLeftGlow.onRelease();
3183             needsInvalidate = mLeftGlow.isFinished();
3184         }
3185         if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
3186             mRightGlow.onRelease();
3187             needsInvalidate |= mRightGlow.isFinished();
3188         }
3189         if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
3190             mTopGlow.onRelease();
3191             needsInvalidate |= mTopGlow.isFinished();
3192         }
3193         if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
3194             mBottomGlow.onRelease();
3195             needsInvalidate |= mBottomGlow.isFinished();
3196         }
3197         if (needsInvalidate) {
3198             postInvalidateOnAnimation();
3199         }
3200     }
3201 
absorbGlows(int velocityX, int velocityY)3202     void absorbGlows(int velocityX, int velocityY) {
3203         if (velocityX < 0) {
3204             ensureLeftGlow();
3205             if (mLeftGlow.isFinished()) {
3206                 mLeftGlow.onAbsorb(-velocityX);
3207             }
3208         } else if (velocityX > 0) {
3209             ensureRightGlow();
3210             if (mRightGlow.isFinished()) {
3211                 mRightGlow.onAbsorb(velocityX);
3212             }
3213         }
3214 
3215         if (velocityY < 0) {
3216             ensureTopGlow();
3217             if (mTopGlow.isFinished()) {
3218                 mTopGlow.onAbsorb(-velocityY);
3219             }
3220         } else if (velocityY > 0) {
3221             ensureBottomGlow();
3222             if (mBottomGlow.isFinished()) {
3223                 mBottomGlow.onAbsorb(velocityY);
3224             }
3225         }
3226 
3227         if (velocityX != 0 || velocityY != 0) {
3228             postInvalidateOnAnimation();
3229         }
3230     }
3231 
ensureLeftGlow()3232     void ensureLeftGlow() {
3233         if (mLeftGlow != null) {
3234             return;
3235         }
3236         mLeftGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_LEFT);
3237         if (mClipToPadding) {
3238             mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
3239                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
3240         } else {
3241             mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
3242         }
3243     }
3244 
ensureRightGlow()3245     void ensureRightGlow() {
3246         if (mRightGlow != null) {
3247             return;
3248         }
3249         mRightGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_RIGHT);
3250         if (mClipToPadding) {
3251             mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
3252                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
3253         } else {
3254             mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
3255         }
3256     }
3257 
ensureTopGlow()3258     void ensureTopGlow() {
3259         if (mTopGlow != null) {
3260             return;
3261         }
3262         mTopGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_TOP);
3263         if (mClipToPadding) {
3264             mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
3265                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
3266         } else {
3267             mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
3268         }
3269 
3270     }
3271 
ensureBottomGlow()3272     void ensureBottomGlow() {
3273         if (mBottomGlow != null) {
3274             return;
3275         }
3276         mBottomGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_BOTTOM);
3277         if (mClipToPadding) {
3278             mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
3279                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
3280         } else {
3281             mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
3282         }
3283     }
3284 
invalidateGlows()3285     void invalidateGlows() {
3286         mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
3287     }
3288 
3289     /**
3290      * Set a {@link EdgeEffectFactory} for this {@link RecyclerView}.
3291      * <p>
3292      * When a new {@link EdgeEffectFactory} is set, any existing over-scroll effects are cleared
3293      * and new effects are created as needed using
3294      * {@link EdgeEffectFactory#createEdgeEffect(RecyclerView, int)}
3295      *
3296      * @param edgeEffectFactory The {@link EdgeEffectFactory} instance.
3297      */
setEdgeEffectFactory(@onNull EdgeEffectFactory edgeEffectFactory)3298     public void setEdgeEffectFactory(@NonNull EdgeEffectFactory edgeEffectFactory) {
3299         Preconditions.checkNotNull(edgeEffectFactory);
3300         mEdgeEffectFactory = edgeEffectFactory;
3301         invalidateGlows();
3302     }
3303 
3304     /**
3305      * Retrieves the previously set {@link EdgeEffectFactory} or the default factory if nothing
3306      * was set.
3307      *
3308      * @return The previously set {@link EdgeEffectFactory}
3309      * @see #setEdgeEffectFactory(EdgeEffectFactory)
3310      */
getEdgeEffectFactory()3311     public @NonNull EdgeEffectFactory getEdgeEffectFactory() {
3312         return mEdgeEffectFactory;
3313     }
3314 
3315     /**
3316      * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
3317      * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
3318      * that differs from other ViewGroups.
3319      * <p>
3320      * It first does a focus search within the RecyclerView. If this search finds a View that is in
3321      * the focus direction with respect to the currently focused View, RecyclerView returns that
3322      * child as the next focus target. When it cannot find such child, it calls
3323      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
3324      * in the focus search direction. If LayoutManager adds a View that matches the
3325      * focus search criteria, it will be returned as the focus search result. Otherwise,
3326      * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
3327      * <p>
3328      * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
3329      * is not in the focus direction is still valid focus target which may not be the desired
3330      * behavior if the Adapter has more children in the focus direction. To handle this case,
3331      * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
3332      * focus search in that direction. If there are no Views to gain focus, it will call
3333      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
3334      * focus search with the original (relative) direction. This allows RecyclerView to provide
3335      * better candidates to the focus search while still allowing the view system to take focus from
3336      * the RecyclerView and give it to a more suitable child if such child exists.
3337      *
3338      * @param focused   The view that currently has focus
3339      * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
3340      *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
3341      *                  {@link View#FOCUS_FORWARD},
3342      *                  {@link View#FOCUS_BACKWARD} or 0 for not applicable.
3343      * @return A new View that can be the next focus after the focused View
3344      */
3345     @Override
focusSearch(View focused, int direction)3346     public View focusSearch(View focused, int direction) {
3347         View result = mLayout.onInterceptFocusSearch(focused, direction);
3348         if (result != null) {
3349             return result;
3350         }
3351         final boolean canRunFocusFailure = mAdapter != null && mLayout != null
3352                 && !isComputingLayout() && !mLayoutSuppressed;
3353 
3354         final FocusFinder ff = FocusFinder.getInstance();
3355         if (canRunFocusFailure
3356                 && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
3357             // convert direction to absolute direction and see if we have a view there and if not
3358             // tell LayoutManager to add if it can.
3359             boolean needsFocusFailureLayout = false;
3360             if (mLayout.canScrollVertically()) {
3361                 final int absDir =
3362                         direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
3363                 final View found = ff.findNextFocus(this, focused, absDir);
3364                 needsFocusFailureLayout = found == null;
3365             }
3366             if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
3367                 boolean rtl = mLayout.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
3368                 final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
3369                         ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
3370                 final View found = ff.findNextFocus(this, focused, absDir);
3371                 needsFocusFailureLayout = found == null;
3372             }
3373             if (needsFocusFailureLayout) {
3374                 consumePendingUpdateOperations();
3375                 final View focusedItemView = findContainingItemView(focused);
3376                 if (focusedItemView == null) {
3377                     // panic, focused view is not a child anymore, cannot call super.
3378                     return null;
3379                 }
3380                 startInterceptRequestLayout();
3381                 mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
3382                 stopInterceptRequestLayout(false);
3383             }
3384             result = ff.findNextFocus(this, focused, direction);
3385         } else {
3386             result = ff.findNextFocus(this, focused, direction);
3387             if (result == null && canRunFocusFailure) {
3388                 consumePendingUpdateOperations();
3389                 final View focusedItemView = findContainingItemView(focused);
3390                 if (focusedItemView == null) {
3391                     // panic, focused view is not a child anymore, cannot call super.
3392                     return null;
3393                 }
3394                 startInterceptRequestLayout();
3395                 result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
3396                 stopInterceptRequestLayout(false);
3397             }
3398         }
3399         if (result != null && !result.hasFocusable()) {
3400             if (getFocusedChild() == null) {
3401                 // Scrolling to this unfocusable view is not meaningful since there is no currently
3402                 // focused view which RV needs to keep visible.
3403                 return super.focusSearch(focused, direction);
3404             }
3405             // If the next view returned by onFocusSearchFailed in layout manager has no focusable
3406             // views, we still scroll to that view in order to make it visible on the screen.
3407             // If it's focusable, framework already calls RV's requestChildFocus which handles
3408             // bringing this newly focused item onto the screen.
3409             requestChildOnScreen(result, null);
3410             return focused;
3411         }
3412         return isPreferredNextFocus(focused, result, direction)
3413                 ? result : super.focusSearch(focused, direction);
3414     }
3415 
3416     /**
3417      * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
3418      * assign it as the next focus View instead of letting view hierarchy decide.
3419      * A good candidate means a View that is aligned in the focus direction wrt the focused View
3420      * and is not the RecyclerView itself.
3421      * When this method returns false, RecyclerView will let the parent make the decision so the
3422      * same View may still get the focus as a result of that search.
3423      */
isPreferredNextFocus(View focused, View next, int direction)3424     private boolean isPreferredNextFocus(View focused, View next, int direction) {
3425         if (next == null || next == this || next == focused) {
3426             return false;
3427         }
3428         // panic, result view is not a child anymore, maybe workaround b/37864393
3429         if (findContainingItemView(next) == null) {
3430             return false;
3431         }
3432         if (focused == null) {
3433             return true;
3434         }
3435         // panic, focused view is not a child anymore, maybe workaround b/37864393
3436         if (findContainingItemView(focused) == null) {
3437             return true;
3438         }
3439 
3440         mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
3441         mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
3442         offsetDescendantRectToMyCoords(focused, mTempRect);
3443         offsetDescendantRectToMyCoords(next, mTempRect2);
3444         final int rtl = mLayout.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? -1 : 1;
3445         int rightness = 0;
3446         if ((mTempRect.left < mTempRect2.left
3447                 || mTempRect.right <= mTempRect2.left)
3448                 && mTempRect.right < mTempRect2.right) {
3449             rightness = 1;
3450         } else if ((mTempRect.right > mTempRect2.right
3451                 || mTempRect.left >= mTempRect2.right)
3452                 && mTempRect.left > mTempRect2.left) {
3453             rightness = -1;
3454         }
3455         int downness = 0;
3456         if ((mTempRect.top < mTempRect2.top
3457                 || mTempRect.bottom <= mTempRect2.top)
3458                 && mTempRect.bottom < mTempRect2.bottom) {
3459             downness = 1;
3460         } else if ((mTempRect.bottom > mTempRect2.bottom
3461                 || mTempRect.top >= mTempRect2.bottom)
3462                 && mTempRect.top > mTempRect2.top) {
3463             downness = -1;
3464         }
3465         switch (direction) {
3466             case View.FOCUS_LEFT:
3467                 return rightness < 0;
3468             case View.FOCUS_RIGHT:
3469                 return rightness > 0;
3470             case View.FOCUS_UP:
3471                 return downness < 0;
3472             case View.FOCUS_DOWN:
3473                 return downness > 0;
3474             case View.FOCUS_FORWARD:
3475                 return downness > 0 || (downness == 0 && rightness * rtl > 0);
3476             case View.FOCUS_BACKWARD:
3477                 return downness < 0 || (downness == 0 && rightness * rtl < 0);
3478         }
3479         throw new IllegalArgumentException("Invalid direction: " + direction + exceptionLabel());
3480     }
3481 
3482     @Override
requestChildFocus(View child, View focused)3483     public void requestChildFocus(View child, View focused) {
3484         if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
3485             requestChildOnScreen(child, focused);
3486         }
3487         super.requestChildFocus(child, focused);
3488     }
3489 
3490     /**
3491      * Requests that the given child of the RecyclerView be positioned onto the screen. This method
3492      * can be called for both unfocusable and focusable child views. For unfocusable child views,
3493      * the {@param focused} parameter passed is null, whereas for a focusable child, this parameter
3494      * indicates the actual descendant view within this child view that holds the focus.
3495      *
3496      * @param child   The child view of this RecyclerView that wants to come onto the screen.
3497      * @param focused The descendant view that actually has the focus if child is focusable, null
3498      *                otherwise.
3499      */
requestChildOnScreen(@onNull View child, @Nullable View focused)3500     private void requestChildOnScreen(@NonNull View child, @Nullable View focused) {
3501         View rectView = (focused != null) ? focused : child;
3502         mTempRect.set(0, 0, rectView.getWidth(), rectView.getHeight());
3503 
3504         // get item decor offsets w/o refreshing. If they are invalid, there will be another
3505         // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
3506         // View in viewport.
3507         final ViewGroup.LayoutParams focusedLayoutParams = rectView.getLayoutParams();
3508         if (focusedLayoutParams instanceof LayoutParams) {
3509             // if focused child has item decors, use them. Otherwise, ignore.
3510             final LayoutParams lp = (LayoutParams) focusedLayoutParams;
3511             if (!lp.mInsetsDirty) {
3512                 final Rect insets = lp.mDecorInsets;
3513                 mTempRect.left -= insets.left;
3514                 mTempRect.right += insets.right;
3515                 mTempRect.top -= insets.top;
3516                 mTempRect.bottom += insets.bottom;
3517             }
3518         }
3519 
3520         if (focused != null) {
3521             offsetDescendantRectToMyCoords(focused, mTempRect);
3522             offsetRectIntoDescendantCoords(child, mTempRect);
3523         }
3524         mLayout.requestChildRectangleOnScreen(this, child, mTempRect, !mFirstLayoutComplete,
3525                 (focused == null));
3526     }
3527 
3528     @Override
requestChildRectangleOnScreen(View child, Rect rect, boolean immediate)3529     public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
3530         return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
3531     }
3532 
3533     @Override
addFocusables(ArrayList<View> views, int direction, int focusableMode)3534     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
3535         if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
3536             super.addFocusables(views, direction, focusableMode);
3537         }
3538     }
3539 
3540     @Override
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)3541     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
3542         if (isComputingLayout()) {
3543             // if we are in the middle of a layout calculation, don't let any child take focus.
3544             // RV will handle it after layout calculation is finished.
3545             return false;
3546         }
3547         return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
3548     }
3549 
3550     @Override
onAttachedToWindow()3551     protected void onAttachedToWindow() {
3552         super.onAttachedToWindow();
3553         mLayoutOrScrollCounter = 0;
3554         mIsAttached = true;
3555         mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
3556 
3557         mRecycler.onAttachedToWindow();
3558 
3559         if (mLayout != null) {
3560             mLayout.dispatchAttachedToWindow(this);
3561         }
3562         mPostedAnimatorRunner = false;
3563 
3564         if (ALLOW_THREAD_GAP_WORK) {
3565             // Register with gap worker
3566             mGapWorker = GapWorker.sGapWorker.get();
3567             if (mGapWorker == null) {
3568                 mGapWorker = new GapWorker();
3569 
3570                 // break 60 fps assumption if data from display appears valid
3571                 // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
3572                 Display display = ViewCompat.getDisplay(this);
3573                 float refreshRate = 60.0f;
3574                 if (!isInEditMode() && display != null) {
3575                     float displayRefreshRate = display.getRefreshRate();
3576                     if (displayRefreshRate >= 30.0f) {
3577                         refreshRate = displayRefreshRate;
3578                     }
3579                 }
3580                 mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
3581                 GapWorker.sGapWorker.set(mGapWorker);
3582             }
3583             mGapWorker.add(this);
3584         }
3585     }
3586 
3587     @Override
onDetachedFromWindow()3588     protected void onDetachedFromWindow() {
3589         super.onDetachedFromWindow();
3590         if (mItemAnimator != null) {
3591             mItemAnimator.endAnimations();
3592         }
3593         stopScroll();
3594         mIsAttached = false;
3595         if (mLayout != null) {
3596             mLayout.dispatchDetachedFromWindow(this, mRecycler);
3597         }
3598         mPendingAccessibilityImportanceChange.clear();
3599         removeCallbacks(mItemAnimatorRunner);
3600         mViewInfoStore.onDetach();
3601         mRecycler.onDetachedFromWindow();
3602 
3603         PoolingContainer.callPoolingContainerOnReleaseForChildren(this);
3604 
3605         if (ALLOW_THREAD_GAP_WORK && mGapWorker != null) {
3606             // Unregister with gap worker
3607             mGapWorker.remove(this);
3608             mGapWorker = null;
3609         }
3610     }
3611 
3612     /**
3613      * Returns true if RecyclerView is attached to window.
3614      */
3615     @Override
isAttachedToWindow()3616     public boolean isAttachedToWindow() {
3617         return mIsAttached;
3618     }
3619 
3620     /**
3621      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
3622      * {@link IllegalStateException} if it <b>is not</b>.
3623      *
3624      * @param message The message for the exception. Can be null.
3625      * @see #assertNotInLayoutOrScroll(String)
3626      */
assertInLayoutOrScroll(String message)3627     void assertInLayoutOrScroll(String message) {
3628         if (!isComputingLayout()) {
3629             if (message == null) {
3630                 throw new IllegalStateException("Cannot call this method unless RecyclerView is "
3631                         + "computing a layout or scrolling" + exceptionLabel());
3632             }
3633             throw new IllegalStateException(message + exceptionLabel());
3634 
3635         }
3636     }
3637 
3638     /**
3639      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
3640      * {@link IllegalStateException} if it <b>is</b>.
3641      *
3642      * @param message The message for the exception. Can be null.
3643      * @see #assertInLayoutOrScroll(String)
3644      */
assertNotInLayoutOrScroll(String message)3645     void assertNotInLayoutOrScroll(String message) {
3646         if (isComputingLayout()) {
3647             if (message == null) {
3648                 throw new IllegalStateException("Cannot call this method while RecyclerView is "
3649                         + "computing a layout or scrolling" + exceptionLabel());
3650             }
3651             throw new IllegalStateException(message);
3652         }
3653         if (mDispatchScrollCounter > 0) {
3654             Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might"
3655                             + "be run during a measure & layout pass where you cannot change the"
3656                             + "RecyclerView data. Any method call that might change the structure"
3657                             + "of the RecyclerView or the adapter contents should be postponed to"
3658                             + "the next frame.",
3659                     new IllegalStateException("" + exceptionLabel()));
3660         }
3661     }
3662 
3663     /**
3664      * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
3665      * to child views or this view's standard scrolling behavior.
3666      *
3667      * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
3668      * returns true from
3669      * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
3670      * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
3671      * for each incoming MotionEvent until the end of the gesture.</p>
3672      *
3673      * @param listener Listener to add
3674      * @see SimpleOnItemTouchListener
3675      */
addOnItemTouchListener(@onNull OnItemTouchListener listener)3676     public void addOnItemTouchListener(@NonNull OnItemTouchListener listener) {
3677         mOnItemTouchListeners.add(listener);
3678     }
3679 
3680     /**
3681      * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
3682      *
3683      * @param listener Listener to remove
3684      */
removeOnItemTouchListener(@onNull OnItemTouchListener listener)3685     public void removeOnItemTouchListener(@NonNull OnItemTouchListener listener) {
3686         mOnItemTouchListeners.remove(listener);
3687         if (mInterceptingOnItemTouchListener == listener) {
3688             mInterceptingOnItemTouchListener = null;
3689         }
3690     }
3691 
3692     /**
3693      * Dispatches the motion event to the intercepting OnItemTouchListener or provides opportunity
3694      * for OnItemTouchListeners to intercept.
3695      *
3696      * @param e The MotionEvent
3697      * @return True if handled by an intercepting OnItemTouchListener.
3698      */
dispatchToOnItemTouchListeners(MotionEvent e)3699     private boolean dispatchToOnItemTouchListeners(MotionEvent e) {
3700 
3701         // OnItemTouchListeners should receive calls to their methods in the same pattern that
3702         // ViewGroups do. That pattern is a bit confusing, which in turn makes the below code a
3703         // bit confusing.  Here are rules for the pattern:
3704         //
3705         // 1. A single MotionEvent should not be passed to either OnInterceptTouchEvent or
3706         // OnTouchEvent twice.
3707         // 2. ACTION_DOWN MotionEvents may be passed to both onInterceptTouchEvent and
3708         // onTouchEvent.
3709         // 3. All other MotionEvents should be passed to either onInterceptTouchEvent or
3710         // onTouchEvent, not both.
3711 
3712         // Side Note: We don't currently perfectly mimic how MotionEvents work in the view system.
3713         // If we were to do so, for every MotionEvent, any OnItemTouchListener that is before the
3714         // intercepting OnItemTouchEvent should still have a chance to intercept, and if it does,
3715         // the previously intercepting OnItemTouchEvent should get an ACTION_CANCEL event.
3716 
3717         if (mInterceptingOnItemTouchListener == null) {
3718             if (e.getAction() == MotionEvent.ACTION_DOWN) {
3719                 return false;
3720             }
3721             return findInterceptingOnItemTouchListener(e);
3722         } else {
3723             mInterceptingOnItemTouchListener.onTouchEvent(this, e);
3724             final int action = e.getAction();
3725             if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
3726                 mInterceptingOnItemTouchListener = null;
3727             }
3728             return true;
3729         }
3730     }
3731 
3732     /**
3733      * Looks for an OnItemTouchListener that wants to intercept.
3734      *
3735      * <p>Calls {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} on each
3736      * of the registered {@link OnItemTouchListener}s, passing in the
3737      * MotionEvent. If one returns true and the action is not ACTION_CANCEL, saves the intercepting
3738      * OnItemTouchListener to be called for future {@link RecyclerView#onTouchEvent(MotionEvent)}
3739      * and immediately returns true. If none want to intercept or the action is ACTION_CANCEL,
3740      * returns false.
3741      *
3742      * @param e The MotionEvent
3743      * @return true if an OnItemTouchListener is saved as intercepting.
3744      */
findInterceptingOnItemTouchListener(MotionEvent e)3745     private boolean findInterceptingOnItemTouchListener(MotionEvent e) {
3746         int action = e.getAction();
3747         final int listenerCount = mOnItemTouchListeners.size();
3748         for (int i = 0; i < listenerCount; i++) {
3749             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
3750             if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
3751                 mInterceptingOnItemTouchListener = listener;
3752                 return true;
3753             }
3754         }
3755         return false;
3756     }
3757 
3758     @Override
onInterceptTouchEvent(MotionEvent e)3759     public boolean onInterceptTouchEvent(MotionEvent e) {
3760         if (mLayoutSuppressed) {
3761             // When layout is suppressed,  RV does not intercept the motion event.
3762             // A child view e.g. a button may still get the click.
3763             return false;
3764         }
3765 
3766         // Clear the active onInterceptTouchListener.  None should be set at this time, and if one
3767         // is, it's because some other code didn't follow the standard contract.
3768         mInterceptingOnItemTouchListener = null;
3769         if (findInterceptingOnItemTouchListener(e)) {
3770             cancelScroll();
3771             MotionEvent cancelEvent = MotionEvent.obtain(e);
3772             cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
3773             final int listenerCount = mOnItemTouchListeners.size();
3774             for (int i = 0; i < listenerCount; i++) {
3775                 final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
3776                 if (listener == null || listener == mInterceptingOnItemTouchListener) {
3777                     continue;
3778                 } else {
3779                     listener.onInterceptTouchEvent(this, cancelEvent);
3780                 }
3781             }
3782             return true;
3783         }
3784 
3785         if (mLayout == null) {
3786             return false;
3787         }
3788 
3789         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
3790         final boolean canScrollVertically = mLayout.canScrollVertically();
3791 
3792         if (mVelocityTracker == null) {
3793             mVelocityTracker = VelocityTracker.obtain();
3794         }
3795         mVelocityTracker.addMovement(e);
3796 
3797         final int action = e.getActionMasked();
3798         final int actionIndex = e.getActionIndex();
3799 
3800         switch (action) {
3801             case MotionEvent.ACTION_DOWN:
3802                 if (mIgnoreMotionEventTillDown) {
3803                     mIgnoreMotionEventTillDown = false;
3804                 }
3805                 mScrollPointerId = e.getPointerId(0);
3806                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
3807                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
3808 
3809                 if (stopGlowAnimations(e) || mScrollState == SCROLL_STATE_SETTLING) {
3810                     getParent().requestDisallowInterceptTouchEvent(true);
3811                     setScrollState(SCROLL_STATE_DRAGGING);
3812                     stopNestedScroll(TYPE_NON_TOUCH);
3813                 }
3814 
3815                 // Clear the nested offsets
3816                 mNestedOffsets[0] = mNestedOffsets[1] = 0;
3817 
3818                 startNestedScrollForType(TYPE_TOUCH);
3819                 break;
3820 
3821             case MotionEvent.ACTION_POINTER_DOWN:
3822                 mScrollPointerId = e.getPointerId(actionIndex);
3823                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
3824                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
3825                 break;
3826 
3827             case MotionEvent.ACTION_MOVE: {
3828                 final int index = e.findPointerIndex(mScrollPointerId);
3829                 if (index < 0) {
3830                     Log.e(TAG, "Error processing scroll; pointer index for id "
3831                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
3832                     return false;
3833                 }
3834 
3835                 final int x = (int) (e.getX(index) + 0.5f);
3836                 final int y = (int) (e.getY(index) + 0.5f);
3837                 if (mScrollState != SCROLL_STATE_DRAGGING) {
3838                     final int dx = x - mInitialTouchX;
3839                     final int dy = y - mInitialTouchY;
3840                     boolean startScroll = false;
3841                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
3842                         mLastTouchX = x;
3843                         startScroll = true;
3844                     }
3845                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
3846                         mLastTouchY = y;
3847                         startScroll = true;
3848                     }
3849                     if (startScroll) {
3850                         setScrollState(SCROLL_STATE_DRAGGING);
3851                     }
3852                 }
3853             }
3854             break;
3855 
3856             case MotionEvent.ACTION_POINTER_UP: {
3857                 onPointerUp(e);
3858             }
3859             break;
3860 
3861             case MotionEvent.ACTION_UP: {
3862                 mVelocityTracker.clear();
3863                 stopNestedScroll(TYPE_TOUCH);
3864             }
3865             break;
3866 
3867             case MotionEvent.ACTION_CANCEL: {
3868                 cancelScroll();
3869             }
3870         }
3871         return mScrollState == SCROLL_STATE_DRAGGING;
3872     }
3873 
3874     /**
3875      * This stops any edge glow animation that is currently running by applying a
3876      * 0 length pull at the displacement given by the provided MotionEvent. On pre-S devices,
3877      * this method does nothing, allowing any animating edge effect to continue animating and
3878      * returning <code>false</code> always.
3879      *
3880      * @param e The motion event to use to indicate the finger position for the displacement of
3881      *          the current pull.
3882      * @return <code>true</code> if any edge effect had an existing effect to be drawn ond the
3883      * animation was stopped or <code>false</code> if no edge effect had a value to display.
3884      */
stopGlowAnimations(MotionEvent e)3885     private boolean stopGlowAnimations(MotionEvent e) {
3886         boolean stopped = false;
3887         if (mLeftGlow != null && EdgeEffectCompat.getDistance(mLeftGlow) != 0
3888                 && !canScrollHorizontally(-1)) {
3889             EdgeEffectCompat.onPullDistance(mLeftGlow, 0, 1 - (e.getY() / getHeight()));
3890             stopped = true;
3891         }
3892         if (mRightGlow != null && EdgeEffectCompat.getDistance(mRightGlow) != 0
3893                 && !canScrollHorizontally(1)) {
3894             EdgeEffectCompat.onPullDistance(mRightGlow, 0, e.getY() / getHeight());
3895             stopped = true;
3896         }
3897         if (mTopGlow != null && EdgeEffectCompat.getDistance(mTopGlow) != 0
3898                 && !canScrollVertically(-1)) {
3899             EdgeEffectCompat.onPullDistance(mTopGlow, 0, e.getX() / getWidth());
3900             stopped = true;
3901         }
3902         if (mBottomGlow != null && EdgeEffectCompat.getDistance(mBottomGlow) != 0
3903                 && !canScrollVertically(1)) {
3904             EdgeEffectCompat.onPullDistance(mBottomGlow, 0, 1 - e.getX() / getWidth());
3905             stopped = true;
3906         }
3907         return stopped;
3908     }
3909 
3910     @Override
requestDisallowInterceptTouchEvent(boolean disallowIntercept)3911     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
3912         final int listenerCount = mOnItemTouchListeners.size();
3913         for (int i = 0; i < listenerCount; i++) {
3914             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
3915             listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
3916         }
3917         super.requestDisallowInterceptTouchEvent(disallowIntercept);
3918     }
3919 
3920     @Override
onTouchEvent(MotionEvent e)3921     public boolean onTouchEvent(MotionEvent e) {
3922         if (mLayoutSuppressed || mIgnoreMotionEventTillDown) {
3923             return false;
3924         }
3925         if (dispatchToOnItemTouchListeners(e)) {
3926             cancelScroll();
3927             return true;
3928         }
3929 
3930         if (mLayout == null) {
3931             return false;
3932         }
3933 
3934         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
3935         final boolean canScrollVertically = mLayout.canScrollVertically();
3936 
3937         if (mVelocityTracker == null) {
3938             mVelocityTracker = VelocityTracker.obtain();
3939         }
3940         boolean eventAddedToVelocityTracker = false;
3941 
3942         final int action = e.getActionMasked();
3943         final int actionIndex = e.getActionIndex();
3944 
3945         if (action == MotionEvent.ACTION_DOWN) {
3946             mNestedOffsets[0] = mNestedOffsets[1] = 0;
3947         }
3948         final MotionEvent vtev = MotionEvent.obtain(e);
3949         vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
3950 
3951         switch (action) {
3952             case MotionEvent.ACTION_DOWN: {
3953                 mScrollPointerId = e.getPointerId(0);
3954                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
3955                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
3956 
3957                 startNestedScrollForType(TYPE_TOUCH);
3958             }
3959             break;
3960 
3961             case MotionEvent.ACTION_POINTER_DOWN: {
3962                 mScrollPointerId = e.getPointerId(actionIndex);
3963                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
3964                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
3965             }
3966             break;
3967 
3968             case MotionEvent.ACTION_MOVE: {
3969                 final int index = e.findPointerIndex(mScrollPointerId);
3970                 if (index < 0) {
3971                     Log.e(TAG, "Error processing scroll; pointer index for id "
3972                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
3973                     return false;
3974                 }
3975 
3976                 final int x = (int) (e.getX(index) + 0.5f);
3977                 final int y = (int) (e.getY(index) + 0.5f);
3978                 int dx = mLastTouchX - x;
3979                 int dy = mLastTouchY - y;
3980 
3981                 if (mScrollState != SCROLL_STATE_DRAGGING) {
3982                     boolean startScroll = false;
3983                     if (canScrollHorizontally) {
3984                         if (dx > 0) {
3985                             dx = Math.max(0, dx - mTouchSlop);
3986                         } else {
3987                             dx = Math.min(0, dx + mTouchSlop);
3988                         }
3989                         if (dx != 0) {
3990                             startScroll = true;
3991                         }
3992                     }
3993                     if (canScrollVertically) {
3994                         if (dy > 0) {
3995                             dy = Math.max(0, dy - mTouchSlop);
3996                         } else {
3997                             dy = Math.min(0, dy + mTouchSlop);
3998                         }
3999                         if (dy != 0) {
4000                             startScroll = true;
4001                         }
4002                     }
4003                     if (startScroll) {
4004                         setScrollState(SCROLL_STATE_DRAGGING);
4005                     }
4006                 }
4007 
4008                 if (mScrollState == SCROLL_STATE_DRAGGING) {
4009                     mReusableIntPair[0] = 0;
4010                     mReusableIntPair[1] = 0;
4011                     dx -= releaseHorizontalGlow(dx, e.getY());
4012                     dy -= releaseVerticalGlow(dy, e.getX());
4013 
4014                     if (dispatchNestedPreScroll(
4015                             canScrollHorizontally ? dx : 0,
4016                             canScrollVertically ? dy : 0,
4017                             mReusableIntPair, mScrollOffset, TYPE_TOUCH
4018                     )) {
4019                         dx -= mReusableIntPair[0];
4020                         dy -= mReusableIntPair[1];
4021                         // Updated the nested offsets
4022                         mNestedOffsets[0] += mScrollOffset[0];
4023                         mNestedOffsets[1] += mScrollOffset[1];
4024                         // Scroll has initiated, prevent parents from intercepting
4025                         getParent().requestDisallowInterceptTouchEvent(true);
4026                     }
4027 
4028                     mLastTouchX = x - mScrollOffset[0];
4029                     mLastTouchY = y - mScrollOffset[1];
4030 
4031                     if (scrollByInternal(
4032                             canScrollHorizontally ? dx : 0,
4033                             canScrollVertically ? dy : 0,
4034                             MotionEvent.AXIS_X,
4035                             MotionEvent.AXIS_Y,
4036                             e, TYPE_TOUCH)) {
4037                         getParent().requestDisallowInterceptTouchEvent(true);
4038                     }
4039                     if (mGapWorker != null && (dx != 0 || dy != 0)) {
4040                         mGapWorker.postFromTraversal(this, dx, dy);
4041                     }
4042                 }
4043             }
4044             break;
4045 
4046             case MotionEvent.ACTION_POINTER_UP: {
4047                 onPointerUp(e);
4048             }
4049             break;
4050 
4051             case MotionEvent.ACTION_UP: {
4052                 mVelocityTracker.addMovement(vtev);
4053                 eventAddedToVelocityTracker = true;
4054                 mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
4055                 final float xvel = canScrollHorizontally
4056                         ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
4057                 final float yvel = canScrollVertically
4058                         ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
4059                 if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
4060                     setScrollState(SCROLL_STATE_IDLE);
4061                 }
4062                 resetScroll();
4063             }
4064             break;
4065 
4066             case MotionEvent.ACTION_CANCEL: {
4067                 cancelScroll();
4068             }
4069             break;
4070         }
4071 
4072         if (!eventAddedToVelocityTracker) {
4073             mVelocityTracker.addMovement(vtev);
4074         }
4075         vtev.recycle();
4076 
4077         return true;
4078     }
4079 
resetScroll()4080     private void resetScroll() {
4081         if (mVelocityTracker != null) {
4082             mVelocityTracker.clear();
4083         }
4084         stopNestedScroll(TYPE_TOUCH);
4085         releaseGlows();
4086     }
4087 
cancelScroll()4088     private void cancelScroll() {
4089         resetScroll();
4090         setScrollState(SCROLL_STATE_IDLE);
4091     }
4092 
onPointerUp(MotionEvent e)4093     private void onPointerUp(MotionEvent e) {
4094         final int actionIndex = e.getActionIndex();
4095         if (e.getPointerId(actionIndex) == mScrollPointerId) {
4096             // Pick a new pointer to pick up the slack.
4097             final int newIndex = actionIndex == 0 ? 1 : 0;
4098             mScrollPointerId = e.getPointerId(newIndex);
4099             mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
4100             mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
4101         }
4102     }
4103 
4104     @Override
onGenericMotionEvent(MotionEvent event)4105     public boolean onGenericMotionEvent(MotionEvent event) {
4106         if (mLayout == null) {
4107             return false;
4108         }
4109         if (mLayoutSuppressed) {
4110             return false;
4111         }
4112 
4113         int flingAxis = 0;
4114         int horizontalAxis = 0;
4115         int verticalAxis = 0;
4116         boolean useSmoothScroll = false;
4117         if (event.getAction() == MotionEvent.ACTION_SCROLL) {
4118             final float vScroll, hScroll;
4119             if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
4120                 if (mLayout.canScrollVertically()) {
4121                     // Inverse the sign of the vertical scroll to align the scroll orientation
4122                     // with AbsListView.
4123                     vScroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
4124                     verticalAxis = MotionEvent.AXIS_VSCROLL;
4125                 } else {
4126                     vScroll = 0f;
4127                 }
4128                 if (mLayout.canScrollHorizontally()) {
4129                     hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
4130                     horizontalAxis = MotionEvent.AXIS_HSCROLL;
4131                 } else {
4132                     hScroll = 0f;
4133                 }
4134             } else if ((event.getSource() & InputDeviceCompat.SOURCE_ROTARY_ENCODER) != 0) {
4135                 final float axisScroll = event.getAxisValue(MotionEventCompat.AXIS_SCROLL);
4136                 if (mLayout.canScrollVertically()) {
4137                     // Invert the sign of the vertical scroll to align the scroll orientation
4138                     // with AbsListView.
4139                     vScroll = -axisScroll;
4140                     verticalAxis = MotionEvent.AXIS_SCROLL;
4141                     hScroll = 0f;
4142                 } else if (mLayout.canScrollHorizontally()) {
4143                     vScroll = 0f;
4144                     hScroll = axisScroll;
4145                     horizontalAxis = MotionEvent.AXIS_SCROLL;
4146                 } else {
4147                     vScroll = 0f;
4148                     hScroll = 0f;
4149                 }
4150                 // Use smooth scrolling for low resolution rotary encoders to avoid the visible
4151                 // pixel jumps that would be caused by doing regular scrolling.
4152                 useSmoothScroll = mLowResRotaryEncoderFeature;
4153                 // Support fling for rotary encoders.
4154                 flingAxis = MotionEventCompat.AXIS_SCROLL;
4155             } else {
4156                 vScroll = 0f;
4157                 hScroll = 0f;
4158             }
4159 
4160             int scaledVScroll = (int) (vScroll * mScaledVerticalScrollFactor);
4161             int scaledHScroll = (int) (hScroll * mScaledHorizontalScrollFactor);
4162             if (useSmoothScroll) {
4163                 OverScroller overScroller = mViewFlinger.mOverScroller;
4164                 // Account for any remaining scroll from a previous generic motion event.
4165                 scaledVScroll += overScroller.getFinalY() - overScroller.getCurrY();
4166                 scaledHScroll += overScroller.getFinalX() - overScroller.getCurrX();
4167                 smoothScrollBy(scaledHScroll, scaledVScroll, /* interpolator= */ null,
4168                         UNDEFINED_DURATION, /* withNestedScrolling= */ true);
4169             } else {
4170                 nestedScrollByInternal(scaledHScroll, scaledVScroll, horizontalAxis,
4171                         verticalAxis, event, TYPE_NON_TOUCH);
4172             }
4173 
4174             if (flingAxis != 0 && !useSmoothScroll) {
4175                 mDifferentialMotionFlingController.onMotionEvent(event, flingAxis);
4176             }
4177         }
4178         return false;
4179     }
4180 
4181     @Override
onMeasure(int widthSpec, int heightSpec)4182     protected void onMeasure(int widthSpec, int heightSpec) {
4183         if (mLayout == null) {
4184             defaultOnMeasure(widthSpec, heightSpec);
4185             return;
4186         }
4187         if (mLayout.isAutoMeasureEnabled()) {
4188             final int widthMode = MeasureSpec.getMode(widthSpec);
4189             final int heightMode = MeasureSpec.getMode(heightSpec);
4190 
4191             /*
4192              * This specific call should be considered deprecated and replaced with
4193              * {@link #defaultOnMeasure(int, int)}. It can't actually be replaced as it could
4194              * break existing third party code but all documentation directs developers to not
4195              * override {@link LayoutManager#onMeasure(int, int)} when
4196              * {@link LayoutManager#isAutoMeasureEnabled()} returns true.
4197              */
4198             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
4199 
4200             // Calculate and track whether we should skip measurement here because the MeasureSpec
4201             // modes in both dimensions are EXACTLY.
4202             mLastAutoMeasureSkippedDueToExact =
4203                     widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
4204             if (mLastAutoMeasureSkippedDueToExact || mAdapter == null) {
4205                 return;
4206             }
4207 
4208             if (mState.mLayoutStep == State.STEP_START) {
4209                 dispatchLayoutStep1();
4210             }
4211             // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
4212             // consistency
4213             mLayout.setMeasureSpecs(widthSpec, heightSpec);
4214             mState.mIsMeasuring = true;
4215             dispatchLayoutStep2();
4216 
4217             // now we can get the width and height from the children.
4218             mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
4219 
4220             // if RecyclerView has non-exact width and height and if there is at least one child
4221             // which also has non-exact width & height, we have to re-measure.
4222             if (mLayout.shouldMeasureTwice()) {
4223                 mLayout.setMeasureSpecs(
4224                         MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
4225                         MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
4226                 mState.mIsMeasuring = true;
4227                 dispatchLayoutStep2();
4228                 // now we can get the width and height from the children.
4229                 mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
4230             }
4231 
4232             mLastAutoMeasureNonExactMeasuredWidth = getMeasuredWidth();
4233             mLastAutoMeasureNonExactMeasuredHeight = getMeasuredHeight();
4234         } else {
4235             if (mHasFixedSize) {
4236                 mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
4237                 return;
4238             }
4239             // custom onMeasure
4240             if (mAdapterUpdateDuringMeasure) {
4241                 startInterceptRequestLayout();
4242                 onEnterLayoutOrScroll();
4243                 processAdapterUpdatesAndSetAnimationFlags();
4244                 onExitLayoutOrScroll();
4245 
4246                 if (mState.mRunPredictiveAnimations) {
4247                     mState.mInPreLayout = true;
4248                 } else {
4249                     // consume remaining updates to provide a consistent state with the layout pass.
4250                     mAdapterHelper.consumeUpdatesInOnePass();
4251                     mState.mInPreLayout = false;
4252                 }
4253                 mAdapterUpdateDuringMeasure = false;
4254                 stopInterceptRequestLayout(false);
4255             } else if (mState.mRunPredictiveAnimations) {
4256                 // If mAdapterUpdateDuringMeasure is false and mRunPredictiveAnimations is true:
4257                 // this means there is already an onMeasure() call performed to handle the pending
4258                 // adapter change, two onMeasure() calls can happen if RV is a child of LinearLayout
4259                 // with layout_width=MATCH_PARENT. RV cannot call LM.onMeasure() second time
4260                 // because getViewForPosition() will crash when LM uses a child to measure.
4261                 setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
4262                 return;
4263             }
4264 
4265             if (mAdapter != null) {
4266                 mState.mItemCount = mAdapter.getItemCount();
4267             } else {
4268                 mState.mItemCount = 0;
4269             }
4270             startInterceptRequestLayout();
4271             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
4272             stopInterceptRequestLayout(false);
4273             mState.mInPreLayout = false; // clear
4274         }
4275     }
4276 
4277     /**
4278      * An implementation of {@link View#onMeasure(int, int)} to fall back to in various scenarios
4279      * where this RecyclerView is otherwise lacking better information.
4280      */
defaultOnMeasure(int widthSpec, int heightSpec)4281     void defaultOnMeasure(int widthSpec, int heightSpec) {
4282         // calling LayoutManager here is not pretty but that API is already public and it is better
4283         // than creating another method since this is internal.
4284         final int width = LayoutManager.chooseSize(widthSpec,
4285                 getPaddingLeft() + getPaddingRight(),
4286                 ViewCompat.getMinimumWidth(this));
4287         final int height = LayoutManager.chooseSize(heightSpec,
4288                 getPaddingTop() + getPaddingBottom(),
4289                 ViewCompat.getMinimumHeight(this));
4290 
4291         setMeasuredDimension(width, height);
4292     }
4293 
4294     @Override
onSizeChanged(int w, int h, int oldw, int oldh)4295     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
4296         super.onSizeChanged(w, h, oldw, oldh);
4297         if (w != oldw || h != oldh) {
4298             invalidateGlows();
4299             // layout's w/h are updated during measure/layout steps.
4300         }
4301     }
4302 
4303     /**
4304      * Sets the {@link ItemAnimator} that will handle animations involving changes
4305      * to the items in this RecyclerView. By default, RecyclerView instantiates and
4306      * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
4307      * enabled for the RecyclerView depends on the ItemAnimator and whether
4308      * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
4309      * supports item animations}.
4310      *
4311      * @param animator The ItemAnimator being set. If null, no animations will occur
4312      *                 when changes occur to the items in this RecyclerView.
4313      */
setItemAnimator(@ullable ItemAnimator animator)4314     public void setItemAnimator(@Nullable ItemAnimator animator) {
4315         if (mItemAnimator != null) {
4316             mItemAnimator.endAnimations();
4317             mItemAnimator.setListener(null);
4318         }
4319         mItemAnimator = animator;
4320         if (mItemAnimator != null) {
4321             mItemAnimator.setListener(mItemAnimatorListener);
4322         }
4323     }
4324 
onEnterLayoutOrScroll()4325     void onEnterLayoutOrScroll() {
4326         mLayoutOrScrollCounter++;
4327     }
4328 
onExitLayoutOrScroll()4329     void onExitLayoutOrScroll() {
4330         onExitLayoutOrScroll(true);
4331     }
4332 
onExitLayoutOrScroll(boolean enableChangeEvents)4333     void onExitLayoutOrScroll(boolean enableChangeEvents) {
4334         mLayoutOrScrollCounter--;
4335         if (mLayoutOrScrollCounter < 1) {
4336             if (sDebugAssertionsEnabled && mLayoutOrScrollCounter < 0) {
4337                 throw new IllegalStateException("layout or scroll counter cannot go below zero."
4338                         + "Some calls are not matching" + exceptionLabel());
4339             }
4340             mLayoutOrScrollCounter = 0;
4341             if (enableChangeEvents) {
4342                 dispatchContentChangedIfNecessary();
4343                 dispatchPendingImportantForAccessibilityChanges();
4344             }
4345         }
4346     }
4347 
isAccessibilityEnabled()4348     boolean isAccessibilityEnabled() {
4349         return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
4350     }
4351 
dispatchContentChangedIfNecessary()4352     private void dispatchContentChangedIfNecessary() {
4353         final int flags = mEatenAccessibilityChangeFlags;
4354         mEatenAccessibilityChangeFlags = 0;
4355         if (flags != 0 && isAccessibilityEnabled()) {
4356             final AccessibilityEvent event = AccessibilityEvent.obtain();
4357             event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
4358             AccessibilityEventCompat.setContentChangeTypes(event, flags);
4359             sendAccessibilityEventUnchecked(event);
4360         }
4361     }
4362 
4363     /**
4364      * Returns whether RecyclerView is currently computing a layout.
4365      * <p>
4366      * If this method returns true, it means that RecyclerView is in a lockdown state and any
4367      * attempt to update adapter contents will result in an exception because adapter contents
4368      * cannot be changed while RecyclerView is trying to compute the layout.
4369      * <p>
4370      * It is very unlikely that your code will be running during this state as it is
4371      * called by the framework when a layout traversal happens or RecyclerView starts to scroll
4372      * in response to system events (touch, accessibility etc).
4373      * <p>
4374      * This case may happen if you have some custom logic to change adapter contents in
4375      * response to a View callback (e.g. focus change callback) which might be triggered during a
4376      * layout calculation. In these cases, you should just postpone the change using a Handler or a
4377      * similar mechanism.
4378      *
4379      * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
4380      * otherwise
4381      */
isComputingLayout()4382     public boolean isComputingLayout() {
4383         return mLayoutOrScrollCounter > 0;
4384     }
4385 
4386     /**
4387      * Returns true if an accessibility event should not be dispatched now. This happens when an
4388      * accessibility request arrives while RecyclerView does not have a stable state which is very
4389      * hard to handle for a LayoutManager. Instead, this method records necessary information about
4390      * the event and dispatches a window change event after the critical section is finished.
4391      *
4392      * @return True if the accessibility event should be postponed.
4393      */
shouldDeferAccessibilityEvent(AccessibilityEvent event)4394     boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
4395         if (isComputingLayout()) {
4396             int type = 0;
4397             if (event != null) {
4398                 type = AccessibilityEventCompat.getContentChangeTypes(event);
4399             }
4400             if (type == 0) {
4401                 type = AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED;
4402             }
4403             mEatenAccessibilityChangeFlags |= type;
4404             return true;
4405         }
4406         return false;
4407     }
4408 
4409     @Override
sendAccessibilityEventUnchecked(AccessibilityEvent event)4410     public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
4411         if (shouldDeferAccessibilityEvent(event)) {
4412             return;
4413         }
4414         super.sendAccessibilityEventUnchecked(event);
4415     }
4416 
4417     @Override
dispatchPopulateAccessibilityEvent(AccessibilityEvent event)4418     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
4419         onPopulateAccessibilityEvent(event);
4420         return true;
4421     }
4422 
4423     /**
4424      * Gets the current ItemAnimator for this RecyclerView. A null return value
4425      * indicates that there is no animator and that item changes will happen without
4426      * any animations. By default, RecyclerView instantiates and
4427      * uses an instance of {@link DefaultItemAnimator}.
4428      *
4429      * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
4430      * when changes occur to the items in this RecyclerView.
4431      */
getItemAnimator()4432     public @Nullable ItemAnimator getItemAnimator() {
4433         return mItemAnimator;
4434     }
4435 
4436     /**
4437      * Post a runnable to the next frame to run pending item animations. Only the first such
4438      * request will be posted, governed by the mPostedAnimatorRunner flag.
4439      */
postAnimationRunner()4440     void postAnimationRunner() {
4441         if (!mPostedAnimatorRunner && mIsAttached) {
4442             ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
4443             mPostedAnimatorRunner = true;
4444         }
4445     }
4446 
predictiveItemAnimationsEnabled()4447     private boolean predictiveItemAnimationsEnabled() {
4448         return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
4449     }
4450 
4451     /**
4452      * Consumes adapter updates and calculates which type of animations we want to run.
4453      * Called in onMeasure and dispatchLayout.
4454      * <p>
4455      * This method may process only the pre-layout state of updates or all of them.
4456      */
processAdapterUpdatesAndSetAnimationFlags()4457     private void processAdapterUpdatesAndSetAnimationFlags() {
4458         if (mDataSetHasChangedAfterLayout) {
4459             // Processing these items have no value since data set changed unexpectedly.
4460             // Instead, we just reset it.
4461             mAdapterHelper.reset();
4462             if (mDispatchItemsChangedEvent) {
4463                 mLayout.onItemsChanged(this);
4464             }
4465         }
4466         // simple animations are a subset of advanced animations (which will cause a
4467         // pre-layout step)
4468         // If layout supports predictive animations, pre-process to decide if we want to run them
4469         if (predictiveItemAnimationsEnabled()) {
4470             mAdapterHelper.preProcess();
4471         } else {
4472             mAdapterHelper.consumeUpdatesInOnePass();
4473         }
4474         boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
4475         mState.mRunSimpleAnimations = mFirstLayoutComplete
4476                 && mItemAnimator != null
4477                 && (mDataSetHasChangedAfterLayout
4478                 || animationTypeSupported
4479                 || mLayout.mRequestedSimpleAnimations)
4480                 && (!mDataSetHasChangedAfterLayout
4481                 || mAdapter.hasStableIds());
4482         mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
4483                 && animationTypeSupported
4484                 && !mDataSetHasChangedAfterLayout
4485                 && predictiveItemAnimationsEnabled();
4486     }
4487 
4488     /**
4489      * Wrapper around layoutChildren() that handles animating changes caused by layout.
4490      * Animations work on the assumption that there are five different kinds of items
4491      * in play:
4492      * PERSISTENT: items are visible before and after layout
4493      * REMOVED: items were visible before layout and were removed by the app
4494      * ADDED: items did not exist before layout and were added by the app
4495      * DISAPPEARING: items exist in the data set before/after, but changed from
4496      * visible to non-visible in the process of layout (they were moved off
4497      * screen as a side-effect of other changes)
4498      * APPEARING: items exist in the data set before/after, but changed from
4499      * non-visible to visible in the process of layout (they were moved on
4500      * screen as a side-effect of other changes)
4501      * The overall approach figures out what items exist before/after layout and
4502      * infers one of the five above states for each of the items. Then the animations
4503      * are set up accordingly:
4504      * PERSISTENT views are animated via
4505      * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
4506      * DISAPPEARING views are animated via
4507      * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
4508      * APPEARING views are animated via
4509      * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
4510      * and changed views are animated via
4511      * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
4512      */
dispatchLayout()4513     void dispatchLayout() {
4514         if (mAdapter == null) {
4515             Log.w(TAG, "No adapter attached; skipping layout");
4516             // leave the state in START
4517             return;
4518         }
4519         if (mLayout == null) {
4520             Log.e(TAG, "No layout manager attached; skipping layout");
4521             // leave the state in START
4522             return;
4523         }
4524         mState.mIsMeasuring = false;
4525 
4526         // If the last time we measured children in onMeasure, we skipped the measurement and layout
4527         // of RV children because the MeasureSpec in both dimensions was EXACTLY, and current
4528         // dimensions of the RV are not equal to the last measured dimensions of RV, we need to
4529         // measure and layout children one last time.
4530         boolean needsRemeasureDueToExactSkip = mLastAutoMeasureSkippedDueToExact
4531                         && (mLastAutoMeasureNonExactMeasuredWidth != getWidth()
4532                         || mLastAutoMeasureNonExactMeasuredHeight != getHeight());
4533         mLastAutoMeasureNonExactMeasuredWidth = 0;
4534         mLastAutoMeasureNonExactMeasuredHeight = 0;
4535         mLastAutoMeasureSkippedDueToExact = false;
4536 
4537         if (mState.mLayoutStep == State.STEP_START) {
4538             dispatchLayoutStep1();
4539             mLayout.setExactMeasureSpecsFrom(this);
4540             dispatchLayoutStep2();
4541         } else if (mAdapterHelper.hasUpdates()
4542                 || needsRemeasureDueToExactSkip
4543                 || mLayout.getWidth() != getWidth()
4544                 || mLayout.getHeight() != getHeight()) {
4545             // First 2 steps are done in onMeasure but looks like we have to run again due to
4546             // changed size.
4547 
4548             // TODO(shepshapard): Worth a note that I believe
4549             //  "mLayout.getWidth() != getWidth() || mLayout.getHeight() != getHeight()" above is
4550             //  not actually correct, causes unnecessary work to be done, and should be
4551             //  removed. Removing causes many tests to fail and I didn't have the time to
4552             //  investigate. Just a note for the a future reader or bug fixer.
4553             mLayout.setExactMeasureSpecsFrom(this);
4554             dispatchLayoutStep2();
4555         } else {
4556             // always make sure we sync them (to ensure mode is exact)
4557             mLayout.setExactMeasureSpecsFrom(this);
4558         }
4559         dispatchLayoutStep3();
4560     }
4561 
saveFocusInfo()4562     private void saveFocusInfo() {
4563         View child = null;
4564         if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
4565             child = getFocusedChild();
4566         }
4567 
4568         final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
4569         if (focusedVh == null) {
4570             resetFocusInfo();
4571         } else {
4572             mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
4573             // mFocusedItemPosition should hold the current adapter position of the previously
4574             // focused item. If the item is removed, we store the previous adapter position of the
4575             // removed item.
4576             mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION
4577                     : (focusedVh.isRemoved() ? focusedVh.mOldPosition
4578                             : focusedVh.getAbsoluteAdapterPosition());
4579             mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
4580         }
4581     }
4582 
resetFocusInfo()4583     private void resetFocusInfo() {
4584         mState.mFocusedItemId = NO_ID;
4585         mState.mFocusedItemPosition = NO_POSITION;
4586         mState.mFocusedSubChildId = View.NO_ID;
4587     }
4588 
4589     /**
4590      * Finds the best view candidate to request focus on using mFocusedItemPosition index of the
4591      * previously focused item. It first traverses the adapter forward to find a focusable candidate
4592      * and if no such candidate is found, it reverses the focus search direction for the items
4593      * before the mFocusedItemPosition'th index;
4594      *
4595      * @return The best candidate to request focus on, or null if no such candidate exists. Null
4596      * indicates all the existing adapter items are unfocusable.
4597      */
findNextViewToFocus()4598     private @Nullable View findNextViewToFocus() {
4599         int startFocusSearchIndex = mState.mFocusedItemPosition != -1 ? mState.mFocusedItemPosition
4600                 : 0;
4601         ViewHolder nextFocus;
4602         final int itemCount = mState.getItemCount();
4603         for (int i = startFocusSearchIndex; i < itemCount; i++) {
4604             nextFocus = findViewHolderForAdapterPosition(i);
4605             if (nextFocus == null) {
4606                 break;
4607             }
4608             if (nextFocus.itemView.hasFocusable()) {
4609                 return nextFocus.itemView;
4610             }
4611         }
4612         final int limit = Math.min(itemCount, startFocusSearchIndex);
4613         for (int i = limit - 1; i >= 0; i--) {
4614             nextFocus = findViewHolderForAdapterPosition(i);
4615             if (nextFocus == null) {
4616                 return null;
4617             }
4618             if (nextFocus.itemView.hasFocusable()) {
4619                 return nextFocus.itemView;
4620             }
4621         }
4622         return null;
4623     }
4624 
recoverFocusFromState()4625     private void recoverFocusFromState() {
4626         if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()
4627                 || getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
4628                 || (getDescendantFocusability() == FOCUS_BEFORE_DESCENDANTS && isFocused())) {
4629             // No-op if either of these cases happens:
4630             // 1. RV has no focus, or 2. RV blocks focus to its children, or 3. RV takes focus
4631             // before its children and is focused (i.e. it already stole the focus away from its
4632             // descendants).
4633             return;
4634         }
4635         // only recover focus if RV itself has the focus or the focused view is hidden
4636         if (!isFocused()) {
4637             final View focusedChild = getFocusedChild();
4638             if (!mChildHelper.isHidden(focusedChild)) {
4639                 // If the currently focused child is hidden, apply the focus recovery logic.
4640                 // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
4641                 return;
4642             }
4643         }
4644         ViewHolder focusTarget = null;
4645         // RV first attempts to locate the previously focused item to request focus on using
4646         // mFocusedItemId. If such an item no longer exists, it then makes a best-effort attempt to
4647         // find the next best candidate to request focus on based on mFocusedItemPosition.
4648         if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
4649             focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
4650         }
4651         View viewToFocus = null;
4652         if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView)
4653                 || !focusTarget.itemView.hasFocusable()) {
4654             if (mChildHelper.getChildCount() > 0) {
4655                 // At this point, RV has focus and either of these conditions are true:
4656                 // 1. There's no previously focused item either because RV received focused before
4657                 // layout, or the previously focused item was removed, or RV doesn't have stable IDs
4658                 // 2. Previous focus child is hidden, or 3. Previous focused child is no longer
4659                 // focusable. In either of these cases, we make sure that RV still passes down the
4660                 // focus to one of its focusable children using a best-effort algorithm.
4661                 viewToFocus = findNextViewToFocus();
4662             }
4663         } else {
4664             // looks like the focused item has been replaced with another view that represents the
4665             // same item in the adapter. Request focus on that.
4666             viewToFocus = focusTarget.itemView;
4667         }
4668 
4669         if (viewToFocus != null) {
4670             if (mState.mFocusedSubChildId != NO_ID) {
4671                 View child = viewToFocus.findViewById(mState.mFocusedSubChildId);
4672                 if (child != null && child.isFocusable()) {
4673                     viewToFocus = child;
4674                 }
4675             }
4676             viewToFocus.requestFocus();
4677         }
4678     }
4679 
getDeepestFocusedViewWithId(View view)4680     private int getDeepestFocusedViewWithId(View view) {
4681         int lastKnownId = view.getId();
4682         while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
4683             view = ((ViewGroup) view).getFocusedChild();
4684             final int id = view.getId();
4685             if (id != View.NO_ID) {
4686                 lastKnownId = view.getId();
4687             }
4688         }
4689         return lastKnownId;
4690     }
4691 
fillRemainingScrollValues(State state)4692     final void fillRemainingScrollValues(State state) {
4693         if (getScrollState() == SCROLL_STATE_SETTLING) {
4694             final OverScroller scroller = mViewFlinger.mOverScroller;
4695             state.mRemainingScrollHorizontal = scroller.getFinalX() - scroller.getCurrX();
4696             state.mRemainingScrollVertical = scroller.getFinalY() - scroller.getCurrY();
4697         } else {
4698             state.mRemainingScrollHorizontal = 0;
4699             state.mRemainingScrollVertical = 0;
4700         }
4701     }
4702 
4703     /**
4704      * The first step of a layout where we;
4705      * - process adapter updates
4706      * - decide which animation should run
4707      * - save information about current views
4708      * - If necessary, run predictive layout and save its information
4709      */
dispatchLayoutStep1()4710     private void dispatchLayoutStep1() {
4711         mState.assertLayoutStep(State.STEP_START);
4712         fillRemainingScrollValues(mState);
4713         mState.mIsMeasuring = false;
4714         startInterceptRequestLayout();
4715         mViewInfoStore.clear();
4716         onEnterLayoutOrScroll();
4717         processAdapterUpdatesAndSetAnimationFlags();
4718         saveFocusInfo();
4719         mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
4720         mItemsAddedOrRemoved = mItemsChanged = false;
4721         mState.mInPreLayout = mState.mRunPredictiveAnimations;
4722         mState.mItemCount = mAdapter.getItemCount();
4723         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
4724 
4725         if (mState.mRunSimpleAnimations) {
4726             // Step 0: Find out where all non-removed items are, pre-layout
4727             int count = mChildHelper.getChildCount();
4728             for (int i = 0; i < count; ++i) {
4729                 final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
4730                 if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
4731                     continue;
4732                 }
4733                 final ItemHolderInfo animationInfo = mItemAnimator
4734                         .recordPreLayoutInformation(mState, holder,
4735                                 ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
4736                                 holder.getUnmodifiedPayloads());
4737                 mViewInfoStore.addToPreLayout(holder, animationInfo);
4738                 if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
4739                         && !holder.shouldIgnore() && !holder.isInvalid()) {
4740                     long key = getChangedHolderKey(holder);
4741                     // This is NOT the only place where a ViewHolder is added to old change holders
4742                     // list. There is another case where:
4743                     //    * A VH is currently hidden but not deleted
4744                     //    * The hidden item is changed in the adapter
4745                     //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
4746                     // When this case is detected, RV will un-hide that view and add to the old
4747                     // change holders list.
4748                     mViewInfoStore.addToOldChangeHolders(key, holder);
4749                 }
4750             }
4751         }
4752         if (mState.mRunPredictiveAnimations) {
4753             // Step 1: run prelayout: This will use the old positions of items. The layout manager
4754             // is expected to layout everything, even removed items (though not to add removed
4755             // items back to the container). This gives the pre-layout position of APPEARING views
4756             // which come into existence as part of the real layout.
4757 
4758             // Save old positions so that LayoutManager can run its mapping logic.
4759             saveOldPositions();
4760             final boolean didStructureChange = mState.mStructureChanged;
4761             mState.mStructureChanged = false;
4762             // temporarily disable flag because we are asking for previous layout
4763             mLayout.onLayoutChildren(mRecycler, mState);
4764             mState.mStructureChanged = didStructureChange;
4765 
4766             for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
4767                 final View child = mChildHelper.getChildAt(i);
4768                 final ViewHolder viewHolder = getChildViewHolderInt(child);
4769                 if (viewHolder.shouldIgnore()) {
4770                     continue;
4771                 }
4772                 if (!mViewInfoStore.isInPreLayout(viewHolder)) {
4773                     int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
4774                     boolean wasHidden = viewHolder
4775                             .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
4776                     if (!wasHidden) {
4777                         flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
4778                     }
4779                     final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
4780                             mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
4781                     if (wasHidden) {
4782                         recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
4783                     } else {
4784                         mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
4785                     }
4786                 }
4787             }
4788             // we don't process disappearing list because they may re-appear in post layout pass.
4789             clearOldPositions();
4790         } else {
4791             clearOldPositions();
4792         }
4793         onExitLayoutOrScroll();
4794         stopInterceptRequestLayout(false);
4795         mState.mLayoutStep = State.STEP_LAYOUT;
4796     }
4797 
4798     /**
4799      * The second layout step where we do the actual layout of the views for the final state.
4800      * This step might be run multiple times if necessary (e.g. measure).
4801      */
dispatchLayoutStep2()4802     private void dispatchLayoutStep2() {
4803         startInterceptRequestLayout();
4804         onEnterLayoutOrScroll();
4805         mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
4806         mAdapterHelper.consumeUpdatesInOnePass();
4807         mState.mItemCount = mAdapter.getItemCount();
4808         mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
4809         if (mPendingSavedState != null && mAdapter.canRestoreState()) {
4810             if (mPendingSavedState.mLayoutState != null) {
4811                 mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
4812             }
4813             mPendingSavedState = null;
4814         }
4815         // Step 2: Run layout
4816         mState.mInPreLayout = false;
4817         mLayout.onLayoutChildren(mRecycler, mState);
4818 
4819         mState.mStructureChanged = false;
4820 
4821         // onLayoutChildren may have caused client code to disable item animations; re-check
4822         mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
4823         mState.mLayoutStep = State.STEP_ANIMATIONS;
4824         onExitLayoutOrScroll();
4825         stopInterceptRequestLayout(false);
4826     }
4827 
4828     /**
4829      * The final step of the layout where we save the information about views for animations,
4830      * trigger animations and do any necessary cleanup.
4831      */
dispatchLayoutStep3()4832     private void dispatchLayoutStep3() {
4833         mState.assertLayoutStep(State.STEP_ANIMATIONS);
4834         startInterceptRequestLayout();
4835         onEnterLayoutOrScroll();
4836         mState.mLayoutStep = State.STEP_START;
4837         if (mState.mRunSimpleAnimations) {
4838             // Step 3: Find out where things are now, and process change animations.
4839             // traverse list in reverse because we may call animateChange in the loop which may
4840             // remove the target view holder.
4841             for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
4842                 ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
4843                 if (holder.shouldIgnore()) {
4844                     continue;
4845                 }
4846                 long key = getChangedHolderKey(holder);
4847                 final ItemHolderInfo animationInfo = mItemAnimator
4848                         .recordPostLayoutInformation(mState, holder);
4849                 ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
4850                 if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
4851                     // run a change animation
4852 
4853                     // If an Item is CHANGED but the updated version is disappearing, it creates
4854                     // a conflicting case.
4855                     // Since a view that is marked as disappearing is likely to be going out of
4856                     // bounds, we run a change animation. Both views will be cleaned automatically
4857                     // once their animations finish.
4858                     // On the other hand, if it is the same view holder instance, we run a
4859                     // disappearing animation instead because we are not going to rebind the updated
4860                     // VH unless it is enforced by the layout manager.
4861                     final boolean oldDisappearing = mViewInfoStore.isDisappearing(
4862                             oldChangeViewHolder);
4863                     final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
4864                     if (oldDisappearing && oldChangeViewHolder == holder) {
4865                         // run disappear animation instead of change
4866                         mViewInfoStore.addToPostLayout(holder, animationInfo);
4867                     } else {
4868                         final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
4869                                 oldChangeViewHolder);
4870                         // we add and remove so that any post info is merged.
4871                         mViewInfoStore.addToPostLayout(holder, animationInfo);
4872                         ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
4873                         if (preInfo == null) {
4874                             handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
4875                         } else {
4876                             animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
4877                                     oldDisappearing, newDisappearing);
4878                         }
4879                     }
4880                 } else {
4881                     mViewInfoStore.addToPostLayout(holder, animationInfo);
4882                 }
4883             }
4884 
4885             // Step 4: Process view info lists and trigger animations
4886             mViewInfoStore.process(mViewInfoProcessCallback);
4887         }
4888 
4889         mLayout.removeAndRecycleScrapInt(mRecycler);
4890         mState.mPreviousLayoutItemCount = mState.mItemCount;
4891         mDataSetHasChangedAfterLayout = false;
4892         mDispatchItemsChangedEvent = false;
4893         mState.mRunSimpleAnimations = false;
4894 
4895         mState.mRunPredictiveAnimations = false;
4896         mLayout.mRequestedSimpleAnimations = false;
4897         if (mRecycler.mChangedScrap != null) {
4898             mRecycler.mChangedScrap.clear();
4899         }
4900         if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
4901             // Initial prefetch has expanded cache, so reset until next prefetch.
4902             // This prevents initial prefetches from expanding the cache permanently.
4903             mLayout.mPrefetchMaxCountObserved = 0;
4904             mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
4905             mRecycler.updateViewCacheSize();
4906         }
4907 
4908         mLayout.onLayoutCompleted(mState);
4909         onExitLayoutOrScroll();
4910         stopInterceptRequestLayout(false);
4911         mViewInfoStore.clear();
4912         if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
4913             dispatchOnScrolled(0, 0);
4914         }
4915         recoverFocusFromState();
4916         resetFocusInfo();
4917     }
4918 
4919     /**
4920      * This handles the case where there is an unexpected VH missing in the pre-layout map.
4921      * <p>
4922      * We might be able to detect the error in the application which will help the developer to
4923      * resolve the issue.
4924      * <p>
4925      * If it is not an expected error, we at least print an error to notify the developer and ignore
4926      * the animation.
4927      *
4928      * https://code.google.com/p/android/issues/detail?id=193958
4929      *
4930      * @param key                 The change key
4931      * @param holder              Current ViewHolder
4932      * @param oldChangeViewHolder Changed ViewHolder
4933      */
handleMissingPreInfoForChangeError(long key, ViewHolder holder, ViewHolder oldChangeViewHolder)4934     private void handleMissingPreInfoForChangeError(long key,
4935             ViewHolder holder, ViewHolder oldChangeViewHolder) {
4936         // check if two VH have the same key, if so, print that as an error
4937         final int childCount = mChildHelper.getChildCount();
4938         for (int i = 0; i < childCount; i++) {
4939             View view = mChildHelper.getChildAt(i);
4940             ViewHolder other = getChildViewHolderInt(view);
4941             if (other == holder) {
4942                 continue;
4943             }
4944             final long otherKey = getChangedHolderKey(other);
4945             if (otherKey == key) {
4946                 if (mAdapter != null && mAdapter.hasStableIds()) {
4947                     throw new IllegalStateException("Two different ViewHolders have the same stable"
4948                             + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
4949                             + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
4950                             + exceptionLabel());
4951                 } else {
4952                     throw new IllegalStateException("Two different ViewHolders have the same change"
4953                             + " ID. This might happen due to inconsistent Adapter update events or"
4954                             + " if the LayoutManager lays out the same View multiple times."
4955                             + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
4956                             + exceptionLabel());
4957                 }
4958             }
4959         }
4960         // Very unlikely to happen but if it does, notify the developer.
4961         Log.e(TAG, "Problem while matching changed view holders with the new"
4962                 + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
4963                 + " cannot be found but it is necessary for " + holder + exceptionLabel());
4964     }
4965 
4966     /**
4967      * Records the animation information for a view holder that was bounced from hidden list. It
4968      * also clears the bounce back flag.
4969      */
recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder, ItemHolderInfo animationInfo)4970     void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
4971             ItemHolderInfo animationInfo) {
4972         // looks like this view bounced back from hidden list!
4973         viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
4974         if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
4975                 && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
4976             long key = getChangedHolderKey(viewHolder);
4977             mViewInfoStore.addToOldChangeHolders(key, viewHolder);
4978         }
4979         mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
4980     }
4981 
findMinMaxChildLayoutPositions(int[] into)4982     private void findMinMaxChildLayoutPositions(int[] into) {
4983         final int count = mChildHelper.getChildCount();
4984         if (count == 0) {
4985             into[0] = NO_POSITION;
4986             into[1] = NO_POSITION;
4987             return;
4988         }
4989         int minPositionPreLayout = Integer.MAX_VALUE;
4990         int maxPositionPreLayout = Integer.MIN_VALUE;
4991         for (int i = 0; i < count; ++i) {
4992             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
4993             if (holder.shouldIgnore()) {
4994                 continue;
4995             }
4996             final int pos = holder.getLayoutPosition();
4997             if (pos < minPositionPreLayout) {
4998                 minPositionPreLayout = pos;
4999             }
5000             if (pos > maxPositionPreLayout) {
5001                 maxPositionPreLayout = pos;
5002             }
5003         }
5004         into[0] = minPositionPreLayout;
5005         into[1] = maxPositionPreLayout;
5006     }
5007 
didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout)5008     private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
5009         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
5010         return mMinMaxLayoutPositions[0] != minPositionPreLayout
5011                 || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
5012     }
5013 
5014     @Override
removeDetachedView(View child, boolean animate)5015     protected void removeDetachedView(View child, boolean animate) {
5016         ViewHolder vh = getChildViewHolderInt(child);
5017         if (vh != null) {
5018             if (vh.isTmpDetached()) {
5019                 vh.clearTmpDetachFlag();
5020             } else if (!vh.shouldIgnore()) {
5021                 throw new IllegalArgumentException("Called removeDetachedView with a view which"
5022                         + " is not flagged as tmp detached." + vh + exceptionLabel());
5023             }
5024         } else {
5025             if (sDebugAssertionsEnabled) {
5026                 throw new IllegalArgumentException(
5027                         "No ViewHolder found for child: " + child + exceptionLabel());
5028             }
5029         }
5030 
5031         // Clear any android.view.animation.Animation that may prevent the item from
5032         // detaching when being removed. If a child is re-added before the
5033         // lazy detach occurs, it will receive invalid attach/detach sequencing.
5034         child.clearAnimation();
5035 
5036         dispatchChildDetached(child);
5037         super.removeDetachedView(child, animate);
5038     }
5039 
5040     /**
5041      * Returns a unique key to be used while handling change animations.
5042      * It might be child's position or stable id depending on the adapter type.
5043      */
getChangedHolderKey(ViewHolder holder)5044     long getChangedHolderKey(ViewHolder holder) {
5045         return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
5046     }
5047 
animateAppearance(@onNull ViewHolder itemHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)5048     void animateAppearance(@NonNull ViewHolder itemHolder,
5049             @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
5050         itemHolder.setIsRecyclable(false);
5051         if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
5052             postAnimationRunner();
5053         }
5054     }
5055 
animateDisappearance(@onNull ViewHolder holder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo)5056     void animateDisappearance(@NonNull ViewHolder holder,
5057             @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
5058         addAnimatingView(holder);
5059         holder.setIsRecyclable(false);
5060         if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
5061             postAnimationRunner();
5062         }
5063     }
5064 
animateChange(@onNull ViewHolder oldHolder, @NonNull ViewHolder newHolder, @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo, boolean oldHolderDisappearing, boolean newHolderDisappearing)5065     private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
5066             @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
5067             boolean oldHolderDisappearing, boolean newHolderDisappearing) {
5068         oldHolder.setIsRecyclable(false);
5069         if (oldHolderDisappearing) {
5070             addAnimatingView(oldHolder);
5071         }
5072         if (oldHolder != newHolder) {
5073             if (newHolderDisappearing) {
5074                 addAnimatingView(newHolder);
5075             }
5076             oldHolder.mShadowedHolder = newHolder;
5077             // old holder should disappear after animation ends
5078             addAnimatingView(oldHolder);
5079             mRecycler.unscrapView(oldHolder);
5080             newHolder.setIsRecyclable(false);
5081             newHolder.mShadowingHolder = oldHolder;
5082         }
5083         if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
5084             postAnimationRunner();
5085         }
5086     }
5087 
5088     @Override
onLayout(boolean changed, int l, int t, int r, int b)5089     protected void onLayout(boolean changed, int l, int t, int r, int b) {
5090         Trace.beginSection(TRACE_ON_LAYOUT_TAG);
5091         dispatchLayout();
5092         Trace.endSection();
5093         mFirstLayoutComplete = true;
5094     }
5095 
5096     @Override
requestLayout()5097     public void requestLayout() {
5098         if (mInterceptRequestLayoutDepth == 0 && !mLayoutSuppressed) {
5099             super.requestLayout();
5100         } else {
5101             mLayoutWasDefered = true;
5102         }
5103     }
5104 
markItemDecorInsetsDirty()5105     void markItemDecorInsetsDirty() {
5106         final int childCount = mChildHelper.getUnfilteredChildCount();
5107         for (int i = 0; i < childCount; i++) {
5108             final View child = mChildHelper.getUnfilteredChildAt(i);
5109             ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
5110         }
5111         mRecycler.markItemDecorInsetsDirty();
5112     }
5113 
5114     @Override
draw(@onNull Canvas c)5115     public void draw(@NonNull Canvas c) {
5116         super.draw(c);
5117 
5118         final int count = mItemDecorations.size();
5119         for (int i = 0; i < count; i++) {
5120             mItemDecorations.get(i).onDrawOver(c, this, mState);
5121         }
5122         // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
5123         // need find children closest to edges. Not sure if it is worth the effort.
5124         boolean needsInvalidate = false;
5125         if (mLeftGlow != null && !mLeftGlow.isFinished()) {
5126             final int restore = c.save();
5127             final int padding = mClipToPadding ? getPaddingBottom() : 0;
5128             c.rotate(270);
5129             c.translate(-getHeight() + padding, 0);
5130             needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
5131             c.restoreToCount(restore);
5132         }
5133         if (mTopGlow != null && !mTopGlow.isFinished()) {
5134             final int restore = c.save();
5135             if (mClipToPadding) {
5136                 c.translate(getPaddingLeft(), getPaddingTop());
5137             }
5138             needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
5139             c.restoreToCount(restore);
5140         }
5141         if (mRightGlow != null && !mRightGlow.isFinished()) {
5142             final int restore = c.save();
5143             final int width = getWidth();
5144             final int padding = mClipToPadding ? getPaddingTop() : 0;
5145             c.rotate(90);
5146             c.translate(padding, -width);
5147             needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
5148             c.restoreToCount(restore);
5149         }
5150         if (mBottomGlow != null && !mBottomGlow.isFinished()) {
5151             final int restore = c.save();
5152             c.rotate(180);
5153             if (mClipToPadding) {
5154                 c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
5155             } else {
5156                 c.translate(-getWidth(), -getHeight());
5157             }
5158             needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
5159             c.restoreToCount(restore);
5160         }
5161 
5162         // If some views are animating, ItemDecorators are likely to move/change with them.
5163         // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
5164         // display lists are not invalidated.
5165         if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
5166                 && mItemAnimator.isRunning()) {
5167             needsInvalidate = true;
5168         }
5169 
5170         if (needsInvalidate) {
5171             postInvalidateOnAnimation();
5172         }
5173     }
5174 
5175     @Override
onDraw(@onNull Canvas c)5176     public void onDraw(@NonNull Canvas c) {
5177         super.onDraw(c);
5178 
5179         final int count = mItemDecorations.size();
5180         for (int i = 0; i < count; i++) {
5181             mItemDecorations.get(i).onDraw(c, this, mState);
5182         }
5183     }
5184 
5185     @Override
checkLayoutParams(ViewGroup.LayoutParams p)5186     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
5187         return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
5188     }
5189 
5190     @Override
generateDefaultLayoutParams()5191     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
5192         if (mLayout == null) {
5193             throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
5194         }
5195         return mLayout.generateDefaultLayoutParams();
5196     }
5197 
5198     @Override
generateLayoutParams(AttributeSet attrs)5199     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
5200         if (mLayout == null) {
5201             throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
5202         }
5203         return mLayout.generateLayoutParams(getContext(), attrs);
5204     }
5205 
5206     @Override
generateLayoutParams(ViewGroup.LayoutParams p)5207     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
5208         if (mLayout == null) {
5209             throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
5210         }
5211         return mLayout.generateLayoutParams(p);
5212     }
5213 
5214     /**
5215      * Returns true if RecyclerView is currently running some animations.
5216      * <p>
5217      * If you want to be notified when animations are finished, use
5218      * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
5219      *
5220      * @return True if there are some item animations currently running or waiting to be started.
5221      */
isAnimating()5222     public boolean isAnimating() {
5223         return mItemAnimator != null && mItemAnimator.isRunning();
5224     }
5225 
saveOldPositions()5226     void saveOldPositions() {
5227         final int childCount = mChildHelper.getUnfilteredChildCount();
5228         for (int i = 0; i < childCount; i++) {
5229             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
5230             if (sDebugAssertionsEnabled && holder.mPosition == -1 && !holder.isRemoved()) {
5231                 throw new IllegalStateException("view holder cannot have position -1 unless it"
5232                         + " is removed" + exceptionLabel());
5233             }
5234             if (!holder.shouldIgnore()) {
5235                 holder.saveOldPosition();
5236             }
5237         }
5238     }
5239 
clearOldPositions()5240     void clearOldPositions() {
5241         final int childCount = mChildHelper.getUnfilteredChildCount();
5242         for (int i = 0; i < childCount; i++) {
5243             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
5244             if (!holder.shouldIgnore()) {
5245                 holder.clearOldPosition();
5246             }
5247         }
5248         mRecycler.clearOldPositions();
5249     }
5250 
offsetPositionRecordsForMove(int from, int to)5251     void offsetPositionRecordsForMove(int from, int to) {
5252         final int childCount = mChildHelper.getUnfilteredChildCount();
5253         final int start, end, inBetweenOffset;
5254         if (from < to) {
5255             start = from;
5256             end = to;
5257             inBetweenOffset = -1;
5258         } else {
5259             start = to;
5260             end = from;
5261             inBetweenOffset = 1;
5262         }
5263 
5264         for (int i = 0; i < childCount; i++) {
5265             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
5266             if (holder == null || holder.mPosition < start || holder.mPosition > end) {
5267                 continue;
5268             }
5269             if (sVerboseLoggingEnabled) {
5270                 Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
5271                         + holder);
5272             }
5273             if (holder.mPosition == from) {
5274                 holder.offsetPosition(to - from, false);
5275             } else {
5276                 holder.offsetPosition(inBetweenOffset, false);
5277             }
5278 
5279             mState.mStructureChanged = true;
5280         }
5281         mRecycler.offsetPositionRecordsForMove(from, to);
5282         requestLayout();
5283     }
5284 
offsetPositionRecordsForInsert(int positionStart, int itemCount)5285     void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
5286         final int childCount = mChildHelper.getUnfilteredChildCount();
5287         for (int i = 0; i < childCount; i++) {
5288             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
5289             if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
5290                 if (sVerboseLoggingEnabled) {
5291                     Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
5292                             + holder + " now at position " + (holder.mPosition + itemCount));
5293                 }
5294                 holder.offsetPosition(itemCount, false);
5295                 mState.mStructureChanged = true;
5296             }
5297         }
5298         mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
5299         requestLayout();
5300     }
5301 
offsetPositionRecordsForRemove(int positionStart, int itemCount, boolean applyToPreLayout)5302     void offsetPositionRecordsForRemove(int positionStart, int itemCount,
5303             boolean applyToPreLayout) {
5304         final int positionEnd = positionStart + itemCount;
5305         final int childCount = mChildHelper.getUnfilteredChildCount();
5306         for (int i = 0; i < childCount; i++) {
5307             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
5308             if (holder != null && !holder.shouldIgnore()) {
5309                 if (holder.mPosition >= positionEnd) {
5310                     if (sVerboseLoggingEnabled) {
5311                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
5312                                 + " holder " + holder + " now at position "
5313                                 + (holder.mPosition - itemCount));
5314                     }
5315                     holder.offsetPosition(-itemCount, applyToPreLayout);
5316                     mState.mStructureChanged = true;
5317                 } else if (holder.mPosition >= positionStart) {
5318                     if (sVerboseLoggingEnabled) {
5319                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
5320                                 + " holder " + holder + " now REMOVED");
5321                     }
5322                     holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
5323                             applyToPreLayout);
5324                     mState.mStructureChanged = true;
5325                 }
5326             }
5327         }
5328         mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
5329         requestLayout();
5330     }
5331 
5332     /**
5333      * Rebind existing views for the given range, or create as needed.
5334      *
5335      * @param positionStart Adapter position to start at
5336      * @param itemCount     Number of views that must explicitly be rebound
5337      */
viewRangeUpdate(int positionStart, int itemCount, Object payload)5338     void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
5339         final int childCount = mChildHelper.getUnfilteredChildCount();
5340         final int positionEnd = positionStart + itemCount;
5341 
5342         for (int i = 0; i < childCount; i++) {
5343             final View child = mChildHelper.getUnfilteredChildAt(i);
5344             final ViewHolder holder = getChildViewHolderInt(child);
5345             if (holder == null || holder.shouldIgnore()) {
5346                 continue;
5347             }
5348             if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
5349                 // We re-bind these view holders after pre-processing is complete so that
5350                 // ViewHolders have their final positions assigned.
5351                 holder.addFlags(ViewHolder.FLAG_UPDATE);
5352                 holder.addChangePayload(payload);
5353                 // lp cannot be null since we get ViewHolder from it.
5354                 ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
5355             }
5356         }
5357         mRecycler.viewRangeUpdate(positionStart, itemCount);
5358     }
5359 
canReuseUpdatedViewHolder(ViewHolder viewHolder)5360     boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
5361         return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
5362                 viewHolder.getUnmodifiedPayloads());
5363     }
5364 
5365     /**
5366      * Processes the fact that, as far as we can tell, the data set has completely changed.
5367      *
5368      * <ul>
5369      *   <li>Once layout occurs, all attached items should be discarded or animated.
5370      *   <li>Attached items are labeled as invalid.
5371      *   <li>Because items may still be prefetched between a "data set completely changed"
5372      *       event and a layout event, all cached items are discarded.
5373      * </ul>
5374      *
5375      * @param dispatchItemsChanged Whether to call
5376      *                             {@link LayoutManager#onItemsChanged(RecyclerView)} during
5377      *                             measure/layout.
5378      */
processDataSetCompletelyChanged(boolean dispatchItemsChanged)5379     void processDataSetCompletelyChanged(boolean dispatchItemsChanged) {
5380         mDispatchItemsChangedEvent |= dispatchItemsChanged;
5381         mDataSetHasChangedAfterLayout = true;
5382         markKnownViewsInvalid();
5383     }
5384 
5385     /**
5386      * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
5387      * data change event.
5388      */
markKnownViewsInvalid()5389     void markKnownViewsInvalid() {
5390         final int childCount = mChildHelper.getUnfilteredChildCount();
5391         for (int i = 0; i < childCount; i++) {
5392             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
5393             if (holder != null && !holder.shouldIgnore()) {
5394                 holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
5395             }
5396         }
5397         markItemDecorInsetsDirty();
5398         mRecycler.markKnownViewsInvalid();
5399     }
5400 
5401     /**
5402      * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
5403      * will trigger a {@link #requestLayout()} call.
5404      */
invalidateItemDecorations()5405     public void invalidateItemDecorations() {
5406         if (mItemDecorations.size() == 0) {
5407             return;
5408         }
5409         if (mLayout != null) {
5410             mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
5411                     + " or layout");
5412         }
5413         markItemDecorInsetsDirty();
5414         requestLayout();
5415     }
5416 
5417     /**
5418      * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
5419      * focus even if the View representing the Item is replaced during a layout calculation.
5420      * <p>
5421      * By default, this value is {@code true}.
5422      *
5423      * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
5424      * focus.
5425      * @see #setPreserveFocusAfterLayout(boolean)
5426      */
getPreserveFocusAfterLayout()5427     public boolean getPreserveFocusAfterLayout() {
5428         return mPreserveFocusAfterLayout;
5429     }
5430 
5431     /**
5432      * Set whether the RecyclerView should try to keep the same Item focused after a layout
5433      * calculation or not.
5434      * <p>
5435      * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
5436      * views may lose focus during a layout calculation as their state changes or they are replaced
5437      * with another view due to type change or animation. In these cases, RecyclerView can request
5438      * focus on the new view automatically.
5439      *
5440      * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
5441      *                                 layout calculations. Defaults to true.
5442      * @see #getPreserveFocusAfterLayout()
5443      */
setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout)5444     public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
5445         mPreserveFocusAfterLayout = preserveFocusAfterLayout;
5446     }
5447 
5448     /**
5449      * Retrieve the {@link ViewHolder} for the given child view.
5450      *
5451      * @param child Child of this RecyclerView to query for its ViewHolder
5452      * @return The child view's ViewHolder
5453      */
getChildViewHolder(@onNull View child)5454     public ViewHolder getChildViewHolder(@NonNull View child) {
5455         final ViewParent parent = child.getParent();
5456         if (parent != null && parent != this) {
5457             throw new IllegalArgumentException("View " + child + " is not a direct child of "
5458                     + this);
5459         }
5460         return getChildViewHolderInt(child);
5461     }
5462 
5463     /**
5464      * Traverses the ancestors of the given view and returns the item view that contains it and
5465      * also a direct child of the RecyclerView. This returned view can be used to get the
5466      * ViewHolder by calling {@link #getChildViewHolder(View)}.
5467      *
5468      * @param view The view that is a descendant of the RecyclerView.
5469      * @return The direct child of the RecyclerView which contains the given view or null if the
5470      * provided view is not a descendant of this RecyclerView.
5471      * @see #getChildViewHolder(View)
5472      * @see #findContainingViewHolder(View)
5473      */
findContainingItemView(@onNull View view)5474     public @Nullable View findContainingItemView(@NonNull View view) {
5475         ViewParent parent = view.getParent();
5476         while (parent != null && parent != this && parent instanceof View) {
5477             view = (View) parent;
5478             parent = view.getParent();
5479         }
5480         return parent == this ? view : null;
5481     }
5482 
5483     /**
5484      * Returns the ViewHolder that contains the given view.
5485      *
5486      * @param view The view that is a descendant of the RecyclerView.
5487      * @return The ViewHolder that contains the given view or null if the provided view is not a
5488      * descendant of this RecyclerView.
5489      */
findContainingViewHolder(@onNull View view)5490     public @Nullable ViewHolder findContainingViewHolder(@NonNull View view) {
5491         View itemView = findContainingItemView(view);
5492         return itemView == null ? null : getChildViewHolder(itemView);
5493     }
5494 
5495 
getChildViewHolderInt(View child)5496     static ViewHolder getChildViewHolderInt(View child) {
5497         if (child == null) {
5498             return null;
5499         }
5500         return ((LayoutParams) child.getLayoutParams()).mViewHolder;
5501     }
5502 
5503     /**
5504      * @deprecated use {@link #getChildAdapterPosition(View)} or
5505      * {@link #getChildLayoutPosition(View)}.
5506      */
5507     @Deprecated
getChildPosition(@onNull View child)5508     public int getChildPosition(@NonNull View child) {
5509         return getChildAdapterPosition(child);
5510     }
5511 
5512     /**
5513      * Return the adapter position that the given child view corresponds to.
5514      *
5515      * @param child Child View to query
5516      * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
5517      */
getChildAdapterPosition(@onNull View child)5518     public int getChildAdapterPosition(@NonNull View child) {
5519         final ViewHolder holder = getChildViewHolderInt(child);
5520         return holder != null ? holder.getAbsoluteAdapterPosition() : NO_POSITION;
5521     }
5522 
5523     /**
5524      * Return the adapter position of the given child view as of the latest completed layout pass.
5525      * <p>
5526      * This position may not be equal to Item's adapter position if there are pending changes
5527      * in the adapter which have not been reflected to the layout yet.
5528      *
5529      * @param child Child View to query
5530      * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
5531      * the View is representing a removed item.
5532      */
getChildLayoutPosition(@onNull View child)5533     public int getChildLayoutPosition(@NonNull View child) {
5534         final ViewHolder holder = getChildViewHolderInt(child);
5535         return holder != null ? holder.getLayoutPosition() : NO_POSITION;
5536     }
5537 
5538     /**
5539      * Return the stable item id that the given child view corresponds to.
5540      *
5541      * @param child Child View to query
5542      * @return Item id corresponding to the given view or {@link #NO_ID}
5543      */
getChildItemId(@onNull View child)5544     public long getChildItemId(@NonNull View child) {
5545         if (mAdapter == null || !mAdapter.hasStableIds()) {
5546             return NO_ID;
5547         }
5548         final ViewHolder holder = getChildViewHolderInt(child);
5549         return holder != null ? holder.getItemId() : NO_ID;
5550     }
5551 
5552     /**
5553      * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
5554      * {@link #findViewHolderForAdapterPosition(int)}
5555      */
5556     @Deprecated
findViewHolderForPosition(int position)5557     public @Nullable ViewHolder findViewHolderForPosition(int position) {
5558         return findViewHolderForPosition(position, false);
5559     }
5560 
5561     /**
5562      * Return the ViewHolder for the item in the given position of the data set as of the latest
5563      * layout pass.
5564      * <p>
5565      * This method checks only the children of RecyclerView. If the item at the given
5566      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
5567      * <p>
5568      * Note that when Adapter contents change, ViewHolder positions are not updated until the
5569      * next layout calculation. If there are pending adapter updates, the return value of this
5570      * method may not match your adapter contents. You can use
5571      * #{@link ViewHolder#getBindingAdapterPosition()} to get the current adapter position
5572      * of a ViewHolder. If the {@link Adapter} that is assigned to the RecyclerView is an adapter
5573      * that combines other adapters (e.g. {@link ConcatAdapter}), you can use the
5574      * {@link ViewHolder#getBindingAdapter()}) to find the position relative to the {@link Adapter}
5575      * that bound the {@link ViewHolder}.
5576      * <p>
5577      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
5578      * with the same layout position representing the same Item. In this case, the updated
5579      * ViewHolder will be returned.
5580      *
5581      * @param position The position of the item in the data set of the adapter
5582      * @return The ViewHolder at <code>position</code> or null if there is no such item
5583      */
findViewHolderForLayoutPosition(int position)5584     public @Nullable ViewHolder findViewHolderForLayoutPosition(int position) {
5585         return findViewHolderForPosition(position, false);
5586     }
5587 
5588     /**
5589      * Return the ViewHolder for the item in the given position of the data set. Unlike
5590      * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
5591      * adapter changes that may not be reflected to the layout yet. On the other hand, if
5592      * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
5593      * calculated yet, this method will return <code>null</code> since the new positions of views
5594      * are unknown until the layout is calculated.
5595      * <p>
5596      * This method checks only the children of RecyclerView. If the item at the given
5597      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
5598      * <p>
5599      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
5600      * representing the same Item. In this case, the updated ViewHolder will be returned.
5601      *
5602      * @param position The position of the item in the data set of the adapter
5603      * @return The ViewHolder at <code>position</code> or null if there is no such item
5604      */
findViewHolderForAdapterPosition(int position)5605     public @Nullable ViewHolder findViewHolderForAdapterPosition(int position) {
5606         if (mDataSetHasChangedAfterLayout) {
5607             return null;
5608         }
5609         final int childCount = mChildHelper.getUnfilteredChildCount();
5610         // hidden VHs are not preferred but if that is the only one we find, we rather return it
5611         ViewHolder hidden = null;
5612         for (int i = 0; i < childCount; i++) {
5613             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
5614             if (holder != null && !holder.isRemoved()
5615                     && getAdapterPositionInRecyclerView(holder) == position) {
5616                 if (mChildHelper.isHidden(holder.itemView)) {
5617                     hidden = holder;
5618                 } else {
5619                     return holder;
5620                 }
5621             }
5622         }
5623         return hidden;
5624     }
5625 
findViewHolderForPosition(int position, boolean checkNewPosition)5626     @Nullable ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
5627         final int childCount = mChildHelper.getUnfilteredChildCount();
5628         ViewHolder hidden = null;
5629         for (int i = 0; i < childCount; i++) {
5630             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
5631             if (holder != null && !holder.isRemoved()) {
5632                 if (checkNewPosition) {
5633                     if (holder.mPosition != position) {
5634                         continue;
5635                     }
5636                 } else if (holder.getLayoutPosition() != position) {
5637                     continue;
5638                 }
5639                 if (mChildHelper.isHidden(holder.itemView)) {
5640                     hidden = holder;
5641                 } else {
5642                     return holder;
5643                 }
5644             }
5645         }
5646         // This method should not query cached views. It creates a problem during adapter updates
5647         // when we are dealing with already laid out views. Also, for the public method, it is more
5648         // reasonable to return null if position is not laid out.
5649         return hidden;
5650     }
5651 
5652     /**
5653      * Return the ViewHolder for the item with the given id. The RecyclerView must
5654      * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
5655      * return a non-null value.
5656      * <p>
5657      * This method checks only the children of RecyclerView. If the item with the given
5658      * <code>id</code> is not laid out, it <em>will not</em> create a new one.
5659      *
5660      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
5661      * same id. In this case, the updated ViewHolder will be returned.
5662      *
5663      * @param id The id for the requested item
5664      * @return The ViewHolder with the given <code>id</code> or null if there is no such item
5665      */
findViewHolderForItemId(long id)5666     public ViewHolder findViewHolderForItemId(long id) {
5667         if (mAdapter == null || !mAdapter.hasStableIds()) {
5668             return null;
5669         }
5670         final int childCount = mChildHelper.getUnfilteredChildCount();
5671         ViewHolder hidden = null;
5672         for (int i = 0; i < childCount; i++) {
5673             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
5674             if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
5675                 if (mChildHelper.isHidden(holder.itemView)) {
5676                     hidden = holder;
5677                 } else {
5678                     return holder;
5679                 }
5680             }
5681         }
5682         return hidden;
5683     }
5684 
5685     /**
5686      * Find the topmost view under the given point.
5687      *
5688      * @param x Horizontal position in pixels to search
5689      * @param y Vertical position in pixels to search
5690      * @return The child view under (x, y) or null if no matching child is found
5691      */
findChildViewUnder(float x, float y)5692     public @Nullable View findChildViewUnder(float x, float y) {
5693         final int count = mChildHelper.getChildCount();
5694         for (int i = count - 1; i >= 0; i--) {
5695             final View child = mChildHelper.getChildAt(i);
5696             final float translationX = child.getTranslationX();
5697             final float translationY = child.getTranslationY();
5698             if (x >= child.getLeft() + translationX
5699                     && x <= child.getRight() + translationX
5700                     && y >= child.getTop() + translationY
5701                     && y <= child.getBottom() + translationY) {
5702                 return child;
5703             }
5704         }
5705         return null;
5706     }
5707 
5708     @Override
drawChild(@onNull Canvas canvas, View child, long drawingTime)5709     public boolean drawChild(@NonNull Canvas canvas, View child, long drawingTime) {
5710         return super.drawChild(canvas, child, drawingTime);
5711     }
5712 
5713     /**
5714      * Offset the bounds of all child views by <code>dy</code> pixels.
5715      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
5716      *
5717      * @param dy Vertical pixel offset to apply to the bounds of all child views
5718      */
offsetChildrenVertical(@x int dy)5719     public void offsetChildrenVertical(@Px int dy) {
5720         final int childCount = mChildHelper.getChildCount();
5721         for (int i = 0; i < childCount; i++) {
5722             mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
5723         }
5724     }
5725 
5726     /**
5727      * Called when an item view is attached to this RecyclerView.
5728      *
5729      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
5730      * of child views as they become attached. This will be called before a
5731      * {@link LayoutManager} measures or lays out the view and is a good time to perform these
5732      * changes.</p>
5733      *
5734      * @param child Child view that is now attached to this RecyclerView and its associated window
5735      */
onChildAttachedToWindow(@onNull View child)5736     public void onChildAttachedToWindow(@NonNull View child) {
5737     }
5738 
5739     /**
5740      * Called when an item view is detached from this RecyclerView.
5741      *
5742      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
5743      * of child views as they become detached. This will be called as a
5744      * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
5745      *
5746      * @param child Child view that is now detached from this RecyclerView and its associated window
5747      */
onChildDetachedFromWindow(@onNull View child)5748     public void onChildDetachedFromWindow(@NonNull View child) {
5749     }
5750 
5751     /**
5752      * Offset the bounds of all child views by <code>dx</code> pixels.
5753      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
5754      *
5755      * @param dx Horizontal pixel offset to apply to the bounds of all child views
5756      */
offsetChildrenHorizontal(@x int dx)5757     public void offsetChildrenHorizontal(@Px int dx) {
5758         final int childCount = mChildHelper.getChildCount();
5759         for (int i = 0; i < childCount; i++) {
5760             mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
5761         }
5762     }
5763 
5764     /**
5765      * Returns the bounds of the view including its decoration and margins.
5766      *
5767      * @param view      The view element to check
5768      * @param outBounds A rect that will receive the bounds of the element including its
5769      *                  decoration and margins.
5770      */
getDecoratedBoundsWithMargins(@onNull View view, @NonNull Rect outBounds)5771     public void getDecoratedBoundsWithMargins(@NonNull View view, @NonNull Rect outBounds) {
5772         getDecoratedBoundsWithMarginsInt(view, outBounds);
5773     }
5774 
getDecoratedBoundsWithMarginsInt(View view, Rect outBounds)5775     static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
5776         final LayoutParams lp = (LayoutParams) view.getLayoutParams();
5777         final Rect insets = lp.mDecorInsets;
5778         outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
5779                 view.getTop() - insets.top - lp.topMargin,
5780                 view.getRight() + insets.right + lp.rightMargin,
5781                 view.getBottom() + insets.bottom + lp.bottomMargin);
5782     }
5783 
getItemDecorInsetsForChild(View child)5784     Rect getItemDecorInsetsForChild(View child) {
5785         final LayoutParams lp = (LayoutParams) child.getLayoutParams();
5786         if (!lp.mInsetsDirty) {
5787             return lp.mDecorInsets;
5788         }
5789 
5790         if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
5791             // changed/invalid items should not be updated until they are rebound.
5792             return lp.mDecorInsets;
5793         }
5794         final Rect insets = lp.mDecorInsets;
5795         insets.set(0, 0, 0, 0);
5796         final int decorCount = mItemDecorations.size();
5797         for (int i = 0; i < decorCount; i++) {
5798             mTempRect.set(0, 0, 0, 0);
5799             mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
5800             insets.left += mTempRect.left;
5801             insets.top += mTempRect.top;
5802             insets.right += mTempRect.right;
5803             insets.bottom += mTempRect.bottom;
5804         }
5805         lp.mInsetsDirty = false;
5806         return insets;
5807     }
5808 
5809     /**
5810      * Called when the scroll position of this RecyclerView changes. Subclasses should use
5811      * this method to respond to scrolling within the adapter's data set instead of an explicit
5812      * listener.
5813      *
5814      * <p>This method will always be invoked before listeners. If a subclass needs to perform
5815      * any additional upkeep or bookkeeping after scrolling but before listeners run,
5816      * this is a good place to do so.</p>
5817      *
5818      * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
5819      * the distance scrolled in either direction within the adapter's data set instead of absolute
5820      * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
5821      * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
5822      * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
5823      * do not correspond to the data set scroll position. However, some subclasses may choose
5824      * to use these fields as special offsets.</p>
5825      *
5826      * @param dx horizontal distance scrolled in pixels
5827      * @param dy vertical distance scrolled in pixels
5828      */
onScrolled(@x int dx, @Px int dy)5829     public void onScrolled(@Px int dx, @Px int dy) {
5830         // Do nothing
5831     }
5832 
dispatchOnScrolled(int hresult, int vresult)5833     void dispatchOnScrolled(int hresult, int vresult) {
5834         mDispatchScrollCounter++;
5835         // Pass the current scrollX/scrollY values as current values. No actual change in these
5836         // properties occurred. Pass negative hresult and vresult as old values so that
5837         // postSendViewScrolledAccessibilityEventCallback(l - oldl, t - oldt) in onScrollChanged
5838         // sends the scrolled accessibility event correctly.
5839         final int scrollX = getScrollX();
5840         final int scrollY = getScrollY();
5841         onScrollChanged(scrollX, scrollY, scrollX - hresult, scrollY - vresult);
5842 
5843         // Pass the real deltas to onScrolled, the RecyclerView-specific method.
5844         onScrolled(hresult, vresult);
5845 
5846         // Invoke listeners last. Subclassed view methods always handle the event first.
5847         // All internal state is consistent by the time listeners are invoked.
5848         if (mScrollListener != null) {
5849             mScrollListener.onScrolled(this, hresult, vresult);
5850         }
5851         if (mScrollListeners != null) {
5852             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
5853                 mScrollListeners.get(i).onScrolled(this, hresult, vresult);
5854             }
5855         }
5856         mDispatchScrollCounter--;
5857     }
5858 
5859     /**
5860      * Called when the scroll state of this RecyclerView changes. Subclasses should use this
5861      * method to respond to state changes instead of an explicit listener.
5862      *
5863      * <p>This method will always be invoked before listeners, but after the LayoutManager
5864      * responds to the scroll state change.</p>
5865      *
5866      * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
5867      *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
5868      */
onScrollStateChanged(int state)5869     public void onScrollStateChanged(int state) {
5870         // Do nothing
5871     }
5872 
5873     /**
5874      * Copied from OverScroller, this returns the distance that a fling with the given velocity
5875      * will go.
5876      * @param velocity The velocity of the fling
5877      * @return The distance that will be traveled by a fling of the given velocity.
5878      */
getSplineFlingDistance(int velocity)5879     private float getSplineFlingDistance(int velocity) {
5880         final double l =
5881                 Math.log(INFLEXION * Math.abs(velocity) / (SCROLL_FRICTION * mPhysicalCoef));
5882         final double decelMinusOne = DECELERATION_RATE - 1.0;
5883         return (float) (SCROLL_FRICTION * mPhysicalCoef
5884                 * Math.exp(DECELERATION_RATE / decelMinusOne * l));
5885     }
5886 
dispatchOnScrollStateChanged(int state)5887     void dispatchOnScrollStateChanged(int state) {
5888         // Let the LayoutManager go first; this allows it to bring any properties into
5889         // a consistent state before the RecyclerView subclass responds.
5890         if (mLayout != null) {
5891             mLayout.onScrollStateChanged(state);
5892         }
5893 
5894         // Let the RecyclerView subclass handle this event next; any LayoutManager property
5895         // changes will be reflected by this time.
5896         onScrollStateChanged(state);
5897 
5898         // Listeners go last. All other internal state is consistent by this point.
5899         if (mScrollListener != null) {
5900             mScrollListener.onScrollStateChanged(this, state);
5901         }
5902         if (mScrollListeners != null) {
5903             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
5904                 mScrollListeners.get(i).onScrollStateChanged(this, state);
5905             }
5906         }
5907     }
5908 
5909     /**
5910      * Returns whether there are pending adapter updates which are not yet applied to the layout.
5911      * <p>
5912      * If this method returns <code>true</code>, it means that what user is currently seeing may not
5913      * reflect them adapter contents (depending on what has changed).
5914      * You may use this information to defer or cancel some operations.
5915      * <p>
5916      * This method returns true if RecyclerView has not yet calculated the first layout after it is
5917      * attached to the Window or the Adapter has been replaced.
5918      *
5919      * @return True if there are some adapter updates which are not yet reflected to layout or false
5920      * if layout is up to date.
5921      */
hasPendingAdapterUpdates()5922     public boolean hasPendingAdapterUpdates() {
5923         return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
5924                 || mAdapterHelper.hasPendingUpdates();
5925     }
5926 
5927     // Effectively private.  Set to default to avoid synthetic accessor.
5928     class ViewFlinger implements Runnable {
5929         private int mLastFlingX;
5930         private int mLastFlingY;
5931         OverScroller mOverScroller;
5932         Interpolator mInterpolator = sQuinticInterpolator;
5933 
5934         // When set to true, postOnAnimation callbacks are delayed until the run method completes
5935         private boolean mEatRunOnAnimationRequest = false;
5936 
5937         // Tracks if postAnimationCallback should be re-attached when it is done
5938         private boolean mReSchedulePostAnimationCallback = false;
5939 
ViewFlinger()5940         ViewFlinger() {
5941             mOverScroller = new OverScroller(getContext(), sQuinticInterpolator);
5942         }
5943 
5944         @Override
run()5945         public void run() {
5946             if (mLayout == null) {
5947                 stop();
5948                 return; // no layout, cannot scroll.
5949             }
5950 
5951             mReSchedulePostAnimationCallback = false;
5952             mEatRunOnAnimationRequest = true;
5953 
5954             consumePendingUpdateOperations();
5955 
5956             // TODO(72745539): After reviewing the code, it seems to me we may actually want to
5957             // update the reference to the OverScroller after onAnimation.  It looks to me like
5958             // it is possible that a new OverScroller could be created (due to a new Interpolator
5959             // being used), when the current OverScroller knows it's done after
5960             // scroller.computeScrollOffset() is called.  If that happens, and we don't update the
5961             // reference, it seems to me that we could prematurely stop the newly created scroller
5962             // due to setScrollState(SCROLL_STATE_IDLE) being called below.
5963 
5964             // Keep a local reference so that if it is changed during onAnimation method, it won't
5965             // cause unexpected behaviors
5966             final OverScroller scroller = mOverScroller;
5967             if (scroller.computeScrollOffset()) {
5968                 final int x = scroller.getCurrX();
5969                 final int y = scroller.getCurrY();
5970                 int unconsumedX = x - mLastFlingX;
5971                 int unconsumedY = y - mLastFlingY;
5972                 mLastFlingX = x;
5973                 mLastFlingY = y;
5974 
5975                 unconsumedX = consumeFlingInHorizontalStretch(unconsumedX);
5976                 unconsumedY = consumeFlingInVerticalStretch(unconsumedY);
5977 
5978                 int consumedX = 0;
5979                 int consumedY = 0;
5980 
5981                 // Nested Pre Scroll
5982                 mReusableIntPair[0] = 0;
5983                 mReusableIntPair[1] = 0;
5984                 if (dispatchNestedPreScroll(unconsumedX, unconsumedY, mReusableIntPair, null,
5985                         TYPE_NON_TOUCH)) {
5986                     unconsumedX -= mReusableIntPair[0];
5987                     unconsumedY -= mReusableIntPair[1];
5988                 }
5989 
5990                 // Based on movement, we may want to trigger the hiding of existing over scroll
5991                 // glows.
5992                 if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
5993                     considerReleasingGlowsOnScroll(unconsumedX, unconsumedY);
5994                 }
5995 
5996                 // Local Scroll
5997                 if (mAdapter != null) {
5998                     mReusableIntPair[0] = 0;
5999                     mReusableIntPair[1] = 0;
6000                     scrollStep(unconsumedX, unconsumedY, mReusableIntPair);
6001                     consumedX = mReusableIntPair[0];
6002                     consumedY = mReusableIntPair[1];
6003                     unconsumedX -= consumedX;
6004                     unconsumedY -= consumedY;
6005 
6006                     // If SmoothScroller exists, this ViewFlinger was started by it, so we must
6007                     // report back to SmoothScroller.
6008                     SmoothScroller smoothScroller = mLayout.mSmoothScroller;
6009                     if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
6010                             && smoothScroller.isRunning()) {
6011                         final int adapterSize = mState.getItemCount();
6012                         if (adapterSize == 0) {
6013                             smoothScroller.stop();
6014                         } else if (smoothScroller.getTargetPosition() >= adapterSize) {
6015                             smoothScroller.setTargetPosition(adapterSize - 1);
6016                             smoothScroller.onAnimation(consumedX, consumedY);
6017                         } else {
6018                             smoothScroller.onAnimation(consumedX, consumedY);
6019                         }
6020                     }
6021                 }
6022 
6023                 if (!mItemDecorations.isEmpty()) {
6024                     invalidate();
6025                 }
6026 
6027                 // Nested Post Scroll
6028                 mReusableIntPair[0] = 0;
6029                 mReusableIntPair[1] = 0;
6030                 dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, null,
6031                         TYPE_NON_TOUCH, mReusableIntPair);
6032                 unconsumedX -= mReusableIntPair[0];
6033                 unconsumedY -= mReusableIntPair[1];
6034 
6035                 if (consumedX != 0 || consumedY != 0) {
6036                     dispatchOnScrolled(consumedX, consumedY);
6037                 }
6038 
6039                 if (!awakenScrollBars()) {
6040                     invalidate();
6041                 }
6042 
6043                 // We are done scrolling if scroller is finished, or for both the x and y dimension,
6044                 // we are done scrolling or we can't scroll further (we know we can't scroll further
6045                 // when we have unconsumed scroll distance).  It's possible that we don't need
6046                 // to also check for scroller.isFinished() at all, but no harm in doing so in case
6047                 // of old bugs in Overscroller.
6048                 boolean scrollerFinishedX = scroller.getCurrX() == scroller.getFinalX();
6049                 boolean scrollerFinishedY = scroller.getCurrY() == scroller.getFinalY();
6050                 final boolean doneScrolling = scroller.isFinished()
6051                         || ((scrollerFinishedX || unconsumedX != 0)
6052                         && (scrollerFinishedY || unconsumedY != 0));
6053 
6054                 // Get the current smoothScroller. It may have changed by this point and we need to
6055                 // make sure we don't stop scrolling if it has changed and it's pending an initial
6056                 // run.
6057                 SmoothScroller smoothScroller = mLayout.mSmoothScroller;
6058                 boolean smoothScrollerPending =
6059                         smoothScroller != null && smoothScroller.isPendingInitialRun();
6060 
6061                 if (!smoothScrollerPending && doneScrolling) {
6062                     // If we are done scrolling and the layout's SmoothScroller is not pending,
6063                     // do the things we do at the end of a scroll and don't postOnAnimation.
6064 
6065                     if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
6066                         final int vel = (int) scroller.getCurrVelocity();
6067                         int velX = unconsumedX < 0 ? -vel : unconsumedX > 0 ? vel : 0;
6068                         int velY = unconsumedY < 0 ? -vel : unconsumedY > 0 ? vel : 0;
6069                         absorbGlows(velX, velY);
6070                     }
6071 
6072                     if (ALLOW_THREAD_GAP_WORK) {
6073                         mPrefetchRegistry.clearPrefetchPositions();
6074                     }
6075                 } else {
6076                     // Otherwise continue the scroll.
6077 
6078                     postOnAnimation();
6079                     if (mGapWorker != null) {
6080                         mGapWorker.postFromTraversal(RecyclerView.this, consumedX, consumedY);
6081                     }
6082                 }
6083                 if (Build.VERSION.SDK_INT >= 35) {
6084                     Api35Impl.setFrameContentVelocity(RecyclerView.this,
6085                             Math.abs(scroller.getCurrVelocity()));
6086                 }
6087             }
6088 
6089             SmoothScroller smoothScroller = mLayout.mSmoothScroller;
6090             // call this after the onAnimation is complete not to have inconsistent callbacks etc.
6091             if (smoothScroller != null && smoothScroller.isPendingInitialRun()) {
6092                 smoothScroller.onAnimation(0, 0);
6093             }
6094 
6095             mEatRunOnAnimationRequest = false;
6096             if (mReSchedulePostAnimationCallback) {
6097                 internalPostOnAnimation();
6098             } else {
6099                 setScrollState(SCROLL_STATE_IDLE);
6100                 stopNestedScroll(TYPE_NON_TOUCH);
6101             }
6102         }
6103 
postOnAnimation()6104         void postOnAnimation() {
6105             if (mEatRunOnAnimationRequest) {
6106                 mReSchedulePostAnimationCallback = true;
6107             } else {
6108                 internalPostOnAnimation();
6109             }
6110         }
6111 
internalPostOnAnimation()6112         private void internalPostOnAnimation() {
6113             removeCallbacks(this);
6114             ViewCompat.postOnAnimation(RecyclerView.this, this);
6115         }
6116 
fling(int velocityX, int velocityY)6117         public void fling(int velocityX, int velocityY) {
6118             setScrollState(SCROLL_STATE_SETTLING);
6119             mLastFlingX = mLastFlingY = 0;
6120             // Because you can't define a custom interpolator for flinging, we should make sure we
6121             // reset ourselves back to the teh default interpolator in case a different call
6122             // changed our interpolator.
6123             if (mInterpolator != sQuinticInterpolator) {
6124                 mInterpolator = sQuinticInterpolator;
6125                 mOverScroller = new OverScroller(getContext(), sQuinticInterpolator);
6126             }
6127             mOverScroller.fling(0, 0, velocityX, velocityY,
6128                     Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
6129             postOnAnimation();
6130         }
6131 
6132         /**
6133          * Smooth scrolls the RecyclerView by a given distance.
6134          *
6135          * @param dx           x distance in pixels.
6136          * @param dy           y distance in pixels.
6137          * @param duration     Duration of the animation in milliseconds. Set to
6138          *                     {@link #UNDEFINED_DURATION} to have the duration automatically
6139          *                     calculated
6140          *                     based on an internally defined standard velocity.
6141          * @param interpolator {@link Interpolator} to be used for scrolling. If it is {@code null},
6142          *                     RecyclerView will use an internal default interpolator.
6143          */
smoothScrollBy(int dx, int dy, int duration, @Nullable Interpolator interpolator)6144         public void smoothScrollBy(int dx, int dy, int duration,
6145                 @Nullable Interpolator interpolator) {
6146 
6147             // Handle cases where parameter values aren't defined.
6148             if (duration == UNDEFINED_DURATION) {
6149                 duration = computeScrollDuration(dx, dy);
6150             }
6151             if (interpolator == null) {
6152                 interpolator = sQuinticInterpolator;
6153             }
6154 
6155             // If the Interpolator has changed, create a new OverScroller with the new
6156             // interpolator.
6157             if (mInterpolator != interpolator) {
6158                 mInterpolator = interpolator;
6159                 mOverScroller = new OverScroller(getContext(), interpolator);
6160             }
6161 
6162             // Reset the last fling information.
6163             mLastFlingX = mLastFlingY = 0;
6164 
6165             // Set to settling state and start scrolling.
6166             setScrollState(SCROLL_STATE_SETTLING);
6167             mOverScroller.startScroll(0, 0, dx, dy, duration);
6168 
6169             if (Build.VERSION.SDK_INT < 23) {
6170                 // b/64931938 before API 23, startScroll() does not reset getCurX()/getCurY()
6171                 // to start values, which causes fillRemainingScrollValues() put in obsolete values
6172                 // for LayoutManager.onLayoutChildren().
6173                 mOverScroller.computeScrollOffset();
6174             }
6175 
6176             postOnAnimation();
6177         }
6178 
6179         /**
6180          * Computes of an animated scroll in milliseconds.
6181          * @param dx           x distance in pixels.
6182          * @param dy           y distance in pixels.
6183          * @return The duration of the animated scroll in milliseconds.
6184          */
computeScrollDuration(int dx, int dy)6185         private int computeScrollDuration(int dx, int dy) {
6186             final int absDx = Math.abs(dx);
6187             final int absDy = Math.abs(dy);
6188             final boolean horizontal = absDx > absDy;
6189             final int containerSize = horizontal ? getWidth() : getHeight();
6190 
6191             float absDelta = (float) (horizontal ? absDx : absDy);
6192             final int duration = (int) (((absDelta / containerSize) + 1) * 300);
6193 
6194             return Math.min(duration, MAX_SCROLL_DURATION);
6195         }
6196 
stop()6197         public void stop() {
6198             removeCallbacks(this);
6199             mOverScroller.abortAnimation();
6200         }
6201 
6202     }
6203 
repositionShadowingViews()6204     void repositionShadowingViews() {
6205         // Fix up shadow views used by change animations
6206         int count = mChildHelper.getChildCount();
6207         for (int i = 0; i < count; i++) {
6208             View view = mChildHelper.getChildAt(i);
6209             ViewHolder holder = getChildViewHolder(view);
6210             if (holder != null && holder.mShadowingHolder != null) {
6211                 View shadowingView = holder.mShadowingHolder.itemView;
6212                 int left = view.getLeft();
6213                 int top = view.getTop();
6214                 if (left != shadowingView.getLeft() || top != shadowingView.getTop()) {
6215                     shadowingView.layout(left, top,
6216                             left + shadowingView.getWidth(),
6217                             top + shadowingView.getHeight());
6218                 }
6219             }
6220         }
6221     }
6222 
6223     private class RecyclerViewDataObserver extends AdapterDataObserver {
RecyclerViewDataObserver()6224         RecyclerViewDataObserver() {
6225         }
6226 
6227         @Override
onChanged()6228         public void onChanged() {
6229             assertNotInLayoutOrScroll(null);
6230             mState.mStructureChanged = true;
6231 
6232             processDataSetCompletelyChanged(true);
6233             if (!mAdapterHelper.hasPendingUpdates()) {
6234                 requestLayout();
6235             }
6236         }
6237 
6238         @Override
onItemRangeChanged(int positionStart, int itemCount, Object payload)6239         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
6240             assertNotInLayoutOrScroll(null);
6241             if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
6242                 triggerUpdateProcessor();
6243             }
6244         }
6245 
6246         @Override
onItemRangeInserted(int positionStart, int itemCount)6247         public void onItemRangeInserted(int positionStart, int itemCount) {
6248             assertNotInLayoutOrScroll(null);
6249             if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
6250                 triggerUpdateProcessor();
6251             }
6252         }
6253 
6254         @Override
onItemRangeRemoved(int positionStart, int itemCount)6255         public void onItemRangeRemoved(int positionStart, int itemCount) {
6256             assertNotInLayoutOrScroll(null);
6257             if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
6258                 triggerUpdateProcessor();
6259             }
6260         }
6261 
6262         @Override
onItemRangeMoved(int fromPosition, int toPosition, int itemCount)6263         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
6264             assertNotInLayoutOrScroll(null);
6265             if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
6266                 triggerUpdateProcessor();
6267             }
6268         }
6269 
triggerUpdateProcessor()6270         void triggerUpdateProcessor() {
6271             if (mHasFixedSize && mIsAttached) {
6272                 ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
6273             } else {
6274                 mAdapterUpdateDuringMeasure = true;
6275                 requestLayout();
6276             }
6277         }
6278 
6279         @Override
onStateRestorationPolicyChanged()6280         public void onStateRestorationPolicyChanged() {
6281             if (mPendingSavedState == null) {
6282                 return;
6283             }
6284             // If there is a pending saved state and the new mode requires us to restore it,
6285             // we'll request a layout which will call the adapter to see if it can restore state
6286             // and trigger state restoration
6287             Adapter<?> adapter = mAdapter;
6288             if (adapter != null && adapter.canRestoreState()) {
6289                 requestLayout();
6290             }
6291         }
6292     }
6293 
6294     /**
6295      * EdgeEffectFactory lets you customize the over-scroll edge effect for RecyclerViews.
6296      *
6297      * @see RecyclerView#setEdgeEffectFactory(EdgeEffectFactory)
6298      */
6299     public static class EdgeEffectFactory {
6300 
6301         @Retention(RetentionPolicy.SOURCE)
6302         @IntDef({DIRECTION_LEFT, DIRECTION_TOP, DIRECTION_RIGHT, DIRECTION_BOTTOM})
6303         public @interface EdgeDirection {
6304         }
6305 
6306         /**
6307          * Direction constant for the left edge
6308          */
6309         public static final int DIRECTION_LEFT = 0;
6310 
6311         /**
6312          * Direction constant for the top edge
6313          */
6314         public static final int DIRECTION_TOP = 1;
6315 
6316         /**
6317          * Direction constant for the right edge
6318          */
6319         public static final int DIRECTION_RIGHT = 2;
6320 
6321         /**
6322          * Direction constant for the bottom edge
6323          */
6324         public static final int DIRECTION_BOTTOM = 3;
6325 
6326         /**
6327          * Create a new EdgeEffect for the provided direction.
6328          */
createEdgeEffect(@onNull RecyclerView view, @EdgeDirection int direction)6329         protected @NonNull EdgeEffect createEdgeEffect(@NonNull RecyclerView view,
6330                         @EdgeDirection int direction) {
6331             return new EdgeEffect(view.getContext());
6332         }
6333     }
6334 
6335     /**
6336      * The default EdgeEffectFactory sets the edge effect type of the EdgeEffect.
6337      */
6338     static class StretchEdgeEffectFactory extends EdgeEffectFactory {
6339         @Override
createEdgeEffect(@onNull RecyclerView view, int direction)6340         protected @NonNull EdgeEffect createEdgeEffect(@NonNull RecyclerView view, int direction) {
6341             return new EdgeEffect(view.getContext());
6342         }
6343     }
6344 
6345     /**
6346      * RecycledViewPool lets you share Views between multiple RecyclerViews.
6347      * <p>
6348      * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
6349      * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
6350      * <p>
6351      * RecyclerView automatically creates a pool for itself if you don't provide one.
6352      */
6353     public static class RecycledViewPool {
6354         private static final int DEFAULT_MAX_SCRAP = 5;
6355 
6356         /**
6357          * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
6358          *
6359          * Note that this tracks running averages of create/bind time across all RecyclerViews
6360          * (and, indirectly, Adapters) that use this pool.
6361          *
6362          * 1) This enables us to track average create and bind times across multiple adapters. Even
6363          * though create (and especially bind) may behave differently for different Adapter
6364          * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
6365          *
6366          * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
6367          * false for all other views of its type for the same deadline. This prevents items
6368          * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
6369          */
6370         static class ScrapData {
6371             final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
6372             int mMaxScrap = DEFAULT_MAX_SCRAP;
6373             long mCreateRunningAverageNs = 0;
6374             long mBindRunningAverageNs = 0;
6375         }
6376 
6377         SparseArray<ScrapData> mScrap = new SparseArray<>();
6378 
6379         /**
6380          * Attach counts for clearing (that is, emptying the pool when there are no adapters
6381          * attached) and for PoolingContainer release are tracked separately to maintain the
6382          * historical behavior of this functionality.
6383          *
6384          * The count for clearing is inaccurate in certain scenarios: for instance, if a
6385          * RecyclerView is removed from the view hierarchy and thrown away to be GCed, the
6386          * attach count will never be correspondingly decreased.  However, it has been this way
6387          * for years without any complaints, so we are not going to potentially increase the
6388          * number of scenarios where the pool would be cleared.
6389          *
6390          * The attached adapters for PoolingContainer purposes strives to be more accurate, as
6391          * it will be decremented whenever a RecyclerView is detached from the window.  This
6392          * could potentially be inaccurate in the unlikely event that someone is manually driving
6393          * a detached RecyclerView by calling measure, layout, draw, etc.  However, the
6394          * implementation of {@link RecyclerView#onDetachedFromWindow()} suggests this is not the
6395          * only unexpected behavior that doing so might provoke, so this should be acceptable.
6396          */
6397         int mAttachCountForClearing = 0;
6398 
6399         /**
6400          * The set of adapters for PoolingContainer release purposes
6401          *
6402          * @see #mAttachCountForClearing
6403          */
6404         Set<Adapter<?>> mAttachedAdaptersForPoolingContainer =
6405                 Collections.newSetFromMap(new IdentityHashMap<>());
6406 
6407         /**
6408          * Discard all ViewHolders.
6409          */
clear()6410         public void clear() {
6411             for (int i = 0; i < mScrap.size(); i++) {
6412                 ScrapData data = mScrap.valueAt(i);
6413                 for (ViewHolder scrap: data.mScrapHeap) {
6414                     PoolingContainer.callPoolingContainerOnRelease(scrap.itemView);
6415                 }
6416                 data.mScrapHeap.clear();
6417             }
6418         }
6419 
6420         /**
6421          * Sets the maximum number of ViewHolders to hold in the pool before discarding.
6422          *
6423          * @param viewType ViewHolder Type
6424          * @param max      Maximum number
6425          */
setMaxRecycledViews(int viewType, int max)6426         public void setMaxRecycledViews(int viewType, int max) {
6427             ScrapData scrapData = getScrapDataForType(viewType);
6428             scrapData.mMaxScrap = max;
6429             final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
6430             while (scrapHeap.size() > max) {
6431                 scrapHeap.remove(scrapHeap.size() - 1);
6432             }
6433         }
6434 
6435         /**
6436          * Returns the current number of Views held by the RecycledViewPool of the given view type.
6437          */
getRecycledViewCount(int viewType)6438         public int getRecycledViewCount(int viewType) {
6439             return getScrapDataForType(viewType).mScrapHeap.size();
6440         }
6441 
6442         /**
6443          * Acquire a ViewHolder of the specified type from the pool, or {@code null} if none are
6444          * present.
6445          *
6446          * @param viewType ViewHolder type.
6447          * @return ViewHolder of the specified type acquired from the pool, or {@code null} if none
6448          * are present.
6449          */
getRecycledView(int viewType)6450         public @Nullable ViewHolder getRecycledView(int viewType) {
6451             final ScrapData scrapData = mScrap.get(viewType);
6452             if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
6453                 final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
6454                 for (int i = scrapHeap.size() - 1; i >= 0; i--) {
6455                     if (!scrapHeap.get(i).isAttachedToTransitionOverlay()) {
6456                         return scrapHeap.remove(i);
6457                     }
6458                 }
6459             }
6460             return null;
6461         }
6462 
6463         /**
6464          * Total number of ViewHolders held by the pool.
6465          *
6466          * @return Number of ViewHolders held by the pool.
6467          */
size()6468         int size() {
6469             int count = 0;
6470             for (int i = 0; i < mScrap.size(); i++) {
6471                 ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
6472                 if (viewHolders != null) {
6473                     count += viewHolders.size();
6474                 }
6475             }
6476             return count;
6477         }
6478 
6479         /**
6480          * Add a scrap ViewHolder to the pool.
6481          * <p>
6482          * If the pool is already full for that ViewHolder's type, it will be immediately discarded.
6483          *
6484          * @param scrap ViewHolder to be added to the pool.
6485          */
putRecycledView(ViewHolder scrap)6486         public void putRecycledView(ViewHolder scrap) {
6487             final int viewType = scrap.getItemViewType();
6488             final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
6489             if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
6490                 PoolingContainer.callPoolingContainerOnRelease(scrap.itemView);
6491                 return;
6492             }
6493             if (sDebugAssertionsEnabled && scrapHeap.contains(scrap)) {
6494                 throw new IllegalArgumentException("this scrap item already exists");
6495             }
6496             scrap.resetInternal();
6497             scrapHeap.add(scrap);
6498         }
6499 
runningAverage(long oldAverage, long newValue)6500         long runningAverage(long oldAverage, long newValue) {
6501             if (oldAverage == 0) {
6502                 return newValue;
6503             }
6504             return (oldAverage / 4 * 3) + (newValue / 4);
6505         }
6506 
factorInCreateTime(int viewType, long createTimeNs)6507         void factorInCreateTime(int viewType, long createTimeNs) {
6508             ScrapData scrapData = getScrapDataForType(viewType);
6509             scrapData.mCreateRunningAverageNs = runningAverage(
6510                     scrapData.mCreateRunningAverageNs, createTimeNs);
6511         }
6512 
factorInBindTime(int viewType, long bindTimeNs)6513         void factorInBindTime(int viewType, long bindTimeNs) {
6514             ScrapData scrapData = getScrapDataForType(viewType);
6515             scrapData.mBindRunningAverageNs = runningAverage(
6516                     scrapData.mBindRunningAverageNs, bindTimeNs);
6517         }
6518 
willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs)6519         boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
6520             long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
6521             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
6522         }
6523 
willBindInTime(int viewType, long approxCurrentNs, long deadlineNs)6524         boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
6525             long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
6526             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
6527         }
6528 
attach()6529         void attach() {
6530             mAttachCountForClearing++;
6531         }
6532 
detach()6533         void detach() {
6534             mAttachCountForClearing--;
6535         }
6536 
6537         /**
6538          * Adds this adapter to the set of adapters being tracked for PoolingContainer release
6539          * purposes.  This method may validly be called multiple times for a given adapter.
6540          * Additional calls to this method for an already-attached adapter are a no-op.
6541          *
6542          * @param adapter the adapter to ensure is in the set
6543          */
attachForPoolingContainer(@onNull Adapter<?> adapter)6544         void attachForPoolingContainer(@NonNull Adapter<?> adapter) {
6545             mAttachedAdaptersForPoolingContainer.add(adapter);
6546         }
6547 
6548         /**
6549          * Removes this adapter from the set of adapters being tracked for PoolingContainer
6550          * release purposes. This method may validly be called multiple times for a given adapter.
6551          + Additional calls to this method for an already-detached adapter are a no-op.
6552          *
6553          * @param adapter the adapter to be removed from the set
6554          * @param isBeingReplaced {@code true} if this detach is immediately preceding a call to
6555          * {@link #attachForPoolingContainer(Adapter)} and
6556          * {@link PoolingContainerListener#onRelease()} should not be triggered, or false otherwise
6557          */
detachForPoolingContainer(@onNull Adapter<?> adapter, boolean isBeingReplaced)6558         void detachForPoolingContainer(@NonNull Adapter<?> adapter, boolean isBeingReplaced) {
6559             mAttachedAdaptersForPoolingContainer.remove(adapter);
6560             if (mAttachedAdaptersForPoolingContainer.size() == 0 && !isBeingReplaced) {
6561                 for (int keyIndex = 0; keyIndex < mScrap.size(); keyIndex++) {
6562                     ArrayList<ViewHolder> scrapHeap = mScrap.get(mScrap.keyAt(keyIndex)).mScrapHeap;
6563                     for (int i = 0; i < scrapHeap.size(); i++) {
6564                         PoolingContainer.callPoolingContainerOnRelease(
6565                                 scrapHeap.get(i).itemView
6566                         );
6567                     }
6568                 }
6569             }
6570         }
6571 
6572         /**
6573          * Detaches the old adapter and attaches the new one.
6574          * <p>
6575          * RecycledViewPool will clear its cache if it has only one adapter attached and the new
6576          * adapter uses a different ViewHolder than the oldAdapter.
6577          *
6578          * @param oldAdapter             The previous adapter instance. Will be detached.
6579          * @param newAdapter             The new adapter instance. Will be attached.
6580          * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
6581          *                               ViewHolder and view types.
6582          */
onAdapterChanged(Adapter<?> oldAdapter, Adapter<?> newAdapter, boolean compatibleWithPrevious)6583         void onAdapterChanged(Adapter<?> oldAdapter, Adapter<?> newAdapter,
6584                 boolean compatibleWithPrevious) {
6585             if (oldAdapter != null) {
6586                 detach();
6587             }
6588             if (!compatibleWithPrevious && mAttachCountForClearing == 0) {
6589                 clear();
6590             }
6591             if (newAdapter != null) {
6592                 attach();
6593             }
6594         }
6595 
getScrapDataForType(int viewType)6596         private ScrapData getScrapDataForType(int viewType) {
6597             ScrapData scrapData = mScrap.get(viewType);
6598             if (scrapData == null) {
6599                 scrapData = new ScrapData();
6600                 mScrap.put(viewType, scrapData);
6601             }
6602             return scrapData;
6603         }
6604     }
6605 
6606     /**
6607      * Utility method for finding an internal RecyclerView, if present
6608      */
findNestedRecyclerView(@onNull View view)6609     static @Nullable RecyclerView findNestedRecyclerView(@NonNull View view) {
6610         if (!(view instanceof ViewGroup)) {
6611             return null;
6612         }
6613         if (view instanceof RecyclerView) {
6614             return (RecyclerView) view;
6615         }
6616         final ViewGroup parent = (ViewGroup) view;
6617         final int count = parent.getChildCount();
6618         for (int i = 0; i < count; i++) {
6619             final View child = parent.getChildAt(i);
6620             final RecyclerView descendant = findNestedRecyclerView(child);
6621             if (descendant != null) {
6622                 return descendant;
6623             }
6624         }
6625         return null;
6626     }
6627 
6628     /**
6629      * Utility method for clearing holder's internal RecyclerView, if present
6630      */
clearNestedRecyclerViewIfNotNested(@onNull ViewHolder holder)6631     static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
6632         if (holder.mNestedRecyclerView != null) {
6633             View item = holder.mNestedRecyclerView.get();
6634             while (item != null) {
6635                 if (item == holder.itemView) {
6636                     return; // match found, don't need to clear
6637                 }
6638 
6639                 ViewParent parent = item.getParent();
6640                 if (parent instanceof View) {
6641                     item = (View) parent;
6642                 } else {
6643                     item = null;
6644                 }
6645             }
6646             holder.mNestedRecyclerView = null; // not nested
6647         }
6648     }
6649 
6650     /**
6651      * Time base for deadline-aware work scheduling. Overridable for testing.
6652      *
6653      * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
6654      * isn't relevant.
6655      */
getNanoTime()6656     long getNanoTime() {
6657         if (ALLOW_THREAD_GAP_WORK) {
6658             return System.nanoTime();
6659         } else {
6660             return 0;
6661         }
6662     }
6663 
6664     /**
6665      * A Recycler is responsible for managing scrapped or detached item views for reuse.
6666      *
6667      * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
6668      * that has been marked for removal or reuse.</p>
6669      *
6670      * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
6671      * an adapter's data set representing the data at a given position or item ID.
6672      * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
6673      * If not, the view can be quickly reused by the LayoutManager with no further work.
6674      * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
6675      * may be repositioned by a LayoutManager without remeasurement.</p>
6676      */
6677     public final class Recycler {
6678         final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
6679         ArrayList<ViewHolder> mChangedScrap = null;
6680 
6681         final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
6682 
6683         private final List<ViewHolder>
6684                 mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
6685 
6686         private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
6687         int mViewCacheMax = DEFAULT_CACHE_SIZE;
6688 
6689         RecycledViewPool mRecyclerPool;
6690 
6691         private ViewCacheExtension mViewCacheExtension;
6692 
6693         static final int DEFAULT_CACHE_SIZE = 2;
6694 
6695         /**
6696          * Clear scrap views out of this recycler. Detached views contained within a
6697          * recycled view pool will remain.
6698          */
clear()6699         public void clear() {
6700             mAttachedScrap.clear();
6701             recycleAndClearCachedViews();
6702         }
6703 
6704         /**
6705          * Set the maximum number of detached, valid views we should retain for later use.
6706          *
6707          * @param viewCount Number of views to keep before sending views to the shared pool
6708          */
setViewCacheSize(int viewCount)6709         public void setViewCacheSize(int viewCount) {
6710             mRequestedCacheMax = viewCount;
6711             updateViewCacheSize();
6712         }
6713 
updateViewCacheSize()6714         void updateViewCacheSize() {
6715             int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
6716             mViewCacheMax = mRequestedCacheMax + extraCache;
6717 
6718             // first, try the views that can be recycled
6719             for (int i = mCachedViews.size() - 1;
6720                     i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
6721                 recycleCachedViewAt(i);
6722             }
6723         }
6724 
6725         /**
6726          * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
6727          *
6728          * @return List of ViewHolders in the scrap list.
6729          */
getScrapList()6730         public @NonNull List<ViewHolder> getScrapList() {
6731             return mUnmodifiableAttachedScrap;
6732         }
6733 
6734         /**
6735          * Helper method for getViewForPosition.
6736          * <p>
6737          * Checks whether a given view holder can be used for the provided position.
6738          *
6739          * @param holder ViewHolder
6740          * @return true if ViewHolder matches the provided position, false otherwise
6741          */
validateViewHolderForOffsetPosition(ViewHolder holder)6742         boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
6743             // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
6744             // if it is not removed, verify the type and id.
6745             if (holder.isRemoved()) {
6746                 if (sDebugAssertionsEnabled && !mState.isPreLayout()) {
6747                     throw new IllegalStateException("should not receive a removed view unless it"
6748                             + " is pre layout" + exceptionLabel());
6749                 }
6750                 return mState.isPreLayout();
6751             }
6752             if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
6753                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
6754                         + "adapter position" + holder + exceptionLabel());
6755             }
6756             if (!mState.isPreLayout()) {
6757                 // don't check type if it is pre-layout.
6758                 final int type = mAdapter.getItemViewType(holder.mPosition);
6759                 if (type != holder.getItemViewType()) {
6760                     return false;
6761                 }
6762             }
6763             if (mAdapter.hasStableIds()) {
6764                 return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
6765             }
6766             return true;
6767         }
6768 
6769         /**
6770          * Attempts to bind view, and account for relevant timing information. If
6771          * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
6772          *
6773          * @param holder         Holder to be bound.
6774          * @param offsetPosition Position of item to be bound.
6775          * @param position       Pre-layout position of item to be bound.
6776          * @param deadlineNs     Time, relative to getNanoTime(), by which bind/create work should
6777          *                       complete. If FOREVER_NS is passed, this method will not fail to
6778          *                       bind the holder.
6779          */
6780         @SuppressWarnings("unchecked")
tryBindViewHolderByDeadline(@onNull ViewHolder holder, int offsetPosition, int position, long deadlineNs)6781         private boolean tryBindViewHolderByDeadline(@NonNull ViewHolder holder, int offsetPosition,
6782                 int position, long deadlineNs) {
6783             holder.mBindingAdapter = null;
6784             holder.mOwnerRecyclerView = RecyclerView.this;
6785             final int viewType = holder.getItemViewType();
6786             long startBindNs = getNanoTime();
6787             if (deadlineNs != FOREVER_NS
6788                     && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
6789                 // abort - we have a deadline we can't meet
6790                 return false;
6791             }
6792 
6793             // Holders being bound should be either fully attached or fully detached.
6794             // We don't want to bind with views that are temporarily detached, because that
6795             // creates a situation in which they are unable to reason about their attach state
6796             // properly.
6797             // For example, isAttachedToWindow will return true, but the itemView will lack a
6798             // parent. This breaks, among other possible issues, anything involving traversing
6799             // the view tree, such as ViewTreeLifecycleOwner.
6800             // Thus, we temporarily reattach any temp-detached holders for the bind operation.
6801             // See https://issuetracker.google.com/265347515 for additional details on problems
6802             // resulting from this
6803             boolean reattachedForBind = false;
6804             if (holder.isTmpDetached()) {
6805                 attachViewToParent(holder.itemView, getChildCount(),
6806                         holder.itemView.getLayoutParams());
6807                 reattachedForBind = true;
6808             }
6809 
6810             mAdapter.bindViewHolder(holder, offsetPosition);
6811 
6812             if (reattachedForBind) {
6813                 detachViewFromParent(holder.itemView);
6814             }
6815 
6816             long endBindNs = getNanoTime();
6817             mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
6818             attachAccessibilityDelegateOnBind(holder);
6819             if (mState.isPreLayout()) {
6820                 holder.mPreLayoutPosition = position;
6821             }
6822             return true;
6823         }
6824 
6825         /**
6826          * Binds the given View to the position. The View can be a View previously retrieved via
6827          * {@link #getViewForPosition(int)} or created by
6828          * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
6829          * <p>
6830          * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
6831          * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
6832          * wants to handle its own recycling logic.
6833          * <p>
6834          * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
6835          * you don't need to call this method unless you want to bind this View to another position.
6836          *
6837          * @param view     The view to update.
6838          * @param position The position of the item to bind to this View.
6839          */
bindViewToPosition(@onNull View view, int position)6840         public void bindViewToPosition(@NonNull View view, int position) {
6841             ViewHolder holder = getChildViewHolderInt(view);
6842             if (holder == null) {
6843                 throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
6844                         + " pass arbitrary views to this method, they should be created by the "
6845                         + "Adapter" + exceptionLabel());
6846             }
6847             final int offsetPosition = mAdapterHelper.findPositionOffset(position);
6848             if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
6849                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
6850                         + "position " + position + "(offset:" + offsetPosition + ")."
6851                         + "state:" + mState.getItemCount() + exceptionLabel());
6852             }
6853             tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
6854 
6855             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
6856             final LayoutParams rvLayoutParams;
6857             if (lp == null) {
6858                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
6859                 holder.itemView.setLayoutParams(rvLayoutParams);
6860             } else if (!checkLayoutParams(lp)) {
6861                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
6862                 holder.itemView.setLayoutParams(rvLayoutParams);
6863             } else {
6864                 rvLayoutParams = (LayoutParams) lp;
6865             }
6866 
6867             rvLayoutParams.mInsetsDirty = true;
6868             rvLayoutParams.mViewHolder = holder;
6869             rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
6870         }
6871 
6872         /**
6873          * RecyclerView provides artificial position range (item count) in pre-layout state and
6874          * automatically maps these positions to {@link Adapter} positions when
6875          * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
6876          * <p>
6877          * Usually, LayoutManager does not need to worry about this. However, in some cases, your
6878          * LayoutManager may need to call some custom component with item positions in which
6879          * case you need the actual adapter position instead of the pre layout position. You
6880          * can use this method to convert a pre-layout position to adapter (post layout) position.
6881          * <p>
6882          * Note that if the provided position belongs to a deleted ViewHolder, this method will
6883          * return -1.
6884          * <p>
6885          * Calling this method in post-layout state returns the same value back.
6886          *
6887          * @param position The pre-layout position to convert. Must be greater or equal to 0 and
6888          *                 less than {@link State#getItemCount()}.
6889          */
convertPreLayoutPositionToPostLayout(int position)6890         public int convertPreLayoutPositionToPostLayout(int position) {
6891             if (position < 0 || position >= mState.getItemCount()) {
6892                 throw new IndexOutOfBoundsException("invalid position " + position + ". State "
6893                         + "item count is " + mState.getItemCount() + exceptionLabel());
6894             }
6895             if (!mState.isPreLayout()) {
6896                 return position;
6897             }
6898             return mAdapterHelper.findPositionOffset(position);
6899         }
6900 
6901         /**
6902          * Obtain a view initialized for the given position.
6903          *
6904          * This method should be used by {@link LayoutManager} implementations to obtain
6905          * views to represent data from an {@link Adapter}.
6906          * <p>
6907          * The Recycler may reuse a scrap or detached view from a shared pool if one is
6908          * available for the correct view type. If the adapter has not indicated that the
6909          * data at the given position has changed, the Recycler will attempt to hand back
6910          * a scrap view that was previously initialized for that data without rebinding.
6911          *
6912          * @param position Position to obtain a view for
6913          * @return A view representing the data at <code>position</code> from <code>adapter</code>
6914          */
getViewForPosition(int position)6915         public @NonNull View getViewForPosition(int position) {
6916             return getViewForPosition(position, false);
6917         }
6918 
getViewForPosition(int position, boolean dryRun)6919         View getViewForPosition(int position, boolean dryRun) {
6920             return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
6921         }
6922 
6923         /**
6924          * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
6925          * cache, the RecycledViewPool, or creating it directly.
6926          * <p>
6927          * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
6928          * rather than constructing or binding a ViewHolder if it doesn't think it has time.
6929          * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
6930          * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
6931          * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
6932          *
6933          * @param position   Position of ViewHolder to be returned.
6934          * @param dryRun     True if the ViewHolder should not be removed from scrap/cache/
6935          * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
6936          *                   complete. If FOREVER_NS is passed, this method will not fail to
6937          *                   create/bind the holder if needed.
6938          * @return ViewHolder for requested position
6939          */
tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs)6940         @Nullable ViewHolder tryGetViewHolderForPositionByDeadline(int position,
6941                 boolean dryRun, long deadlineNs) {
6942             if (position < 0 || position >= mState.getItemCount()) {
6943                 throw new IndexOutOfBoundsException("Invalid item position " + position
6944                         + "(" + position + "). Item count:" + mState.getItemCount()
6945                         + exceptionLabel());
6946             }
6947             boolean fromScrapOrHiddenOrCache = false;
6948             ViewHolder holder = null;
6949             // 0) If there is a changed scrap, try to find from there
6950             if (mState.isPreLayout()) {
6951                 holder = getChangedScrapViewForPosition(position);
6952                 fromScrapOrHiddenOrCache = holder != null;
6953             }
6954             // 1) Find by position from scrap/hidden list/cache
6955             if (holder == null) {
6956                 holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
6957                 if (holder != null) {
6958                     if (!validateViewHolderForOffsetPosition(holder)) {
6959                         // recycle holder (and unscrap if relevant) since it can't be used
6960                         if (!dryRun) {
6961                             // we would like to recycle this but need to make sure it is not used by
6962                             // animation logic etc.
6963                             holder.addFlags(ViewHolder.FLAG_INVALID);
6964                             if (holder.isScrap()) {
6965                                 removeDetachedView(holder.itemView, false);
6966                                 holder.unScrap();
6967                             } else if (holder.wasReturnedFromScrap()) {
6968                                 holder.clearReturnedFromScrapFlag();
6969                             }
6970                             recycleViewHolderInternal(holder);
6971                         }
6972                         holder = null;
6973                     } else {
6974                         fromScrapOrHiddenOrCache = true;
6975                     }
6976                 }
6977             }
6978             if (holder == null) {
6979                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
6980                 if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
6981                     throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
6982                             + "position " + position + "(offset:" + offsetPosition + ")."
6983                             + "state:" + mState.getItemCount() + exceptionLabel());
6984                 }
6985 
6986                 final int type = mAdapter.getItemViewType(offsetPosition);
6987                 // 2) Find from scrap/cache via stable ids, if exists
6988                 if (mAdapter.hasStableIds()) {
6989                     holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
6990                             type, dryRun);
6991                     if (holder != null) {
6992                         // update position
6993                         holder.mPosition = offsetPosition;
6994                         fromScrapOrHiddenOrCache = true;
6995                     }
6996                 }
6997                 if (holder == null && mViewCacheExtension != null) {
6998                     // We are NOT sending the offsetPosition because LayoutManager does not
6999                     // know it.
7000                     final View view = mViewCacheExtension
7001                             .getViewForPositionAndType(this, position, type);
7002                     if (view != null) {
7003                         holder = getChildViewHolder(view);
7004                         if (holder == null) {
7005                             throw new IllegalArgumentException("getViewForPositionAndType returned"
7006                                     + " a view which does not have a ViewHolder"
7007                                     + exceptionLabel());
7008                         } else if (holder.shouldIgnore()) {
7009                             throw new IllegalArgumentException("getViewForPositionAndType returned"
7010                                     + " a view that is ignored. You must call stopIgnoring before"
7011                                     + " returning this view." + exceptionLabel());
7012                         }
7013                     }
7014                 }
7015                 if (holder == null) { // fallback to pool
7016                     if (sVerboseLoggingEnabled) {
7017                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
7018                                 + position + ") fetching from shared pool");
7019                     }
7020                     holder = getRecycledViewPool().getRecycledView(type);
7021                     if (holder != null) {
7022                         holder.resetInternal();
7023                         if (FORCE_INVALIDATE_DISPLAY_LIST) {
7024                             invalidateDisplayListInt(holder);
7025                         }
7026                     }
7027                 }
7028                 if (holder == null) {
7029                     long start = getNanoTime();
7030                     if (deadlineNs != FOREVER_NS
7031                             && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
7032                         // abort - we have a deadline we can't meet
7033                         return null;
7034                     }
7035                     holder = mAdapter.createViewHolder(RecyclerView.this, type);
7036                     if (ALLOW_THREAD_GAP_WORK) {
7037                         // only bother finding nested RV if prefetching
7038                         RecyclerView innerView = findNestedRecyclerView(holder.itemView);
7039                         if (innerView != null) {
7040                             holder.mNestedRecyclerView = new WeakReference<>(innerView);
7041                         }
7042                     }
7043 
7044                     long end = getNanoTime();
7045                     mRecyclerPool.factorInCreateTime(type, end - start);
7046                     if (sVerboseLoggingEnabled) {
7047                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
7048                     }
7049                 }
7050             }
7051 
7052             // This is very ugly but the only place we can grab this information
7053             // before the View is rebound and returned to the LayoutManager for post layout ops.
7054             // We don't need this in pre-layout since the VH is not updated by the LM.
7055             if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
7056                     .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
7057                 holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
7058                 if (mState.mRunSimpleAnimations) {
7059                     int changeFlags = ItemAnimator
7060                             .buildAdapterChangeFlagsForAnimations(holder);
7061                     changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
7062                     final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
7063                             holder, changeFlags, holder.getUnmodifiedPayloads());
7064                     recordAnimationInfoIfBouncedHiddenView(holder, info);
7065                 }
7066             }
7067 
7068             boolean bound = false;
7069             if (mState.isPreLayout() && holder.isBound()) {
7070                 // do not update unless we absolutely have to.
7071                 holder.mPreLayoutPosition = position;
7072             } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
7073                 if (sDebugAssertionsEnabled && holder.isRemoved()) {
7074                     throw new IllegalStateException("Removed holder should be bound and it should"
7075                             + " come here only in pre-layout. Holder: " + holder
7076                             + exceptionLabel());
7077                 }
7078                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
7079                 bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
7080             }
7081 
7082             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
7083             final LayoutParams rvLayoutParams;
7084             if (lp == null) {
7085                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
7086                 holder.itemView.setLayoutParams(rvLayoutParams);
7087             } else if (!checkLayoutParams(lp)) {
7088                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
7089                 holder.itemView.setLayoutParams(rvLayoutParams);
7090             } else {
7091                 rvLayoutParams = (LayoutParams) lp;
7092             }
7093             rvLayoutParams.mViewHolder = holder;
7094             rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
7095             return holder;
7096         }
7097 
attachAccessibilityDelegateOnBind(ViewHolder holder)7098         private void attachAccessibilityDelegateOnBind(ViewHolder holder) {
7099             if (isAccessibilityEnabled()) {
7100                 final View itemView = holder.itemView;
7101                 if (itemView.getImportantForAccessibility()
7102                         == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
7103                     itemView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
7104                 }
7105                 if (mAccessibilityDelegate == null) {
7106                     return;
7107                 }
7108                 AccessibilityDelegateCompat itemDelegate = mAccessibilityDelegate.getItemDelegate();
7109                 if (itemDelegate instanceof RecyclerViewAccessibilityDelegate.ItemDelegate) {
7110                     // If there was already an a11y delegate set on the itemView, store it in the
7111                     // itemDelegate and then set the itemDelegate as the a11y delegate.
7112                     ((RecyclerViewAccessibilityDelegate.ItemDelegate) itemDelegate)
7113                             .saveOriginalDelegate(itemView);
7114                 }
7115                 ViewCompat.setAccessibilityDelegate(itemView, itemDelegate);
7116             }
7117         }
7118 
invalidateDisplayListInt(ViewHolder holder)7119         private void invalidateDisplayListInt(ViewHolder holder) {
7120             if (holder.itemView instanceof ViewGroup) {
7121                 invalidateDisplayListInt((ViewGroup) holder.itemView, false);
7122             }
7123         }
7124 
invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis)7125         private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
7126             for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
7127                 final View view = viewGroup.getChildAt(i);
7128                 if (view instanceof ViewGroup) {
7129                     invalidateDisplayListInt((ViewGroup) view, true);
7130                 }
7131             }
7132             if (!invalidateThis) {
7133                 return;
7134             }
7135             // we need to force it to become invisible
7136             if (viewGroup.getVisibility() == View.INVISIBLE) {
7137                 viewGroup.setVisibility(View.VISIBLE);
7138                 viewGroup.setVisibility(View.INVISIBLE);
7139             } else {
7140                 final int visibility = viewGroup.getVisibility();
7141                 viewGroup.setVisibility(View.INVISIBLE);
7142                 viewGroup.setVisibility(visibility);
7143             }
7144         }
7145 
7146         /**
7147          * Recycle a detached view. The specified view will be added to a pool of views
7148          * for later rebinding and reuse.
7149          *
7150          * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
7151          * View is scrapped, it will be removed from scrap list.</p>
7152          *
7153          * @param view Removed view for recycling
7154          * @see LayoutManager#removeAndRecycleView(View, Recycler)
7155          */
recycleView(@onNull View view)7156         public void recycleView(@NonNull View view) {
7157             // This public recycle method tries to make view recycle-able since layout manager
7158             // intended to recycle this view (e.g. even if it is in scrap or change cache)
7159             ViewHolder holder = getChildViewHolderInt(view);
7160             if (holder.isTmpDetached()) {
7161                 removeDetachedView(view, false);
7162             }
7163             if (holder.isScrap()) {
7164                 holder.unScrap();
7165             } else if (holder.wasReturnedFromScrap()) {
7166                 holder.clearReturnedFromScrapFlag();
7167             }
7168             recycleViewHolderInternal(holder);
7169             // If the ViewHolder is running ItemAnimator, we want the recycleView() in scroll pass
7170             // to stop the ItemAnimator and put ViewHolder back in cache or Pool.
7171             // There are three situations:
7172             // 1. If the custom Adapter clears ViewPropertyAnimator in view detach like the
7173             //    leanback (TV) app does, the ItemAnimator is likely to be stopped and
7174             //    recycleViewHolderInternal will succeed.
7175             // 2. If the custom Adapter clears ViewPropertyAnimator, but the ItemAnimator uses
7176             //    "pending runnable" and ViewPropertyAnimator has not started yet, the ItemAnimator
7177             //    on the view will not be cleared. See b/73552923.
7178             // 3. If the custom Adapter does not clear ViewPropertyAnimator in view detach, the
7179             //    ItemAnimator will not be cleared.
7180             // Since both 2&3 lead to failure of recycleViewHolderInternal(), we just explicitly end
7181             // the ItemAnimator, the callback of ItemAnimator.endAnimations() will recycle the View.
7182             //
7183             // Note the order: we must call endAnimation() after recycleViewHolderInternal()
7184             // to avoid recycle twice. If ViewHolder isRecyclable is false,
7185             // recycleViewHolderInternal() will not recycle it, endAnimation() will reset
7186             // isRecyclable flag and recycle the view.
7187             if (mItemAnimator != null && !holder.isRecyclable()) {
7188                 mItemAnimator.endAnimation(holder);
7189             }
7190         }
7191 
recycleAndClearCachedViews()7192         void recycleAndClearCachedViews() {
7193             final int count = mCachedViews.size();
7194             for (int i = count - 1; i >= 0; i--) {
7195                 recycleCachedViewAt(i);
7196             }
7197             mCachedViews.clear();
7198             if (ALLOW_THREAD_GAP_WORK) {
7199                 mPrefetchRegistry.clearPrefetchPositions();
7200             }
7201         }
7202 
7203         /**
7204          * Recycles a cached view and removes the view from the list. Views are added to cache
7205          * if and only if they are recyclable, so this method does not check it again.
7206          * <p>
7207          * A small exception to this rule is when the view does not have an animator reference
7208          * but transient state is true (due to animations created outside ItemAnimator). In that
7209          * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
7210          * still recyclable since Adapter wants to do so.
7211          *
7212          * @param cachedViewIndex The index of the view in cached views list
7213          */
recycleCachedViewAt(int cachedViewIndex)7214         void recycleCachedViewAt(int cachedViewIndex) {
7215             if (sVerboseLoggingEnabled) {
7216                 Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
7217             }
7218             ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
7219             if (sVerboseLoggingEnabled) {
7220                 Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
7221             }
7222             addViewHolderToRecycledViewPool(viewHolder, true);
7223             mCachedViews.remove(cachedViewIndex);
7224         }
7225 
7226         /**
7227          * internal implementation checks if view is scrapped or attached and throws an exception
7228          * if so.
7229          * Public version un-scraps before calling recycle.
7230          */
recycleViewHolderInternal(ViewHolder holder)7231         void recycleViewHolderInternal(ViewHolder holder) {
7232             if (holder.isScrap() || holder.itemView.getParent() != null) {
7233                 throw new IllegalArgumentException(
7234                         "Scrapped or attached views may not be recycled. isScrap:"
7235                                 + holder.isScrap() + " isAttached:"
7236                                 + (holder.itemView.getParent() != null) + exceptionLabel());
7237             }
7238 
7239             if (holder.isTmpDetached()) {
7240                 throw new IllegalArgumentException("Tmp detached view should be removed "
7241                         + "from RecyclerView before it can be recycled: " + holder
7242                         + exceptionLabel());
7243             }
7244 
7245             if (holder.shouldIgnore()) {
7246                 throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
7247                         + " should first call stopIgnoringView(view) before calling recycle."
7248                         + exceptionLabel());
7249             }
7250             final boolean transientStatePreventsRecycling = holder
7251                     .doesTransientStatePreventRecycling();
7252             @SuppressWarnings("unchecked") final boolean forceRecycle = mAdapter != null
7253                     && transientStatePreventsRecycling
7254                     && mAdapter.onFailedToRecycleView(holder);
7255             boolean cached = false;
7256             boolean recycled = false;
7257             if (sDebugAssertionsEnabled && mCachedViews.contains(holder)) {
7258                 throw new IllegalArgumentException("cached view received recycle internal? "
7259                         + holder + exceptionLabel());
7260             }
7261             if (forceRecycle || holder.isRecyclable()) {
7262                 if (mViewCacheMax > 0
7263                         && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
7264                         | ViewHolder.FLAG_REMOVED
7265                         | ViewHolder.FLAG_UPDATE
7266                         | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
7267                     // Retire oldest cached view
7268                     int cachedViewSize = mCachedViews.size();
7269                     if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
7270                         recycleCachedViewAt(0);
7271                         cachedViewSize--;
7272                     }
7273 
7274                     int targetCacheIndex = cachedViewSize;
7275                     if (ALLOW_THREAD_GAP_WORK
7276                             && cachedViewSize > 0
7277                             && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
7278                         // when adding the view, skip past most recently prefetched views
7279                         int cacheIndex = cachedViewSize - 1;
7280                         while (cacheIndex >= 0) {
7281                             int cachedPos = mCachedViews.get(cacheIndex).mPosition;
7282                             if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
7283                                 break;
7284                             }
7285                             cacheIndex--;
7286                         }
7287                         targetCacheIndex = cacheIndex + 1;
7288                     }
7289                     mCachedViews.add(targetCacheIndex, holder);
7290                     cached = true;
7291                 }
7292                 if (!cached) {
7293                     addViewHolderToRecycledViewPool(holder, true);
7294                     recycled = true;
7295                 }
7296             } else {
7297                 // NOTE: A view can fail to be recycled when it is scrolled off while an animation
7298                 // runs. In this case, the item is eventually recycled by
7299                 // ItemAnimatorRestoreListener#onAnimationFinished.
7300 
7301                 // TODO: consider cancelling an animation when an item is removed scrollBy,
7302                 // to return it to the pool faster
7303                 if (sVerboseLoggingEnabled) {
7304                     Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
7305                             + "re-visit here. We are still removing it from animation lists"
7306                             + exceptionLabel());
7307                 }
7308             }
7309             // even if the holder is not removed, we still call this method so that it is removed
7310             // from view holder lists.
7311             mViewInfoStore.removeViewHolder(holder);
7312             if (!cached && !recycled && transientStatePreventsRecycling) {
7313                 PoolingContainer.callPoolingContainerOnRelease(holder.itemView);
7314                 holder.mBindingAdapter = null;
7315                 holder.mOwnerRecyclerView = null;
7316             }
7317         }
7318 
7319         /**
7320          * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
7321          *
7322          * Pass false to dispatchRecycled for views that have not been bound.
7323          *
7324          * @param holder           Holder to be added to the pool.
7325          * @param dispatchRecycled True to dispatch View recycled callbacks.
7326          */
addViewHolderToRecycledViewPool(@onNull ViewHolder holder, boolean dispatchRecycled)7327         void addViewHolderToRecycledViewPool(@NonNull ViewHolder holder, boolean dispatchRecycled) {
7328             clearNestedRecyclerViewIfNotNested(holder);
7329             View itemView = holder.itemView;
7330             if (mAccessibilityDelegate != null) {
7331                 AccessibilityDelegateCompat itemDelegate = mAccessibilityDelegate.getItemDelegate();
7332                 AccessibilityDelegateCompat originalDelegate = null;
7333                 if (itemDelegate instanceof RecyclerViewAccessibilityDelegate.ItemDelegate) {
7334                     originalDelegate =
7335                             ((RecyclerViewAccessibilityDelegate.ItemDelegate) itemDelegate)
7336                                     .getAndRemoveOriginalDelegateForItem(itemView);
7337                 }
7338                 // Set the a11y delegate back to whatever the original delegate was.
7339                 ViewCompat.setAccessibilityDelegate(itemView, originalDelegate);
7340             }
7341             if (dispatchRecycled) {
7342                 dispatchViewRecycled(holder);
7343             }
7344             holder.mBindingAdapter = null;
7345             holder.mOwnerRecyclerView = null;
7346             getRecycledViewPool().putRecycledView(holder);
7347         }
7348 
7349         /**
7350          * Used as a fast path for unscrapping and recycling a view during a bulk operation.
7351          * The caller must call {@link #clearScrap()} when it's done to update the recycler's
7352          * internal bookkeeping.
7353          */
quickRecycleScrapView(View view)7354         void quickRecycleScrapView(View view) {
7355             final ViewHolder holder = getChildViewHolderInt(view);
7356             holder.mScrapContainer = null;
7357             holder.mInChangeScrap = false;
7358             holder.clearReturnedFromScrapFlag();
7359             recycleViewHolderInternal(holder);
7360         }
7361 
7362         /**
7363          * Mark an attached view as scrap.
7364          *
7365          * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
7366          * for rebinding and reuse. Requests for a view for a given position may return a
7367          * reused or rebound scrap view instance.</p>
7368          *
7369          * @param view View to scrap
7370          */
scrapView(View view)7371         void scrapView(View view) {
7372             final ViewHolder holder = getChildViewHolderInt(view);
7373             if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
7374                     || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
7375                 if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
7376                     throw new IllegalArgumentException("Called scrap view with an invalid view."
7377                             + " Invalid views cannot be reused from scrap, they should rebound from"
7378                             + " recycler pool." + exceptionLabel());
7379                 }
7380                 holder.setScrapContainer(this, false);
7381                 mAttachedScrap.add(holder);
7382             } else {
7383                 if (mChangedScrap == null) {
7384                     mChangedScrap = new ArrayList<ViewHolder>();
7385                 }
7386                 holder.setScrapContainer(this, true);
7387                 mChangedScrap.add(holder);
7388             }
7389         }
7390 
7391         /**
7392          * Remove a previously scrapped view from the pool of eligible scrap.
7393          *
7394          * <p>This view will no longer be eligible for reuse until re-scrapped or
7395          * until it is explicitly removed and recycled.</p>
7396          */
unscrapView(ViewHolder holder)7397         void unscrapView(ViewHolder holder) {
7398             if (holder.mInChangeScrap) {
7399                 mChangedScrap.remove(holder);
7400             } else {
7401                 mAttachedScrap.remove(holder);
7402             }
7403             holder.mScrapContainer = null;
7404             holder.mInChangeScrap = false;
7405             holder.clearReturnedFromScrapFlag();
7406         }
7407 
getScrapCount()7408         int getScrapCount() {
7409             return mAttachedScrap.size();
7410         }
7411 
getScrapViewAt(int index)7412         View getScrapViewAt(int index) {
7413             return mAttachedScrap.get(index).itemView;
7414         }
7415 
clearScrap()7416         void clearScrap() {
7417             mAttachedScrap.clear();
7418             if (mChangedScrap != null) {
7419                 mChangedScrap.clear();
7420             }
7421         }
7422 
getChangedScrapViewForPosition(int position)7423         ViewHolder getChangedScrapViewForPosition(int position) {
7424             // If pre-layout, check the changed scrap for an exact match.
7425             final int changedScrapSize;
7426             if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
7427                 return null;
7428             }
7429             // find by position
7430             for (int i = 0; i < changedScrapSize; i++) {
7431                 final ViewHolder holder = mChangedScrap.get(i);
7432                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
7433                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
7434                     return holder;
7435                 }
7436             }
7437             // find by id
7438             if (mAdapter.hasStableIds()) {
7439                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
7440                 if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
7441                     final long id = mAdapter.getItemId(offsetPosition);
7442                     for (int i = 0; i < changedScrapSize; i++) {
7443                         final ViewHolder holder = mChangedScrap.get(i);
7444                         if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
7445                             holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
7446                             return holder;
7447                         }
7448                     }
7449                 }
7450             }
7451             return null;
7452         }
7453 
7454         /**
7455          * Returns a view for the position either from attach scrap, hidden children, or cache.
7456          *
7457          * @param position Item position
7458          * @param dryRun   Does a dry run, finds the ViewHolder but does not remove
7459          * @return a ViewHolder that can be re-used for this position.
7460          */
getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun)7461         ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
7462             final int scrapCount = mAttachedScrap.size();
7463 
7464             // Try first for an exact, non-invalid match from scrap.
7465             for (int i = 0; i < scrapCount; i++) {
7466                 final ViewHolder holder = mAttachedScrap.get(i);
7467                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
7468                         && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
7469                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
7470                     return holder;
7471                 }
7472             }
7473 
7474             if (!dryRun) {
7475                 View view = mChildHelper.findHiddenNonRemovedView(position);
7476                 if (view != null) {
7477                     // This View is good to be used. We just need to unhide, detach and move to the
7478                     // scrap list.
7479                     final ViewHolder vh = getChildViewHolderInt(view);
7480                     mChildHelper.unhide(view);
7481                     int layoutIndex = mChildHelper.indexOfChild(view);
7482                     if (layoutIndex == RecyclerView.NO_POSITION) {
7483                         throw new IllegalStateException("layout index should not be -1 after "
7484                                 + "unhiding a view:" + vh + exceptionLabel());
7485                     }
7486                     mChildHelper.detachViewFromParent(layoutIndex);
7487                     scrapView(view);
7488                     vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
7489                             | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
7490                     return vh;
7491                 }
7492             }
7493 
7494             // Search in our first-level recycled view cache.
7495             final int cacheSize = mCachedViews.size();
7496             for (int i = 0; i < cacheSize; i++) {
7497                 final ViewHolder holder = mCachedViews.get(i);
7498                 // invalid view holders may be in cache if adapter has stable ids as they can be
7499                 // retrieved via getScrapOrCachedViewForId
7500                 if (!holder.isInvalid() && holder.getLayoutPosition() == position
7501                         && !holder.isAttachedToTransitionOverlay()) {
7502                     if (!dryRun) {
7503                         mCachedViews.remove(i);
7504                     }
7505                     if (sVerboseLoggingEnabled) {
7506                         Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
7507                                 + ") found match in cache: " + holder);
7508                     }
7509                     return holder;
7510                 }
7511             }
7512             return null;
7513         }
7514 
getScrapOrCachedViewForId(long id, int type, boolean dryRun)7515         ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
7516             // Look in our attached views first
7517             final int count = mAttachedScrap.size();
7518             for (int i = count - 1; i >= 0; i--) {
7519                 final ViewHolder holder = mAttachedScrap.get(i);
7520                 if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
7521                     if (type == holder.getItemViewType()) {
7522                         holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
7523                         if (holder.isRemoved()) {
7524                             // this might be valid in two cases:
7525                             // > item is removed but we are in pre-layout pass
7526                             // >> do nothing. return as is. make sure we don't rebind
7527                             // > item is removed then added to another position and we are in
7528                             // post layout.
7529                             // >> remove removed and invalid flags, add update flag to rebind
7530                             // because item was invisible to us and we don't know what happened in
7531                             // between.
7532                             if (!mState.isPreLayout()) {
7533                                 holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
7534                                         | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
7535                             }
7536                         }
7537                         return holder;
7538                     } else if (!dryRun) {
7539                         // if we are running animations, it is actually better to keep it in scrap
7540                         // but this would force layout manager to lay it out which would be bad.
7541                         // Recycle this scrap. Type mismatch.
7542                         mAttachedScrap.remove(i);
7543                         removeDetachedView(holder.itemView, false);
7544                         quickRecycleScrapView(holder.itemView);
7545                     }
7546                 }
7547             }
7548 
7549             // Search the first-level cache
7550             final int cacheSize = mCachedViews.size();
7551             for (int i = cacheSize - 1; i >= 0; i--) {
7552                 final ViewHolder holder = mCachedViews.get(i);
7553                 if (holder.getItemId() == id && !holder.isAttachedToTransitionOverlay()) {
7554                     if (type == holder.getItemViewType()) {
7555                         if (!dryRun) {
7556                             mCachedViews.remove(i);
7557                         }
7558                         return holder;
7559                     } else if (!dryRun) {
7560                         recycleCachedViewAt(i);
7561                         return null;
7562                     }
7563                 }
7564             }
7565             return null;
7566         }
7567 
7568         @SuppressWarnings("unchecked")
dispatchViewRecycled(@onNull ViewHolder holder)7569         void dispatchViewRecycled(@NonNull ViewHolder holder) {
7570             // TODO: Remove this once setRecyclerListener (currently deprecated) is deleted.
7571             if (mRecyclerListener != null) {
7572                 mRecyclerListener.onViewRecycled(holder);
7573             }
7574 
7575             final int listenerCount = mRecyclerListeners.size();
7576             for (int i = 0; i < listenerCount; i++) {
7577                 mRecyclerListeners.get(i).onViewRecycled(holder);
7578             }
7579             if (mAdapter != null) {
7580                 mAdapter.onViewRecycled(holder);
7581             }
7582             if (mState != null) {
7583                 mViewInfoStore.removeViewHolder(holder);
7584             }
7585             if (sVerboseLoggingEnabled) Log.d(TAG, "dispatchViewRecycled: " + holder);
7586         }
7587 
onAdapterChanged(Adapter<?> oldAdapter, Adapter<?> newAdapter, boolean compatibleWithPrevious)7588         void onAdapterChanged(Adapter<?> oldAdapter, Adapter<?> newAdapter,
7589                 boolean compatibleWithPrevious) {
7590             clear();
7591             poolingContainerDetach(oldAdapter, true);
7592             getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter,
7593                     compatibleWithPrevious);
7594             maybeSendPoolingContainerAttach();
7595         }
7596 
offsetPositionRecordsForMove(int from, int to)7597         void offsetPositionRecordsForMove(int from, int to) {
7598             final int start, end, inBetweenOffset;
7599             if (from < to) {
7600                 start = from;
7601                 end = to;
7602                 inBetweenOffset = -1;
7603             } else {
7604                 start = to;
7605                 end = from;
7606                 inBetweenOffset = 1;
7607             }
7608             final int cachedCount = mCachedViews.size();
7609             for (int i = 0; i < cachedCount; i++) {
7610                 final ViewHolder holder = mCachedViews.get(i);
7611                 if (holder == null || holder.mPosition < start || holder.mPosition > end) {
7612                     continue;
7613                 }
7614                 if (holder.mPosition == from) {
7615                     holder.offsetPosition(to - from, false);
7616                 } else {
7617                     holder.offsetPosition(inBetweenOffset, false);
7618                 }
7619                 if (sVerboseLoggingEnabled) {
7620                     Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
7621                             + holder);
7622                 }
7623             }
7624         }
7625 
offsetPositionRecordsForInsert(int insertedAt, int count)7626         void offsetPositionRecordsForInsert(int insertedAt, int count) {
7627             final int cachedCount = mCachedViews.size();
7628             for (int i = 0; i < cachedCount; i++) {
7629                 final ViewHolder holder = mCachedViews.get(i);
7630                 if (holder != null && holder.mPosition >= insertedAt) {
7631                     if (sVerboseLoggingEnabled) {
7632                         Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder "
7633                                 + holder + " now at position " + (holder.mPosition + count));
7634                     }
7635                     // insertions only affect post layout hence don't apply them to pre-layout.
7636                     holder.offsetPosition(count, false);
7637                 }
7638             }
7639         }
7640 
7641         /**
7642          * @param removedFrom      Remove start index
7643          * @param count            Remove count
7644          * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
7645          *                         false, they'll be applied before the second layout pass
7646          */
offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout)7647         void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
7648             final int removedEnd = removedFrom + count;
7649             final int cachedCount = mCachedViews.size();
7650             for (int i = cachedCount - 1; i >= 0; i--) {
7651                 final ViewHolder holder = mCachedViews.get(i);
7652                 if (holder != null) {
7653                     if (holder.mPosition >= removedEnd) {
7654                         if (sVerboseLoggingEnabled) {
7655                             Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
7656                                     + " holder " + holder + " now at position "
7657                                     + (holder.mPosition - count));
7658                         }
7659                         holder.offsetPosition(-count, applyToPreLayout);
7660                     } else if (holder.mPosition >= removedFrom) {
7661                         // Item for this view was removed. Dump it from the cache.
7662                         holder.addFlags(ViewHolder.FLAG_REMOVED);
7663                         recycleCachedViewAt(i);
7664                     }
7665                 }
7666             }
7667         }
7668 
setViewCacheExtension(ViewCacheExtension extension)7669         void setViewCacheExtension(ViewCacheExtension extension) {
7670             mViewCacheExtension = extension;
7671         }
7672 
setRecycledViewPool(RecycledViewPool pool)7673         void setRecycledViewPool(RecycledViewPool pool) {
7674             poolingContainerDetach(mAdapter);
7675             if (mRecyclerPool != null) {
7676                 mRecyclerPool.detach();
7677             }
7678             mRecyclerPool = pool;
7679             if (mRecyclerPool != null && getAdapter() != null) {
7680                 mRecyclerPool.attach();
7681             }
7682             maybeSendPoolingContainerAttach();
7683         }
7684 
maybeSendPoolingContainerAttach()7685         private void maybeSendPoolingContainerAttach() {
7686             if (mRecyclerPool != null
7687                     && mAdapter != null
7688                     && isAttachedToWindow()) {
7689                 mRecyclerPool.attachForPoolingContainer(mAdapter);
7690             }
7691         }
7692 
poolingContainerDetach(Adapter<?> adapter)7693         private void poolingContainerDetach(Adapter<?> adapter) {
7694             poolingContainerDetach(adapter, false);
7695         }
7696 
poolingContainerDetach(Adapter<?> adapter, boolean isBeingReplaced)7697         private void poolingContainerDetach(Adapter<?> adapter, boolean isBeingReplaced) {
7698             if (mRecyclerPool != null) {
7699                 mRecyclerPool.detachForPoolingContainer(adapter, isBeingReplaced);
7700             }
7701         }
7702 
onAttachedToWindow()7703         void onAttachedToWindow() {
7704             maybeSendPoolingContainerAttach();
7705         }
7706 
onDetachedFromWindow()7707         void onDetachedFromWindow() {
7708             for (int i = 0; i < mCachedViews.size(); i++) {
7709                 PoolingContainer.callPoolingContainerOnRelease(mCachedViews.get(i).itemView);
7710             }
7711             poolingContainerDetach(mAdapter);
7712         }
7713 
getRecycledViewPool()7714         RecycledViewPool getRecycledViewPool() {
7715             if (mRecyclerPool == null) {
7716                 mRecyclerPool = new RecycledViewPool();
7717                 maybeSendPoolingContainerAttach();
7718             }
7719             return mRecyclerPool;
7720         }
7721 
viewRangeUpdate(int positionStart, int itemCount)7722         void viewRangeUpdate(int positionStart, int itemCount) {
7723             final int positionEnd = positionStart + itemCount;
7724             final int cachedCount = mCachedViews.size();
7725             for (int i = cachedCount - 1; i >= 0; i--) {
7726                 final ViewHolder holder = mCachedViews.get(i);
7727                 if (holder == null) {
7728                     continue;
7729                 }
7730 
7731                 final int pos = holder.mPosition;
7732                 if (pos >= positionStart && pos < positionEnd) {
7733                     holder.addFlags(ViewHolder.FLAG_UPDATE);
7734                     recycleCachedViewAt(i);
7735                     // cached views should not be flagged as changed because this will cause them
7736                     // to animate when they are returned from cache.
7737                 }
7738             }
7739         }
7740 
markKnownViewsInvalid()7741         void markKnownViewsInvalid() {
7742             final int cachedCount = mCachedViews.size();
7743             for (int i = 0; i < cachedCount; i++) {
7744                 final ViewHolder holder = mCachedViews.get(i);
7745                 if (holder != null) {
7746                     holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
7747                     holder.addChangePayload(null);
7748                 }
7749             }
7750 
7751             if (mAdapter == null || !mAdapter.hasStableIds()) {
7752                 // we cannot re-use cached views in this case. Recycle them all
7753                 recycleAndClearCachedViews();
7754             }
7755         }
7756 
clearOldPositions()7757         void clearOldPositions() {
7758             final int cachedCount = mCachedViews.size();
7759             for (int i = 0; i < cachedCount; i++) {
7760                 final ViewHolder holder = mCachedViews.get(i);
7761                 holder.clearOldPosition();
7762             }
7763             final int scrapCount = mAttachedScrap.size();
7764             for (int i = 0; i < scrapCount; i++) {
7765                 mAttachedScrap.get(i).clearOldPosition();
7766             }
7767             if (mChangedScrap != null) {
7768                 final int changedScrapCount = mChangedScrap.size();
7769                 for (int i = 0; i < changedScrapCount; i++) {
7770                     mChangedScrap.get(i).clearOldPosition();
7771                 }
7772             }
7773         }
7774 
markItemDecorInsetsDirty()7775         void markItemDecorInsetsDirty() {
7776             final int cachedCount = mCachedViews.size();
7777             for (int i = 0; i < cachedCount; i++) {
7778                 final ViewHolder holder = mCachedViews.get(i);
7779                 LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
7780                 if (layoutParams != null) {
7781                     layoutParams.mInsetsDirty = true;
7782                 }
7783             }
7784         }
7785     }
7786 
7787     /**
7788      * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
7789      * be controlled by the developer.
7790      * <p>
7791      * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
7792      * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
7793      * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
7794      * {@link RecycledViewPool}.
7795      * <p>
7796      * Note that, Recycler never sends Views to this method to be cached. It is developers
7797      * responsibility to decide whether they want to keep their Views in this custom cache or let
7798      * the default recycling policy handle it.
7799      */
7800     public abstract static class ViewCacheExtension {
7801 
7802         /**
7803          * Returns a View that can be binded to the given Adapter position.
7804          * <p>
7805          * This method should <b>not</b> create a new View. Instead, it is expected to return
7806          * an already created View that can be re-used for the given type and position.
7807          * If the View is marked as ignored, it should first call
7808          * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
7809          * <p>
7810          * RecyclerView will re-bind the returned View to the position if necessary.
7811          *
7812          * @param recycler The Recycler that can be used to bind the View
7813          * @param position The adapter position
7814          * @param type     The type of the View, defined by adapter
7815          * @return A View that is bound to the given position or NULL if there is no View to re-use
7816          * @see LayoutManager#ignoreView(View)
7817          */
getViewForPositionAndType(@onNull Recycler recycler, int position, int type)7818         public abstract @Nullable View getViewForPositionAndType(@NonNull Recycler recycler,
7819                 int position, int type);
7820     }
7821 
7822     /**
7823      * Base class for an Adapter
7824      *
7825      * <p>Adapters provide a binding from an app-specific data set to views that are displayed
7826      * within a {@link RecyclerView}.</p>
7827      *
7828      * @param <VH> A class that extends ViewHolder that will be used by the adapter.
7829      */
7830     public abstract static class Adapter<VH extends ViewHolder> {
7831         private final AdapterDataObservable mObservable = new AdapterDataObservable();
7832         private boolean mHasStableIds = false;
7833         private StateRestorationPolicy mStateRestorationPolicy = StateRestorationPolicy.ALLOW;
7834 
7835         /**
7836          * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
7837          * an item.
7838          * <p>
7839          * This new ViewHolder should be constructed with a new View that can represent the items
7840          * of the given type. You can either create a new View manually or inflate it from an XML
7841          * layout file.
7842          * <p>
7843          * The new ViewHolder will be used to display items of the adapter using
7844          * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
7845          * different items in the data set, it is a good idea to cache references to sub views of
7846          * the View to avoid unnecessary {@link View#findViewById(int)} calls.
7847          *
7848          * @param parent   The ViewGroup into which the new View will be added after it is bound to
7849          *                 an adapter position.
7850          * @param viewType The view type of the new View.
7851          * @return A new ViewHolder that holds a View of the given view type.
7852          * @see #getItemViewType(int)
7853          * @see #onBindViewHolder(ViewHolder, int)
7854          */
onCreateViewHolder(@onNull ViewGroup parent, int viewType)7855         public abstract @NonNull VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
7856 
7857         /**
7858          * Called by RecyclerView to display the data at the specified position. This method should
7859          * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
7860          * position.
7861          * <p>
7862          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
7863          * again if the position of the item changes in the data set unless the item itself is
7864          * invalidated or the new position cannot be determined. For this reason, you should only
7865          * use the <code>position</code> parameter while acquiring the related data item inside
7866          * this method and should not keep a copy of it. If you need the position of an item later
7867          * on (e.g. in a click listener), use {@link ViewHolder#getBindingAdapterPosition()} which
7868          * will have the updated adapter position.
7869          *
7870          * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
7871          * handle efficient partial bind.
7872          *
7873          * @param holder   The ViewHolder which should be updated to represent the contents of the
7874          *                 item at the given position in the data set.
7875          * @param position The position of the item within the adapter's data set.
7876          */
onBindViewHolder(@onNull VH holder, int position)7877         public abstract void onBindViewHolder(@NonNull VH holder, int position);
7878 
7879         /**
7880          * Called by RecyclerView to display the data at the specified position. This method
7881          * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
7882          * the given position.
7883          * <p>
7884          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
7885          * again if the position of the item changes in the data set unless the item itself is
7886          * invalidated or the new position cannot be determined. For this reason, you should only
7887          * use the <code>position</code> parameter while acquiring the related data item inside
7888          * this method and should not keep a copy of it. If you need the position of an item later
7889          * on (e.g. in a click listener), use {@link ViewHolder#getBindingAdapterPosition()} which
7890          * will have the updated adapter position.
7891          * <p>
7892          * Partial bind vs full bind:
7893          * <p>
7894          * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
7895          * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
7896          * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
7897          * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
7898          * Adapter should not assume that the payload passed in notify methods will be received by
7899          * onBindViewHolder().  For example when the view is not attached to the screen, the
7900          * payload in notifyItemChange() will be simply dropped.
7901          *
7902          * @param holder   The ViewHolder which should be updated to represent the contents of the
7903          *                 item at the given position in the data set.
7904          * @param position The position of the item within the adapter's data set.
7905          * @param payloads A non-null list of merged payloads. Can be empty list if requires full
7906          *                 update.
7907          */
onBindViewHolder(@onNull VH holder, int position, @NonNull List<Object> payloads)7908         public void onBindViewHolder(@NonNull VH holder, int position,
7909                 @NonNull List<Object> payloads) {
7910             onBindViewHolder(holder, position);
7911         }
7912 
7913         /**
7914          * Returns the position of the given {@link ViewHolder} in the given {@link Adapter}.
7915          *
7916          * If the given {@link Adapter} is not part of this {@link Adapter},
7917          * {@link RecyclerView#NO_POSITION} is returned.
7918          *
7919          * @param adapter    The adapter which is a sub adapter of this adapter or itself.
7920          * @param viewHolder The ViewHolder whose local position in the given adapter will be
7921          *                   returned.
7922          * @param localPosition The position of the given {@link ViewHolder} in this
7923          * {@link Adapter}.
7924          *
7925          * @return The local position of the given {@link ViewHolder} in this {@link Adapter}
7926          * or {@link RecyclerView#NO_POSITION} if the {@link ViewHolder} is not bound to an item
7927          * or the given {@link Adapter} is not part of this Adapter (if this Adapter merges other
7928          * adapters).
7929          */
findRelativeAdapterPositionIn( @onNull Adapter<? extends ViewHolder> adapter, @NonNull ViewHolder viewHolder, int localPosition )7930         public int findRelativeAdapterPositionIn(
7931                 @NonNull Adapter<? extends ViewHolder> adapter,
7932                 @NonNull ViewHolder viewHolder,
7933                 int localPosition
7934         ) {
7935             if (adapter == this) {
7936                 return localPosition;
7937             }
7938             return NO_POSITION;
7939         }
7940 
7941         /**
7942          * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
7943          * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
7944          *
7945          * @see #onCreateViewHolder(ViewGroup, int)
7946          */
createViewHolder(@onNull ViewGroup parent, int viewType)7947         public final @NonNull VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
7948             try {
7949                 if (TraceCompat.isEnabled()) {
7950                     Trace.beginSection(String.format("RV onCreateViewHolder type=0x%X", viewType));
7951                 }
7952                 final VH holder = onCreateViewHolder(parent, viewType);
7953                 if (holder.itemView.getParent() != null) {
7954                     throw new IllegalStateException("ViewHolder views must not be attached when"
7955                             + " created. Ensure that you are not passing 'true' to the attachToRoot"
7956                             + " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
7957                 }
7958                 holder.mItemViewType = viewType;
7959                 return holder;
7960             } finally {
7961                 Trace.endSection();
7962             }
7963         }
7964 
7965         /**
7966          * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
7967          * {@link ViewHolder} contents with the item at the given position and also sets up some
7968          * private fields to be used by RecyclerView.
7969          *
7970          * Adapters that merge other adapters should use
7971          * {@link #bindViewHolder(ViewHolder, int)} when calling nested adapters so that
7972          * RecyclerView can track which adapter bound the {@link ViewHolder} to return the correct
7973          * position from {@link ViewHolder#getBindingAdapterPosition()} method.
7974          * They should also override
7975          * the {@link #findRelativeAdapterPositionIn(Adapter, ViewHolder, int)} method.
7976          *
7977          * @param holder   The view holder whose contents should be updated
7978          * @param position The position of the holder with respect to this adapter
7979          * @see #onBindViewHolder(ViewHolder, int)
7980          */
bindViewHolder(@onNull VH holder, int position)7981         public final void bindViewHolder(@NonNull VH holder, int position) {
7982             boolean rootBind = holder.mBindingAdapter == null;
7983             if (rootBind) {
7984                 holder.mPosition = position;
7985                 if (hasStableIds()) {
7986                     holder.mItemId = getItemId(position);
7987                 }
7988                 holder.setFlags(ViewHolder.FLAG_BOUND,
7989                         ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
7990                                 | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
7991                 if (TraceCompat.isEnabled()) {
7992                     // Note: we only trace when rootBind=true to avoid duplicate trace sections
7993                     Trace.beginSection(
7994                             String.format("RV onBindViewHolder type=0x%X", holder.mItemViewType)
7995                     );
7996                 }
7997             }
7998             holder.mBindingAdapter = this;
7999             if (sDebugAssertionsEnabled) {
8000                 if (holder.itemView.getParent() == null
8001                         && (holder.itemView.isAttachedToWindow()
8002                         != holder.isTmpDetached())) {
8003                     throw new IllegalStateException("Temp-detached state out of sync with reality. "
8004                             + "holder.isTmpDetached(): " + holder.isTmpDetached()
8005                             + ", attached to window: "
8006                             + holder.itemView.isAttachedToWindow()
8007                             + ", holder: " + holder);
8008                 }
8009                 if (holder.itemView.getParent() == null
8010                         && holder.itemView.isAttachedToWindow()) {
8011                     throw new IllegalStateException(
8012                             "Attempting to bind attached holder with no parent"
8013                                     + " (AKA temp detached): " + holder);
8014                 }
8015             }
8016             onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
8017             if (rootBind) {
8018                 holder.clearPayload();
8019                 final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
8020                 if (layoutParams instanceof RecyclerView.LayoutParams) {
8021                     ((LayoutParams) layoutParams).mInsetsDirty = true;
8022                 }
8023                 Trace.endSection();
8024             }
8025         }
8026 
8027         /**
8028          * Return the view type of the item at <code>position</code> for the purposes
8029          * of view recycling.
8030          *
8031          * <p>The default implementation of this method returns 0, making the assumption of
8032          * a single view type for the adapter. Unlike ListView adapters, types need not
8033          * be contiguous. Consider using id resources to uniquely identify item view types.
8034          *
8035          * @param position position to query
8036          * @return integer value identifying the type of the view needed to represent the item at
8037          * <code>position</code>. Type codes need not be contiguous.
8038          */
getItemViewType(int position)8039         public int getItemViewType(int position) {
8040             return 0;
8041         }
8042 
8043         /**
8044          * Indicates whether each item in the data set can be represented with a unique identifier
8045          * of type {@link java.lang.Long}.
8046          *
8047          * @param hasStableIds Whether items in data set have unique identifiers or not.
8048          * @see #hasStableIds()
8049          * @see #getItemId(int)
8050          */
setHasStableIds(boolean hasStableIds)8051         public void setHasStableIds(boolean hasStableIds) {
8052             if (hasObservers()) {
8053                 throw new IllegalStateException("Cannot change whether this adapter has "
8054                         + "stable IDs while the adapter has registered observers.");
8055             }
8056             mHasStableIds = hasStableIds;
8057         }
8058 
8059         /**
8060          * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
8061          * would return false this method should return {@link #NO_ID}. The default implementation
8062          * of this method returns {@link #NO_ID}.
8063          *
8064          * @param position Adapter position to query
8065          * @return the stable ID of the item at position
8066          */
getItemId(int position)8067         public long getItemId(int position) {
8068             return NO_ID;
8069         }
8070 
8071         /**
8072          * Returns the total number of items in the data set held by the adapter.
8073          *
8074          * @return The total number of items in this adapter.
8075          */
getItemCount()8076         public abstract int getItemCount();
8077 
8078         /**
8079          * Returns true if this adapter publishes a unique <code>long</code> value that can
8080          * act as a key for the item at a given position in the data set. If that item is relocated
8081          * in the data set, the ID returned for that item should be the same.
8082          *
8083          * @return true if this adapter's items have stable IDs
8084          */
hasStableIds()8085         public final boolean hasStableIds() {
8086             return mHasStableIds;
8087         }
8088 
8089         /**
8090          * Called when a view created by this adapter has been recycled.
8091          *
8092          * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
8093          * needs to be attached to its parent {@link RecyclerView}. This can be because it has
8094          * fallen out of visibility or a set of cached views represented by views still
8095          * attached to the parent RecyclerView. If an item view has large or expensive data
8096          * bound to it such as large bitmaps, this may be a good place to release those
8097          * resources.</p>
8098          * <p>
8099          * RecyclerView calls this method right before clearing ViewHolder's internal data and
8100          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
8101          * before being recycled, you can call {@link ViewHolder#getBindingAdapterPosition()} to get
8102          * its adapter position.
8103          *
8104          * @param holder The ViewHolder for the view being recycled
8105          */
onViewRecycled(@onNull VH holder)8106         public void onViewRecycled(@NonNull VH holder) {
8107         }
8108 
8109         /**
8110          * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
8111          * due to its transient state. Upon receiving this callback, Adapter can clear the
8112          * animation(s) that effect the View's transient state and return <code>true</code> so that
8113          * the View can be recycled. Keep in mind that the View in question is already removed from
8114          * the RecyclerView.
8115          * <p>
8116          * In some cases, it is acceptable to recycle a View although it has transient state. Most
8117          * of the time, this is a case where the transient state will be cleared in
8118          * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
8119          * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
8120          * value of this method to decide whether the View should be recycled or not.
8121          * <p>
8122          * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
8123          * should never receive this callback because RecyclerView keeps those Views as children
8124          * until their animations are complete. This callback is useful when children of the item
8125          * views create animations which may not be easy to implement using an {@link ItemAnimator}.
8126          * <p>
8127          * You should <em>never</em> fix this issue by calling
8128          * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
8129          * <code>holder.itemView.setHasTransientState(true);</code>. Each
8130          * <code>View.setHasTransientState(true)</code> call must be matched by a
8131          * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
8132          * may become inconsistent. You should always prefer to end or cancel animations that are
8133          * triggering the transient state instead of handling it manually.
8134          *
8135          * @param holder The ViewHolder containing the View that could not be recycled due to its
8136          *               transient state.
8137          * @return True if the View should be recycled, false otherwise. Note that if this method
8138          * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
8139          * the View and recycle it regardless. If this method returns <code>false</code>,
8140          * RecyclerView will check the View's transient state again before giving a final decision.
8141          * Default implementation returns false.
8142          */
onFailedToRecycleView(@onNull VH holder)8143         public boolean onFailedToRecycleView(@NonNull VH holder) {
8144             return false;
8145         }
8146 
8147         /**
8148          * Called when a view created by this adapter has been attached to a window.
8149          *
8150          * <p>This can be used as a reasonable signal that the view is about to be seen
8151          * by the user. If the adapter previously freed any resources in
8152          * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
8153          * those resources should be restored here.</p>
8154          *
8155          * @param holder Holder of the view being attached
8156          */
onViewAttachedToWindow(@onNull VH holder)8157         public void onViewAttachedToWindow(@NonNull VH holder) {
8158         }
8159 
8160         /**
8161          * Called when a view created by this adapter has been detached from its window.
8162          *
8163          * <p>Becoming detached from the window is not necessarily a permanent condition;
8164          * the consumer of an Adapter's views may choose to cache views offscreen while they
8165          * are not visible, attaching and detaching them as appropriate.</p>
8166          *
8167          * @param holder Holder of the view being detached
8168          */
onViewDetachedFromWindow(@onNull VH holder)8169         public void onViewDetachedFromWindow(@NonNull VH holder) {
8170         }
8171 
8172         /**
8173          * Returns true if one or more observers are attached to this adapter.
8174          *
8175          * @return true if this adapter has observers
8176          */
hasObservers()8177         public final boolean hasObservers() {
8178             return mObservable.hasObservers();
8179         }
8180 
8181         /**
8182          * Register a new observer to listen for data changes.
8183          *
8184          * <p>The adapter may publish a variety of events describing specific changes.
8185          * Not all adapters may support all change types and some may fall back to a generic
8186          * {@link RecyclerView.AdapterDataObserver#onChanged()
8187          * "something changed"} event if more specific data is not available.</p>
8188          *
8189          * <p>Components registering observers with an adapter are responsible for
8190          * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
8191          * unregistering} those observers when finished.</p>
8192          *
8193          * @param observer Observer to register
8194          * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
8195          */
registerAdapterDataObserver(@onNull AdapterDataObserver observer)8196         public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
8197             mObservable.registerObserver(observer);
8198         }
8199 
8200         /**
8201          * Unregister an observer currently listening for data changes.
8202          *
8203          * <p>The unregistered observer will no longer receive events about changes
8204          * to the adapter.</p>
8205          *
8206          * @param observer Observer to unregister
8207          * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
8208          */
unregisterAdapterDataObserver(@onNull AdapterDataObserver observer)8209         public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {
8210             mObservable.unregisterObserver(observer);
8211         }
8212 
8213         /**
8214          * Called by RecyclerView when it starts observing this Adapter.
8215          * <p>
8216          * Keep in mind that same adapter may be observed by multiple RecyclerViews.
8217          *
8218          * @param recyclerView The RecyclerView instance which started observing this adapter.
8219          * @see #onDetachedFromRecyclerView(RecyclerView)
8220          */
onAttachedToRecyclerView(@onNull RecyclerView recyclerView)8221         public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
8222         }
8223 
8224         /**
8225          * Called by RecyclerView when it stops observing this Adapter.
8226          *
8227          * @param recyclerView The RecyclerView instance which stopped observing this adapter.
8228          * @see #onAttachedToRecyclerView(RecyclerView)
8229          */
onDetachedFromRecyclerView(@onNull RecyclerView recyclerView)8230         public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
8231         }
8232 
8233         /**
8234          * Notify any registered observers that the data set has changed.
8235          *
8236          * <p>There are two different classes of data change events, item changes and structural
8237          * changes. Item changes are when a single item has its data updated but no positional
8238          * changes have occurred. Structural changes are when items are inserted, removed or moved
8239          * within the data set.</p>
8240          *
8241          * <p>This event does not specify what about the data set has changed, forcing
8242          * any observers to assume that all existing items and structure may no longer be valid.
8243          * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
8244          *
8245          * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
8246          * for adapters that report that they have {@link #hasStableIds() stable IDs} when
8247          * this method is used. This can help for the purposes of animation and visual
8248          * object persistence but individual item views will still need to be rebound
8249          * and relaid out.</p>
8250          *
8251          * <p>If you are writing an adapter it will always be more efficient to use the more
8252          * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
8253          * as a last resort.</p>
8254          *
8255          * @see #notifyItemChanged(int)
8256          * @see #notifyItemInserted(int)
8257          * @see #notifyItemRemoved(int)
8258          * @see #notifyItemRangeChanged(int, int)
8259          * @see #notifyItemRangeInserted(int, int)
8260          * @see #notifyItemRangeRemoved(int, int)
8261          */
notifyDataSetChanged()8262         public final void notifyDataSetChanged() {
8263             mObservable.notifyChanged();
8264         }
8265 
8266         /**
8267          * Notify any registered observers that the item at <code>position</code> has changed.
8268          * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
8269          *
8270          * <p>This is an item change event, not a structural change event. It indicates that any
8271          * reflection of the data at <code>position</code> is out of date and should be updated.
8272          * The item at <code>position</code> retains the same identity.</p>
8273          *
8274          * @param position Position of the item that has changed
8275          * @see #notifyItemRangeChanged(int, int)
8276          */
notifyItemChanged(int position)8277         public final void notifyItemChanged(int position) {
8278             mObservable.notifyItemRangeChanged(position, 1);
8279         }
8280 
8281         /**
8282          * Notify any registered observers that the item at <code>position</code> has changed with
8283          * an optional payload object.
8284          *
8285          * <p>This is an item change event, not a structural change event. It indicates that any
8286          * reflection of the data at <code>position</code> is out of date and should be updated.
8287          * The item at <code>position</code> retains the same identity.
8288          * </p>
8289          *
8290          * <p>
8291          * Client can optionally pass a payload for partial change. These payloads will be merged
8292          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
8293          * item is already represented by a ViewHolder and it will be rebound to the same
8294          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
8295          * payloads on that item and prevent future payload until
8296          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
8297          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
8298          * attached, the payload will be simply dropped.
8299          *
8300          * @param position Position of the item that has changed
8301          * @param payload  Optional parameter, use null to identify a "full" update
8302          * @see #notifyItemRangeChanged(int, int)
8303          */
notifyItemChanged(int position, @Nullable Object payload)8304         public final void notifyItemChanged(int position, @Nullable Object payload) {
8305             mObservable.notifyItemRangeChanged(position, 1, payload);
8306         }
8307 
8308         /**
8309          * Notify any registered observers that the <code>itemCount</code> items starting at
8310          * position <code>positionStart</code> have changed.
8311          * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
8312          *
8313          * <p>This is an item change event, not a structural change event. It indicates that
8314          * any reflection of the data in the given position range is out of date and should
8315          * be updated. The items in the given range retain the same identity.</p>
8316          *
8317          * @param positionStart Position of the first item that has changed
8318          * @param itemCount     Number of items that have changed
8319          * @see #notifyItemChanged(int)
8320          */
notifyItemRangeChanged(int positionStart, int itemCount)8321         public final void notifyItemRangeChanged(int positionStart, int itemCount) {
8322             mObservable.notifyItemRangeChanged(positionStart, itemCount);
8323         }
8324 
8325         /**
8326          * Notify any registered observers that the <code>itemCount</code> items starting at
8327          * position <code>positionStart</code> have changed. An optional payload can be
8328          * passed to each changed item.
8329          *
8330          * <p>This is an item change event, not a structural change event. It indicates that any
8331          * reflection of the data in the given position range is out of date and should be updated.
8332          * The items in the given range retain the same identity.
8333          * </p>
8334          *
8335          * <p>
8336          * Client can optionally pass a payload for partial change. These payloads will be merged
8337          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
8338          * item is already represented by a ViewHolder and it will be rebound to the same
8339          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
8340          * payloads on that item and prevent future payload until
8341          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
8342          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
8343          * attached, the payload will be simply dropped.
8344          *
8345          * @param positionStart Position of the first item that has changed
8346          * @param itemCount     Number of items that have changed
8347          * @param payload       Optional parameter, use null to identify a "full" update
8348          * @see #notifyItemChanged(int)
8349          */
notifyItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload)8350         public final void notifyItemRangeChanged(int positionStart, int itemCount,
8351                 @Nullable Object payload) {
8352             mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
8353         }
8354 
8355         /**
8356          * Notify any registered observers that the item reflected at <code>position</code>
8357          * has been newly inserted. The item previously at <code>position</code> is now at
8358          * position <code>position + 1</code>.
8359          *
8360          * <p>This is a structural change event. Representations of other existing items in the
8361          * data set are still considered up to date and will not be rebound, though their
8362          * positions may be altered.</p>
8363          *
8364          * @param position Position of the newly inserted item in the data set
8365          * @see #notifyItemRangeInserted(int, int)
8366          */
notifyItemInserted(int position)8367         public final void notifyItemInserted(int position) {
8368             mObservable.notifyItemRangeInserted(position, 1);
8369         }
8370 
8371         /**
8372          * Notify any registered observers that the item reflected at <code>fromPosition</code>
8373          * has been moved to <code>toPosition</code>.
8374          *
8375          * <p>This is a structural change event. Representations of other existing items in the
8376          * data set are still considered up to date and will not be rebound, though their
8377          * positions may be altered.</p>
8378          *
8379          * @param fromPosition Previous position of the item.
8380          * @param toPosition   New position of the item.
8381          */
notifyItemMoved(int fromPosition, int toPosition)8382         public final void notifyItemMoved(int fromPosition, int toPosition) {
8383             mObservable.notifyItemMoved(fromPosition, toPosition);
8384         }
8385 
8386         /**
8387          * Notify any registered observers that the currently reflected <code>itemCount</code>
8388          * items starting at <code>positionStart</code> have been newly inserted. The items
8389          * previously located at <code>positionStart</code> and beyond can now be found starting
8390          * at position <code>positionStart + itemCount</code>.
8391          *
8392          * <p>This is a structural change event. Representations of other existing items in the
8393          * data set are still considered up to date and will not be rebound, though their positions
8394          * may be altered.</p>
8395          *
8396          * @param positionStart Position of the first item that was inserted
8397          * @param itemCount     Number of items inserted
8398          * @see #notifyItemInserted(int)
8399          */
notifyItemRangeInserted(int positionStart, int itemCount)8400         public final void notifyItemRangeInserted(int positionStart, int itemCount) {
8401             mObservable.notifyItemRangeInserted(positionStart, itemCount);
8402         }
8403 
8404         /**
8405          * Notify any registered observers that the item previously located at <code>position</code>
8406          * has been removed from the data set. The items previously located at and after
8407          * <code>position</code> may now be found at <code>oldPosition - 1</code>.
8408          *
8409          * <p>This is a structural change event. Representations of other existing items in the
8410          * data set are still considered up to date and will not be rebound, though their positions
8411          * may be altered.</p>
8412          *
8413          * @param position Position of the item that has now been removed
8414          * @see #notifyItemRangeRemoved(int, int)
8415          */
notifyItemRemoved(int position)8416         public final void notifyItemRemoved(int position) {
8417             mObservable.notifyItemRangeRemoved(position, 1);
8418         }
8419 
8420         /**
8421          * Notify any registered observers that the <code>itemCount</code> items previously
8422          * located at <code>positionStart</code> have been removed from the data set. The items
8423          * previously located at and after <code>positionStart + itemCount</code> may now be found
8424          * at <code>oldPosition - itemCount</code>.
8425          *
8426          * <p>This is a structural change event. Representations of other existing items in the data
8427          * set are still considered up to date and will not be rebound, though their positions
8428          * may be altered.</p>
8429          *
8430          * @param positionStart Previous position of the first item that was removed
8431          * @param itemCount     Number of items removed from the data set
8432          */
notifyItemRangeRemoved(int positionStart, int itemCount)8433         public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
8434             mObservable.notifyItemRangeRemoved(positionStart, itemCount);
8435         }
8436 
8437         /**
8438          * Sets the state restoration strategy for the Adapter.
8439          *
8440          * By default, it is set to {@link StateRestorationPolicy#ALLOW} which means RecyclerView
8441          * expects any set Adapter to be immediately capable of restoring the RecyclerView's saved
8442          * scroll position.
8443          * <p>
8444          * This behaviour might be undesired if the Adapter's data is loaded asynchronously, and
8445          * thus unavailable during initial layout (e.g. after Activity rotation). To avoid losing
8446          * scroll position, you can change this to be either
8447          * {@link StateRestorationPolicy#PREVENT_WHEN_EMPTY} or
8448          * {@link StateRestorationPolicy#PREVENT}.
8449          * Note that the former means your RecyclerView will restore state as soon as Adapter has
8450          * 1 or more items while the latter requires you to call
8451          * {@link #setStateRestorationPolicy(StateRestorationPolicy)} with either
8452          * {@link StateRestorationPolicy#ALLOW} or
8453          * {@link StateRestorationPolicy#PREVENT_WHEN_EMPTY} again when the Adapter is
8454          * ready to restore its state.
8455          * <p>
8456          * RecyclerView will still layout even when State restoration is disabled. The behavior of
8457          * how State is restored is up to the {@link LayoutManager}. All default LayoutManagers
8458          * will override current state with restored state when state restoration happens (unless
8459          * an explicit call to {@link LayoutManager#scrollToPosition(int)} is made).
8460          * <p>
8461          * Calling this method after state is restored will not have any effect other than changing
8462          * the return value of {@link #getStateRestorationPolicy()}.
8463          *
8464          * @param strategy The saved state restoration strategy for this Adapter.
8465          * @see #getStateRestorationPolicy()
8466          */
setStateRestorationPolicy(@onNull StateRestorationPolicy strategy)8467         public void setStateRestorationPolicy(@NonNull StateRestorationPolicy strategy) {
8468             mStateRestorationPolicy = strategy;
8469             mObservable.notifyStateRestorationPolicyChanged();
8470         }
8471 
8472         /**
8473          * Returns when this Adapter wants to restore the state.
8474          *
8475          * @return The current {@link StateRestorationPolicy} for this Adapter. Defaults to
8476          * {@link StateRestorationPolicy#ALLOW}.
8477          * @see #setStateRestorationPolicy(StateRestorationPolicy)
8478          */
getStateRestorationPolicy()8479         public final @NonNull StateRestorationPolicy getStateRestorationPolicy() {
8480             return mStateRestorationPolicy;
8481         }
8482 
8483         /**
8484          * Called by the RecyclerView to decide whether the SavedState should be given to the
8485          * LayoutManager or not.
8486          *
8487          * @return {@code true} if the Adapter is ready to restore its state, {@code false}
8488          * otherwise.
8489          */
canRestoreState()8490         boolean canRestoreState() {
8491             switch (mStateRestorationPolicy) {
8492                 case PREVENT:
8493                     return false;
8494                 case PREVENT_WHEN_EMPTY:
8495                     return getItemCount() > 0;
8496                 default:
8497                     return true;
8498             }
8499         }
8500 
8501         /**
8502          * Defines how this Adapter wants to restore its state after a view reconstruction (e.g.
8503          * configuration change).
8504          */
8505         public enum StateRestorationPolicy {
8506             /**
8507              * Adapter is ready to restore State immediately, RecyclerView will provide the state
8508              * to the LayoutManager in the next layout pass.
8509              */
8510             ALLOW,
8511             /**
8512              * Adapter is ready to restore State when it has more than 0 items. RecyclerView will
8513              * provide the state to the LayoutManager as soon as the Adapter has 1 or more items.
8514              */
8515             PREVENT_WHEN_EMPTY,
8516             /**
8517              * RecyclerView will not restore the state for the Adapter until a call to
8518              * {@link #setStateRestorationPolicy(StateRestorationPolicy)} is made with either
8519              * {@link #ALLOW} or {@link #PREVENT_WHEN_EMPTY}.
8520              */
8521             PREVENT
8522         }
8523     }
8524 
8525     @SuppressWarnings("unchecked")
dispatchChildDetached(View child)8526     void dispatchChildDetached(View child) {
8527         final ViewHolder viewHolder = getChildViewHolderInt(child);
8528         onChildDetachedFromWindow(child);
8529         if (mAdapter != null && viewHolder != null) {
8530             mAdapter.onViewDetachedFromWindow(viewHolder);
8531         }
8532         if (mOnChildAttachStateListeners != null) {
8533             final int cnt = mOnChildAttachStateListeners.size();
8534             for (int i = cnt - 1; i >= 0; i--) {
8535                 mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
8536             }
8537         }
8538     }
8539 
8540     @SuppressWarnings("unchecked")
dispatchChildAttached(View child)8541     void dispatchChildAttached(View child) {
8542         final ViewHolder viewHolder = getChildViewHolderInt(child);
8543         onChildAttachedToWindow(child);
8544         if (mAdapter != null && viewHolder != null) {
8545             mAdapter.onViewAttachedToWindow(viewHolder);
8546         }
8547         if (mOnChildAttachStateListeners != null) {
8548             final int cnt = mOnChildAttachStateListeners.size();
8549             for (int i = cnt - 1; i >= 0; i--) {
8550                 mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
8551             }
8552         }
8553     }
8554 
8555     /**
8556      * A <code>LayoutManager</code> is responsible for measuring and positioning item views
8557      * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
8558      * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
8559      * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
8560      * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
8561      * layout managers are provided for general use.
8562      * <p/>
8563      * If the LayoutManager specifies a default constructor or one with the signature
8564      * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
8565      * instantiate and set the LayoutManager when being inflated. Most used properties can
8566      * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
8567      * a LayoutManager specifies both constructors, the non-default constructor will take
8568      * precedence.
8569      */
8570     public abstract static class LayoutManager {
8571         ChildHelper mChildHelper;
8572         RecyclerView mRecyclerView;
8573 
8574         /**
8575          * The callback used for retrieving information about a RecyclerView and its children in the
8576          * horizontal direction.
8577          */
8578         private final ViewBoundsCheck.Callback mHorizontalBoundCheckCallback =
8579                 new ViewBoundsCheck.Callback() {
8580                     @Override
8581                     public View getChildAt(int index) {
8582                         return LayoutManager.this.getChildAt(index);
8583                     }
8584 
8585                     @Override
8586                     public int getParentStart() {
8587                         return LayoutManager.this.getPaddingLeft();
8588                     }
8589 
8590                     @Override
8591                     public int getParentEnd() {
8592                         return LayoutManager.this.getWidth() - LayoutManager.this.getPaddingRight();
8593                     }
8594 
8595                     @Override
8596                     public int getChildStart(View view) {
8597                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
8598                                 view.getLayoutParams();
8599                         return LayoutManager.this.getDecoratedLeft(view) - params.leftMargin;
8600                     }
8601 
8602                     @Override
8603                     public int getChildEnd(View view) {
8604                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
8605                                 view.getLayoutParams();
8606                         return LayoutManager.this.getDecoratedRight(view) + params.rightMargin;
8607                     }
8608                 };
8609 
8610         /**
8611          * The callback used for retrieving information about a RecyclerView and its children in the
8612          * vertical direction.
8613          */
8614         private final ViewBoundsCheck.Callback mVerticalBoundCheckCallback =
8615                 new ViewBoundsCheck.Callback() {
8616                     @Override
8617                     public View getChildAt(int index) {
8618                         return LayoutManager.this.getChildAt(index);
8619                     }
8620 
8621                     @Override
8622                     public int getParentStart() {
8623                         return LayoutManager.this.getPaddingTop();
8624                     }
8625 
8626                     @Override
8627                     public int getParentEnd() {
8628                         return LayoutManager.this.getHeight()
8629                                 - LayoutManager.this.getPaddingBottom();
8630                     }
8631 
8632                     @Override
8633                     public int getChildStart(View view) {
8634                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
8635                                 view.getLayoutParams();
8636                         return LayoutManager.this.getDecoratedTop(view) - params.topMargin;
8637                     }
8638 
8639                     @Override
8640                     public int getChildEnd(View view) {
8641                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
8642                                 view.getLayoutParams();
8643                         return LayoutManager.this.getDecoratedBottom(view) + params.bottomMargin;
8644                     }
8645                 };
8646 
8647         /**
8648          * Utility objects used to check the boundaries of children against their parent
8649          * RecyclerView.
8650          *
8651          * @see #isViewPartiallyVisible(View, boolean, boolean),
8652          * {@link LinearLayoutManager#findOneVisibleChild(int, int, boolean, boolean)},
8653          * and {@link LinearLayoutManager#findOnePartiallyOrCompletelyInvisibleChild(int, int)}.
8654          */
8655         ViewBoundsCheck mHorizontalBoundCheck = new ViewBoundsCheck(mHorizontalBoundCheckCallback);
8656         ViewBoundsCheck mVerticalBoundCheck = new ViewBoundsCheck(mVerticalBoundCheckCallback);
8657 
8658         @Nullable SmoothScroller mSmoothScroller;
8659 
8660         boolean mRequestedSimpleAnimations = false;
8661 
8662         boolean mIsAttachedToWindow = false;
8663 
8664         /**
8665          * This field is only set via the deprecated {@link #setAutoMeasureEnabled(boolean)} and is
8666          * only accessed via {@link #isAutoMeasureEnabled()} for backwards compatability reasons.
8667          */
8668         boolean mAutoMeasure = false;
8669 
8670         /**
8671          * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
8672          * if the space that will be given to it is already larger than what it has measured before.
8673          */
8674         private boolean mMeasurementCacheEnabled = true;
8675 
8676         private boolean mItemPrefetchEnabled = true;
8677 
8678         /**
8679          * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
8680          * requested by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)} or
8681          * {@link #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)} call.
8682          *
8683          * If expanded by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)},
8684          * will be reset upon layout to prevent initial prefetches (often large, since they're
8685          * proportional to expected child count) from expanding cache permanently.
8686          */
8687         int mPrefetchMaxCountObserved;
8688 
8689         /**
8690          * If true, mPrefetchMaxCountObserved is only valid until next layout, and should be reset.
8691          */
8692         boolean mPrefetchMaxObservedInInitialPrefetch;
8693 
8694         /**
8695          * These measure specs might be the measure specs that were passed into RecyclerView's
8696          * onMeasure method OR fake measure specs created by the RecyclerView.
8697          * For example, when a layout is run, RecyclerView always sets these specs to be
8698          * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
8699          * <p>
8700          * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
8701          * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
8702          * corrupt values. Older platforms have no responsibility to provide a size if they set
8703          * mode to unspecified.
8704          */
8705         private int mWidthMode, mHeightMode;
8706         private int mWidth, mHeight;
8707 
8708 
8709         /**
8710          * Interface for LayoutManagers to request items to be prefetched, based on position, with
8711          * specified distance from viewport, which indicates priority.
8712          *
8713          * @see LayoutManager#collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
8714          * @see LayoutManager#collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
8715          */
8716         public interface LayoutPrefetchRegistry {
8717             /**
8718              * Requests an an item to be prefetched, based on position, with a specified distance,
8719              * indicating priority.
8720              *
8721              * @param layoutPosition Position of the item to prefetch.
8722              * @param pixelDistance  Distance from the current viewport to the bounds of the item,
8723              *                       must be non-negative.
8724              */
addPosition(int layoutPosition, int pixelDistance)8725             void addPosition(int layoutPosition, int pixelDistance);
8726         }
8727 
setRecyclerView(RecyclerView recyclerView)8728         void setRecyclerView(RecyclerView recyclerView) {
8729             if (recyclerView == null) {
8730                 mRecyclerView = null;
8731                 mChildHelper = null;
8732                 mWidth = 0;
8733                 mHeight = 0;
8734             } else {
8735                 mRecyclerView = recyclerView;
8736                 mChildHelper = recyclerView.mChildHelper;
8737                 mWidth = recyclerView.getWidth();
8738                 mHeight = recyclerView.getHeight();
8739             }
8740             mWidthMode = MeasureSpec.EXACTLY;
8741             mHeightMode = MeasureSpec.EXACTLY;
8742         }
8743 
setMeasureSpecs(int wSpec, int hSpec)8744         void setMeasureSpecs(int wSpec, int hSpec) {
8745             mWidth = MeasureSpec.getSize(wSpec);
8746             mWidthMode = MeasureSpec.getMode(wSpec);
8747             if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
8748                 mWidth = 0;
8749             }
8750 
8751             mHeight = MeasureSpec.getSize(hSpec);
8752             mHeightMode = MeasureSpec.getMode(hSpec);
8753             if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
8754                 mHeight = 0;
8755             }
8756         }
8757 
8758         /**
8759          * Called after a layout is calculated during a measure pass when using auto-measure.
8760          * <p>
8761          * It simply traverses all children to calculate a bounding box then calls
8762          * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
8763          * if they need to handle the bounding box differently.
8764          * <p>
8765          * For example, GridLayoutManager override that method to ensure that even if a column is
8766          * empty, the GridLayoutManager still measures wide enough to include it.
8767          *
8768          * @param widthSpec  The widthSpec that was passing into RecyclerView's onMeasure
8769          * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
8770          */
setMeasuredDimensionFromChildren(int widthSpec, int heightSpec)8771         void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
8772             final int count = getChildCount();
8773             if (count == 0) {
8774                 mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
8775                 return;
8776             }
8777             int minX = Integer.MAX_VALUE;
8778             int minY = Integer.MAX_VALUE;
8779             int maxX = Integer.MIN_VALUE;
8780             int maxY = Integer.MIN_VALUE;
8781 
8782             for (int i = 0; i < count; i++) {
8783                 View child = getChildAt(i);
8784                 final Rect bounds = mRecyclerView.mTempRect;
8785                 getDecoratedBoundsWithMargins(child, bounds);
8786                 if (bounds.left < minX) {
8787                     minX = bounds.left;
8788                 }
8789                 if (bounds.right > maxX) {
8790                     maxX = bounds.right;
8791                 }
8792                 if (bounds.top < minY) {
8793                     minY = bounds.top;
8794                 }
8795                 if (bounds.bottom > maxY) {
8796                     maxY = bounds.bottom;
8797                 }
8798             }
8799             mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
8800             setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
8801         }
8802 
8803         /**
8804          * Sets the measured dimensions from the given bounding box of the children and the
8805          * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
8806          * only called if a LayoutManager returns <code>true</code> from
8807          * {@link #isAutoMeasureEnabled()} and it is called after the RecyclerView calls
8808          * {@link LayoutManager#onLayoutChildren(Recycler, State)} in the execution of
8809          * {@link RecyclerView#onMeasure(int, int)}.
8810          * <p>
8811          * This method must call {@link #setMeasuredDimension(int, int)}.
8812          * <p>
8813          * The default implementation adds the RecyclerView's padding to the given bounding box
8814          * then caps the value to be within the given measurement specs.
8815          *
8816          * @param childrenBounds The bounding box of all children
8817          * @param wSpec          The widthMeasureSpec that was passed into the RecyclerView.
8818          * @param hSpec          The heightMeasureSpec that was passed into the RecyclerView.
8819          * @see #isAutoMeasureEnabled()
8820          * @see #setMeasuredDimension(int, int)
8821          */
setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec)8822         public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
8823             int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
8824             int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
8825             int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
8826             int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
8827             setMeasuredDimension(width, height);
8828         }
8829 
8830         /**
8831          * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
8832          */
requestLayout()8833         public void requestLayout() {
8834             if (mRecyclerView != null) {
8835                 mRecyclerView.requestLayout();
8836             }
8837         }
8838 
8839         /**
8840          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
8841          * {@link IllegalStateException} if it <b>is not</b>.
8842          *
8843          * @param message The message for the exception. Can be null.
8844          * @see #assertNotInLayoutOrScroll(String)
8845          */
assertInLayoutOrScroll(String message)8846         public void assertInLayoutOrScroll(String message) {
8847             if (mRecyclerView != null) {
8848                 mRecyclerView.assertInLayoutOrScroll(message);
8849             }
8850         }
8851 
8852         /**
8853          * Chooses a size from the given specs and parameters that is closest to the desired size
8854          * and also complies with the spec.
8855          *
8856          * @param spec    The measureSpec
8857          * @param desired The preferred measurement
8858          * @param min     The minimum value
8859          * @return A size that fits to the given specs
8860          */
chooseSize(int spec, int desired, int min)8861         public static int chooseSize(int spec, int desired, int min) {
8862             final int mode = View.MeasureSpec.getMode(spec);
8863             final int size = View.MeasureSpec.getSize(spec);
8864             switch (mode) {
8865                 case View.MeasureSpec.EXACTLY:
8866                     return size;
8867                 case View.MeasureSpec.AT_MOST:
8868                     return Math.min(size, Math.max(desired, min));
8869                 case View.MeasureSpec.UNSPECIFIED:
8870                 default:
8871                     return Math.max(desired, min);
8872             }
8873         }
8874 
8875         /**
8876          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
8877          * {@link IllegalStateException} if it <b>is</b>.
8878          *
8879          * @param message The message for the exception. Can be null.
8880          * @see #assertInLayoutOrScroll(String)
8881          */
8882         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
assertNotInLayoutOrScroll(String message)8883         public void assertNotInLayoutOrScroll(String message) {
8884             if (mRecyclerView != null) {
8885                 mRecyclerView.assertNotInLayoutOrScroll(message);
8886             }
8887         }
8888 
8889         /**
8890          * Defines whether the measuring pass of layout should use the AutoMeasure mechanism of
8891          * {@link RecyclerView} or if it should be done by the LayoutManager's implementation of
8892          * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
8893          *
8894          * @param enabled <code>True</code> if layout measurement should be done by the
8895          *                RecyclerView, <code>false</code> if it should be done by this
8896          *                LayoutManager.
8897          * @see #isAutoMeasureEnabled()
8898          * @deprecated Implementors of LayoutManager should define whether or not it uses
8899          * AutoMeasure by overriding {@link #isAutoMeasureEnabled()}.
8900          */
8901         @Deprecated
setAutoMeasureEnabled(boolean enabled)8902         public void setAutoMeasureEnabled(boolean enabled) {
8903             mAutoMeasure = enabled;
8904         }
8905 
8906         /**
8907          * Returns whether the measuring pass of layout should use the AutoMeasure mechanism of
8908          * {@link RecyclerView} or if it should be done by the LayoutManager's implementation of
8909          * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
8910          * <p>
8911          * This method returns false by default (it actually returns the value passed to the
8912          * deprecated {@link #setAutoMeasureEnabled(boolean)}) and should be overridden to return
8913          * true if a LayoutManager wants to be auto measured by the RecyclerView.
8914          * <p>
8915          * If this method is overridden to return true,
8916          * {@link LayoutManager#onMeasure(Recycler, State, int, int)} should not be overridden.
8917          * <p>
8918          * AutoMeasure is a RecyclerView mechanism that handles the measuring pass of layout in a
8919          * simple and contract satisfying way, including the wrapping of children laid out by
8920          * LayoutManager. Simply put, it handles wrapping children by calling
8921          * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a call to
8922          * {@link RecyclerView#onMeasure(int, int)}, and then calculating desired dimensions based
8923          * on children's dimensions and positions. It does this while supporting all existing
8924          * animation capabilities of the RecyclerView.
8925          * <p>
8926          * More specifically:
8927          * <ol>
8928          * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided measure
8929          * specs both have a mode of {@link View.MeasureSpec#EXACTLY}, RecyclerView will set its
8930          * measured dimensions accordingly and return, allowing layout to continue as normal
8931          * (Actually, RecyclerView will call
8932          * {@link LayoutManager#onMeasure(Recycler, State, int, int)} for backwards compatibility
8933          * reasons but it should not be overridden if AutoMeasure is being used).</li>
8934          * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
8935          * layout process. It will first process all pending Adapter updates and
8936          * then decide whether to run a predictive layout. If it decides to do so, it will first
8937          * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
8938          * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
8939          * return the width and height of the RecyclerView as of the last layout calculation.
8940          * <p>
8941          * After handling the predictive case, RecyclerView will call
8942          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
8943          * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
8944          * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
8945          * {@link #getWidth()} and {@link #getWidthMode()}.</li>
8946          * <li>After the layout calculation, RecyclerView sets the measured width & height by
8947          * calculating the bounding box for the children (+ RecyclerView's padding). The
8948          * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
8949          * different values. For instance, GridLayoutManager overrides this value to handle the case
8950          * where if it is vertical and has 3 columns but only 2 items, it should still measure its
8951          * width to fit 3 items, not 2.</li>
8952          * <li>Any following calls to {@link RecyclerView#onMeasure(int, int)} will run
8953          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
8954          * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
8955          * take care of which views are actually added / removed / moved / changed for animations so
8956          * that the LayoutManager should not worry about them and handle each
8957          * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.</li>
8958          * <li>When measure is complete and RecyclerView's
8959          * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
8960          * whether it already did layout calculations during the measure pass and if so, it re-uses
8961          * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
8962          * if the last measure spec was different from the final dimensions or adapter contents
8963          * have changed between the measure call and the layout call.</li>
8964          * <li>Finally, animations are calculated and run as usual.</li>
8965          * </ol>
8966          *
8967          * @return <code>True</code> if the measuring pass of layout should use the AutoMeasure
8968          * mechanism of {@link RecyclerView} or <code>False</code> if it should be done by the
8969          * LayoutManager's implementation of
8970          * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
8971          * @see #setMeasuredDimension(Rect, int, int)
8972          * @see #onMeasure(Recycler, State, int, int)
8973          */
isAutoMeasureEnabled()8974         public boolean isAutoMeasureEnabled() {
8975             return mAutoMeasure;
8976         }
8977 
8978         /**
8979          * Returns whether this LayoutManager supports "predictive item animations".
8980          * <p>
8981          * "Predictive item animations" are automatically created animations that show
8982          * where items came from, and where they are going to, as items are added, removed,
8983          * or moved within a layout.
8984          * <p>
8985          * A LayoutManager wishing to support predictive item animations must override this
8986          * method to return true (the default implementation returns false) and must obey certain
8987          * behavioral contracts outlined in {@link #onLayoutChildren(Recycler, State)}.
8988          * <p>
8989          * Whether item animations actually occur in a RecyclerView is actually determined by both
8990          * the return value from this method and the
8991          * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
8992          * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
8993          * method returns false, then only "simple item animations" will be enabled in the
8994          * RecyclerView, in which views whose position are changing are simply faded in/out. If the
8995          * RecyclerView has a non-null ItemAnimator and this method returns true, then predictive
8996          * item animations will be enabled in the RecyclerView.
8997          *
8998          * @return true if this LayoutManager supports predictive item animations, false otherwise.
8999          */
supportsPredictiveItemAnimations()9000         public boolean supportsPredictiveItemAnimations() {
9001             return false;
9002         }
9003 
9004         /**
9005          * Sets whether the LayoutManager should be queried for views outside of
9006          * its viewport while the UI thread is idle between frames.
9007          *
9008          * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
9009          * view system traversals on devices running API 21 or greater. Default value is true.</p>
9010          *
9011          * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
9012          * to RenderThread and the starting up its next frame at the next VSync pulse. By
9013          * prefetching out of window views in this time period, delays from inflation and view
9014          * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
9015          *
9016          * <p>While prefetch is enabled, it will have the side effect of expanding the effective
9017          * size of the View cache to hold prefetched views.</p>
9018          *
9019          * @param enabled <code>True</code> if items should be prefetched in between traversals.
9020          * @see #isItemPrefetchEnabled()
9021          */
setItemPrefetchEnabled(boolean enabled)9022         public final void setItemPrefetchEnabled(boolean enabled) {
9023             if (enabled != mItemPrefetchEnabled) {
9024                 mItemPrefetchEnabled = enabled;
9025                 mPrefetchMaxCountObserved = 0;
9026                 if (mRecyclerView != null) {
9027                     mRecyclerView.mRecycler.updateViewCacheSize();
9028                 }
9029             }
9030         }
9031 
9032         /**
9033          * Sets whether the LayoutManager should be queried for views outside of
9034          * its viewport while the UI thread is idle between frames.
9035          *
9036          * @return true if item prefetch is enabled, false otherwise
9037          * @see #setItemPrefetchEnabled(boolean)
9038          */
isItemPrefetchEnabled()9039         public final boolean isItemPrefetchEnabled() {
9040             return mItemPrefetchEnabled;
9041         }
9042 
9043         /**
9044          * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
9045          *
9046          * <p>If item prefetch is enabled, this method is called in between traversals to gather
9047          * which positions the LayoutManager will soon need, given upcoming movement in subsequent
9048          * traversals.</p>
9049          *
9050          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
9051          * each item to be prepared, and these positions will have their ViewHolders created and
9052          * bound, if there is sufficient time available, in advance of being needed by a
9053          * scroll or layout.</p>
9054          *
9055          * @param dx                     X movement component.
9056          * @param dy                     Y movement component.
9057          * @param state                  State of RecyclerView
9058          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
9059          * @see #isItemPrefetchEnabled()
9060          * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
9061          */
9062         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
collectAdjacentPrefetchPositions(int dx, int dy, State state, LayoutPrefetchRegistry layoutPrefetchRegistry)9063         public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
9064                 LayoutPrefetchRegistry layoutPrefetchRegistry) {
9065         }
9066 
9067         /**
9068          * Gather all positions from the LayoutManager to be prefetched in preperation for its
9069          * RecyclerView to come on screen, due to the movement of another, containing RecyclerView.
9070          *
9071          * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
9072          *
9073          * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
9074          * LayoutManager, this method is called in between draw traversals to gather
9075          * which positions this LayoutManager will first need, once it appears on the screen.</p>
9076          *
9077          * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
9078          * vertically scrolling LayoutManager, this method would be called when the horizontal list
9079          * is about to come onscreen.</p>
9080          *
9081          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
9082          * each item to be prepared, and these positions will have their ViewHolders created and
9083          * bound, if there is sufficient time available, in advance of being needed by a
9084          * scroll or layout.</p>
9085          *
9086          * @param adapterItemCount       number of items in the associated adapter.
9087          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
9088          * @see #isItemPrefetchEnabled()
9089          * @see #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
9090          */
9091         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
collectInitialPrefetchPositions(int adapterItemCount, LayoutPrefetchRegistry layoutPrefetchRegistry)9092         public void collectInitialPrefetchPositions(int adapterItemCount,
9093                 LayoutPrefetchRegistry layoutPrefetchRegistry) {
9094         }
9095 
dispatchAttachedToWindow(RecyclerView view)9096         void dispatchAttachedToWindow(RecyclerView view) {
9097             mIsAttachedToWindow = true;
9098             onAttachedToWindow(view);
9099         }
9100 
dispatchDetachedFromWindow(RecyclerView view, Recycler recycler)9101         void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
9102             mIsAttachedToWindow = false;
9103             onDetachedFromWindow(view, recycler);
9104         }
9105 
9106         /**
9107          * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
9108          * to a window.
9109          *
9110          * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
9111          * is attached to window.
9112          */
isAttachedToWindow()9113         public boolean isAttachedToWindow() {
9114             return mIsAttachedToWindow;
9115         }
9116 
9117         /**
9118          * Causes the Runnable to execute on the next animation time step.
9119          * The runnable will be run on the user interface thread.
9120          * <p>
9121          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
9122          *
9123          * @param action The Runnable that will be executed.
9124          * @see #removeCallbacks
9125          */
postOnAnimation(Runnable action)9126         public void postOnAnimation(Runnable action) {
9127             if (mRecyclerView != null) {
9128                 ViewCompat.postOnAnimation(mRecyclerView, action);
9129             }
9130         }
9131 
9132         /**
9133          * Removes the specified Runnable from the message queue.
9134          * <p>
9135          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
9136          *
9137          * @param action The Runnable to remove from the message handling queue
9138          * @return true if RecyclerView could ask the Handler to remove the Runnable,
9139          * false otherwise. When the returned value is true, the Runnable
9140          * may or may not have been actually removed from the message queue
9141          * (for instance, if the Runnable was not in the queue already.)
9142          * @see #postOnAnimation
9143          */
removeCallbacks(Runnable action)9144         public boolean removeCallbacks(Runnable action) {
9145             if (mRecyclerView != null) {
9146                 return mRecyclerView.removeCallbacks(action);
9147             }
9148             return false;
9149         }
9150 
9151         /**
9152          * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
9153          * is attached to a window.
9154          * <p>
9155          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
9156          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
9157          * not requested on the RecyclerView while it was detached.
9158          * <p>
9159          * Subclass implementations should always call through to the superclass implementation.
9160          *
9161          * @param view The RecyclerView this LayoutManager is bound to
9162          * @see #onDetachedFromWindow(RecyclerView, Recycler)
9163          */
9164         @CallSuper
onAttachedToWindow(RecyclerView view)9165         public void onAttachedToWindow(RecyclerView view) {
9166         }
9167 
9168         /**
9169          * @deprecated override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
9170          */
9171         @Deprecated
onDetachedFromWindow(RecyclerView view)9172         public void onDetachedFromWindow(RecyclerView view) {
9173 
9174         }
9175 
9176         /**
9177          * Called when this LayoutManager is detached from its parent RecyclerView or when
9178          * its parent RecyclerView is detached from its window.
9179          * <p>
9180          * LayoutManager should clear all of its View references as another LayoutManager might be
9181          * assigned to the RecyclerView.
9182          * <p>
9183          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
9184          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
9185          * not requested on the RecyclerView while it was detached.
9186          * <p>
9187          * If your LayoutManager has View references that it cleans in on-detach, it should also
9188          * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
9189          * RecyclerView is re-attached.
9190          * <p>
9191          * Subclass implementations should always call through to the superclass implementation.
9192          *
9193          * @param view     The RecyclerView this LayoutManager is bound to
9194          * @param recycler The recycler to use if you prefer to recycle your children instead of
9195          *                 keeping them around.
9196          * @see #onAttachedToWindow(RecyclerView)
9197          */
9198         @CallSuper
9199         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
onDetachedFromWindow(RecyclerView view, Recycler recycler)9200         public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
9201             onDetachedFromWindow(view);
9202         }
9203 
9204         /**
9205          * Check if the RecyclerView is configured to clip child views to its padding.
9206          *
9207          * @return true if this RecyclerView clips children to its padding, false otherwise
9208          */
getClipToPadding()9209         public boolean getClipToPadding() {
9210             return mRecyclerView != null && mRecyclerView.mClipToPadding;
9211         }
9212 
9213         /**
9214          * Lay out all relevant child views from the given adapter.
9215          *
9216          * The LayoutManager is in charge of the behavior of item animations. By default,
9217          * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
9218          * item animations are enabled. This means that add/remove operations on the
9219          * adapter will result in animations to add new or appearing items, removed or
9220          * disappearing items, and moved items. If a LayoutManager returns false from
9221          * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
9222          * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
9223          * RecyclerView will have enough information to run those animations in a simple
9224          * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
9225          * simply fade views in and out, whether they are actually added/removed or whether
9226          * they are moved on or off the screen due to other add/remove operations.
9227          *
9228          * <p>A LayoutManager wanting a better item animation experience, where items can be
9229          * animated onto and off of the screen according to where the items exist when they
9230          * are not on screen, then the LayoutManager should return true from
9231          * {@link #supportsPredictiveItemAnimations()} and add additional logic to
9232          * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
9233          * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
9234          * once as a "pre" layout step to determine where items would have been prior to
9235          * a real layout, and again to do the "real" layout. In the pre-layout phase,
9236          * items will remember their pre-layout positions to allow them to be laid out
9237          * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
9238          * be returned from the scrap to help determine correct placement of other items.
9239          * These removed items should not be added to the child list, but should be used
9240          * to help calculate correct positioning of other views, including views that
9241          * were not previously onscreen (referred to as APPEARING views), but whose
9242          * pre-layout offscreen position can be determined given the extra
9243          * information about the pre-layout removed views.</p>
9244          *
9245          * <p>The second layout pass is the real layout in which only non-removed views
9246          * will be used. The only additional requirement during this pass is, if
9247          * {@link #supportsPredictiveItemAnimations()} returns true, to note which
9248          * views exist in the child list prior to layout and which are not there after
9249          * layout (referred to as DISAPPEARING views), and to position/layout those views
9250          * appropriately, without regard to the actual bounds of the RecyclerView. This allows
9251          * the animation system to know the location to which to animate these disappearing
9252          * views.</p>
9253          *
9254          * <p>The default LayoutManager implementations for RecyclerView handle all of these
9255          * requirements for animations already. Clients of RecyclerView can either use one
9256          * of these layout managers directly or look at their implementations of
9257          * onLayoutChildren() to see how they account for the APPEARING and
9258          * DISAPPEARING views.</p>
9259          *
9260          * @param recycler Recycler to use for fetching potentially cached views for a
9261          *                 position
9262          * @param state    Transient state of RecyclerView
9263          */
9264         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
onLayoutChildren(Recycler recycler, State state)9265         public void onLayoutChildren(Recycler recycler, State state) {
9266             Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
9267         }
9268 
9269         /**
9270          * Called after a full layout calculation is finished. The layout calculation may include
9271          * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
9272          * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
9273          * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
9274          * <p>
9275          * This is a good place for the LayoutManager to do some cleanup like pending scroll
9276          * position, saved state etc.
9277          *
9278          * @param state Transient state of RecyclerView
9279          */
9280         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
onLayoutCompleted(State state)9281         public void onLayoutCompleted(State state) {
9282         }
9283 
9284         /**
9285          * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
9286          *
9287          * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
9288          * to store extra information specific to the layout. Client code should subclass
9289          * {@link RecyclerView.LayoutParams} for this purpose.</p>
9290          *
9291          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
9292          * you must also override
9293          * {@link #checkLayoutParams(LayoutParams)},
9294          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
9295          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
9296          *
9297          * @return A new LayoutParams for a child view
9298          */
9299         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
generateDefaultLayoutParams()9300         public abstract LayoutParams generateDefaultLayoutParams();
9301 
9302         /**
9303          * Determines the validity of the supplied LayoutParams object.
9304          *
9305          * <p>This should check to make sure that the object is of the correct type
9306          * and all values are within acceptable ranges. The default implementation
9307          * returns <code>true</code> for non-null params.</p>
9308          *
9309          * @param lp LayoutParams object to check
9310          * @return true if this LayoutParams object is valid, false otherwise
9311          */
checkLayoutParams(LayoutParams lp)9312         public boolean checkLayoutParams(LayoutParams lp) {
9313             return lp != null;
9314         }
9315 
9316         /**
9317          * Create a LayoutParams object suitable for this LayoutManager, copying relevant
9318          * values from the supplied LayoutParams object if possible.
9319          *
9320          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
9321          * you must also override
9322          * {@link #checkLayoutParams(LayoutParams)},
9323          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
9324          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
9325          *
9326          * @param lp Source LayoutParams object to copy values from
9327          * @return a new LayoutParams object
9328          */
9329         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
generateLayoutParams(ViewGroup.LayoutParams lp)9330         public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
9331             if (lp instanceof LayoutParams) {
9332                 return new LayoutParams((LayoutParams) lp);
9333             } else if (lp instanceof MarginLayoutParams) {
9334                 return new LayoutParams((MarginLayoutParams) lp);
9335             } else {
9336                 return new LayoutParams(lp);
9337             }
9338         }
9339 
9340         /**
9341          * Create a LayoutParams object suitable for this LayoutManager from
9342          * an inflated layout resource.
9343          *
9344          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
9345          * you must also override
9346          * {@link #checkLayoutParams(LayoutParams)},
9347          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
9348          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
9349          *
9350          * @param c     Context for obtaining styled attributes
9351          * @param attrs AttributeSet describing the supplied arguments
9352          * @return a new LayoutParams object
9353          */
9354         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
generateLayoutParams(Context c, AttributeSet attrs)9355         public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
9356             return new LayoutParams(c, attrs);
9357         }
9358 
9359         /**
9360          * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
9361          * The default implementation does nothing and returns 0.
9362          *
9363          * @param dx       distance to scroll by in pixels. X increases as scroll position
9364          *                 approaches the right.
9365          * @param recycler Recycler to use for fetching potentially cached views for a
9366          *                 position
9367          * @param state    Transient state of RecyclerView
9368          * @return The actual distance scrolled. The return value will be negative if dx was
9369          * negative and scrolling proceeeded in that direction.
9370          * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
9371          */
9372         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
scrollHorizontallyBy(int dx, Recycler recycler, State state)9373         public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
9374             return 0;
9375         }
9376 
9377         /**
9378          * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
9379          * The default implementation does nothing and returns 0.
9380          *
9381          * @param dy       distance to scroll in pixels. Y increases as scroll position
9382          *                 approaches the bottom.
9383          * @param recycler Recycler to use for fetching potentially cached views for a
9384          *                 position
9385          * @param state    Transient state of RecyclerView
9386          * @return The actual distance scrolled. The return value will be negative if dy was
9387          * negative and scrolling proceeeded in that direction.
9388          * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
9389          */
9390         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
scrollVerticallyBy(int dy, Recycler recycler, State state)9391         public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
9392             return 0;
9393         }
9394 
9395         /**
9396          * Query if horizontal scrolling is currently supported. The default implementation
9397          * returns false.
9398          *
9399          * @return True if this LayoutManager can scroll the current contents horizontally
9400          */
canScrollHorizontally()9401         public boolean canScrollHorizontally() {
9402             return false;
9403         }
9404 
9405         /**
9406          * Query if vertical scrolling is currently supported. The default implementation
9407          * returns false.
9408          *
9409          * @return True if this LayoutManager can scroll the current contents vertically
9410          */
canScrollVertically()9411         public boolean canScrollVertically() {
9412             return false;
9413         }
9414 
9415         /**
9416          * Scroll to the specified adapter position.
9417          *
9418          * Actual position of the item on the screen depends on the LayoutManager implementation.
9419          *
9420          * @param position Scroll to this adapter position.
9421          */
scrollToPosition(int position)9422         public void scrollToPosition(int position) {
9423             if (sVerboseLoggingEnabled) {
9424                 Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
9425             }
9426         }
9427 
9428         /**
9429          * <p>Smooth scroll to the specified adapter position.</p>
9430          * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
9431          * instance and call {@link #startSmoothScroll(SmoothScroller)}.
9432          * </p>
9433          *
9434          * @param recyclerView The RecyclerView to which this layout manager is attached
9435          * @param state        Current State of RecyclerView
9436          * @param position     Scroll to this adapter position.
9437          */
9438         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
smoothScrollToPosition(RecyclerView recyclerView, State state, int position)9439         public void smoothScrollToPosition(RecyclerView recyclerView, State state,
9440                 int position) {
9441             Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
9442         }
9443 
9444         /**
9445          * Starts a smooth scroll using the provided {@link SmoothScroller}.
9446          *
9447          * <p>Each instance of SmoothScroller is intended to only be used once. Provide a new
9448          * SmoothScroller instance each time this method is called.
9449          *
9450          * <p>Calling this method will cancel any previous smooth scroll request.
9451          *
9452          * @param smoothScroller Instance which defines how smooth scroll should be animated
9453          */
9454         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
startSmoothScroll(SmoothScroller smoothScroller)9455         public void startSmoothScroll(SmoothScroller smoothScroller) {
9456             if (mSmoothScroller != null && smoothScroller != mSmoothScroller
9457                     && mSmoothScroller.isRunning()) {
9458                 mSmoothScroller.stop();
9459             }
9460             mSmoothScroller = smoothScroller;
9461             mSmoothScroller.start(mRecyclerView, this);
9462         }
9463 
9464         /**
9465          * @return true if RecyclerView is currently in the state of smooth scrolling.
9466          */
isSmoothScrolling()9467         public boolean isSmoothScrolling() {
9468             return mSmoothScroller != null && mSmoothScroller.isRunning();
9469         }
9470 
9471         /**
9472          * Returns the resolved layout direction for this RecyclerView.
9473          *
9474          * @return {@link androidx.core.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout
9475          * direction is RTL or returns
9476          * {@link androidx.core.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction
9477          * is not RTL.
9478          */
getLayoutDirection()9479         public int getLayoutDirection() {
9480             return mRecyclerView.getLayoutDirection();
9481         }
9482 
9483         /**
9484          * Query if the layout is in reverse order. This will affect, for example, keyboard
9485          * navigation via page up/page down.  The default implementation returns false.
9486          *
9487          * @return true if this LayoutManager is currently in reverse order.
9488          */
isLayoutReversed()9489         public boolean isLayoutReversed() {
9490             return false;
9491         }
9492 
9493         /**
9494          * Ends all animations on the view created by the {@link ItemAnimator}.
9495          *
9496          * @param view The View for which the animations should be ended.
9497          * @see RecyclerView.ItemAnimator#endAnimations()
9498          */
9499         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
endAnimation(View view)9500         public void endAnimation(View view) {
9501             if (mRecyclerView.mItemAnimator != null) {
9502                 mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
9503             }
9504         }
9505 
9506         /**
9507          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
9508          * to the layout that is known to be going away, either because it has been
9509          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
9510          * visible portion of the container but is being laid out in order to inform RecyclerView
9511          * in how to animate the item out of view.
9512          * <p>
9513          * Views added via this method are going to be invisible to LayoutManager after the
9514          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
9515          * or won't be included in {@link #getChildCount()} method.
9516          *
9517          * @param child View to add and then remove with animation.
9518          */
9519         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
addDisappearingView(View child)9520         public void addDisappearingView(View child) {
9521             addDisappearingView(child, -1);
9522         }
9523 
9524         /**
9525          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
9526          * to the layout that is known to be going away, either because it has been
9527          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
9528          * visible portion of the container but is being laid out in order to inform RecyclerView
9529          * in how to animate the item out of view.
9530          * <p>
9531          * Views added via this method are going to be invisible to LayoutManager after the
9532          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
9533          * or won't be included in {@link #getChildCount()} method.
9534          *
9535          * @param child View to add and then remove with animation.
9536          * @param index Index of the view.
9537          */
9538         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
addDisappearingView(View child, int index)9539         public void addDisappearingView(View child, int index) {
9540             addViewInt(child, index, true);
9541         }
9542 
9543         /**
9544          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
9545          * use this method to add views obtained from a {@link Recycler} using
9546          * {@link Recycler#getViewForPosition(int)}.
9547          *
9548          * @param child View to add
9549          */
9550         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
addView(View child)9551         public void addView(View child) {
9552             addView(child, -1);
9553         }
9554 
9555         /**
9556          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
9557          * use this method to add views obtained from a {@link Recycler} using
9558          * {@link Recycler#getViewForPosition(int)}.
9559          *
9560          * @param child View to add
9561          * @param index Index to add child at
9562          */
9563         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
addView(View child, int index)9564         public void addView(View child, int index) {
9565             addViewInt(child, index, false);
9566         }
9567 
addViewInt(View child, int index, boolean disappearing)9568         private void addViewInt(View child, int index, boolean disappearing) {
9569             final ViewHolder holder = getChildViewHolderInt(child);
9570             if (disappearing || holder.isRemoved()) {
9571                 // these views will be hidden at the end of the layout pass.
9572                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
9573             } else {
9574                 // This may look like unnecessary but may happen if layout manager supports
9575                 // predictive layouts and adapter removed then re-added the same item.
9576                 // In this case, added version will be visible in the post layout (because add is
9577                 // deferred) but RV will still bind it to the same View.
9578                 // So if a View re-appears in post layout pass, remove it from disappearing list.
9579                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
9580             }
9581             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
9582             if (holder.wasReturnedFromScrap() || holder.isScrap()) {
9583                 if (holder.isScrap()) {
9584                     holder.unScrap();
9585                 } else {
9586                     holder.clearReturnedFromScrapFlag();
9587                 }
9588                 mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
9589                 if (DISPATCH_TEMP_DETACH) {
9590                     ViewCompat.dispatchFinishTemporaryDetach(child);
9591                 }
9592             } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
9593                 // ensure in correct position
9594                 int currentIndex = mChildHelper.indexOfChild(child);
9595                 if (index == -1) {
9596                     index = mChildHelper.getChildCount();
9597                 }
9598                 if (currentIndex == -1) {
9599                     throw new IllegalStateException("Added View has RecyclerView as parent but"
9600                             + " view is not a real child. Unfiltered index:"
9601                             + mRecyclerView.indexOfChild(child) + mRecyclerView.exceptionLabel());
9602                 }
9603                 if (currentIndex != index) {
9604                     mRecyclerView.mLayout.moveView(currentIndex, index);
9605                 }
9606             } else {
9607                 mChildHelper.addView(child, index, false);
9608                 lp.mInsetsDirty = true;
9609                 if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
9610                     mSmoothScroller.onChildAttachedToWindow(child);
9611                 }
9612             }
9613             if (lp.mPendingInvalidate) {
9614                 if (sVerboseLoggingEnabled) {
9615                     Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
9616                 }
9617                 holder.itemView.invalidate();
9618                 lp.mPendingInvalidate = false;
9619             }
9620         }
9621 
9622         /**
9623          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
9624          * use this method to completely remove a child view that is no longer needed.
9625          * LayoutManagers should strongly consider recycling removed views using
9626          * {@link Recycler#recycleView(android.view.View)}.
9627          *
9628          * @param child View to remove
9629          */
9630         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
removeView(View child)9631         public void removeView(View child) {
9632             mChildHelper.removeView(child);
9633         }
9634 
9635         /**
9636          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
9637          * use this method to completely remove a child view that is no longer needed.
9638          * LayoutManagers should strongly consider recycling removed views using
9639          * {@link Recycler#recycleView(android.view.View)}.
9640          *
9641          * @param index Index of the child view to remove
9642          */
removeViewAt(int index)9643         public void removeViewAt(int index) {
9644             final View child = getChildAt(index);
9645             if (child != null) {
9646                 mChildHelper.removeViewAt(index);
9647             }
9648         }
9649 
9650         /**
9651          * Remove all views from the currently attached RecyclerView. This will not recycle
9652          * any of the affected views; the LayoutManager is responsible for doing so if desired.
9653          */
removeAllViews()9654         public void removeAllViews() {
9655             // Only remove non-animating views
9656             final int childCount = getChildCount();
9657             for (int i = childCount - 1; i >= 0; i--) {
9658                 mChildHelper.removeViewAt(i);
9659             }
9660         }
9661 
9662         /**
9663          * Returns offset of the RecyclerView's text baseline from the its top boundary.
9664          *
9665          * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
9666          * there is no baseline.
9667          */
getBaseline()9668         public int getBaseline() {
9669             return -1;
9670         }
9671 
9672         /**
9673          * Returns the adapter position of the item represented by the given View. This does not
9674          * contain any adapter changes that might have happened after the last layout.
9675          *
9676          * @param view The view to query
9677          * @return The adapter position of the item which is rendered by this View.
9678          */
getPosition(@onNull View view)9679         public int getPosition(@NonNull View view) {
9680             return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
9681         }
9682 
9683         /**
9684          * Returns the View type defined by the adapter.
9685          *
9686          * @param view The view to query
9687          * @return The type of the view assigned by the adapter.
9688          */
getItemViewType(@onNull View view)9689         public int getItemViewType(@NonNull View view) {
9690             return getChildViewHolderInt(view).getItemViewType();
9691         }
9692 
9693         /**
9694          * Traverses the ancestors of the given view and returns the item view that contains it
9695          * and also a direct child of the LayoutManager.
9696          * <p>
9697          * Note that this method may return null if the view is a child of the RecyclerView but
9698          * not a child of the LayoutManager (e.g. running a disappear animation).
9699          *
9700          * @param view The view that is a descendant of the LayoutManager.
9701          * @return The direct child of the LayoutManager which contains the given view or null if
9702          * the provided view is not a descendant of this LayoutManager.
9703          * @see RecyclerView#getChildViewHolder(View)
9704          * @see RecyclerView#findContainingViewHolder(View)
9705          */
findContainingItemView(@onNull View view)9706         public @Nullable View findContainingItemView(@NonNull View view) {
9707             if (mRecyclerView == null) {
9708                 return null;
9709             }
9710             View found = mRecyclerView.findContainingItemView(view);
9711             if (found == null) {
9712                 return null;
9713             }
9714             if (mChildHelper.isHidden(found)) {
9715                 return null;
9716             }
9717             return found;
9718         }
9719 
9720         /**
9721          * Finds the view which represents the given adapter position.
9722          * <p>
9723          * This method traverses each child since it has no information about child order.
9724          * Override this method to improve performance if your LayoutManager keeps data about
9725          * child views.
9726          * <p>
9727          * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
9728          *
9729          * @param position Position of the item in adapter
9730          * @return The child view that represents the given position or null if the position is not
9731          * laid out
9732          */
findViewByPosition(int position)9733         public @Nullable View findViewByPosition(int position) {
9734             final int childCount = getChildCount();
9735             for (int i = 0; i < childCount; i++) {
9736                 View child = getChildAt(i);
9737                 ViewHolder vh = getChildViewHolderInt(child);
9738                 if (vh == null) {
9739                     continue;
9740                 }
9741                 if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
9742                         && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
9743                     return child;
9744                 }
9745             }
9746             return null;
9747         }
9748 
9749         /**
9750          * Temporarily detach a child view.
9751          *
9752          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
9753          * views currently attached to the RecyclerView. Generally LayoutManager implementations
9754          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
9755          * so that the detached view may be rebound and reused.</p>
9756          *
9757          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
9758          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
9759          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
9760          * before the LayoutManager entry point method called by RecyclerView returns.</p>
9761          *
9762          * @param child Child to detach
9763          */
detachView(@onNull View child)9764         public void detachView(@NonNull View child) {
9765             final int ind = mChildHelper.indexOfChild(child);
9766             if (ind >= 0) {
9767                 detachViewInternal(ind, child);
9768             }
9769         }
9770 
9771         /**
9772          * Temporarily detach a child view.
9773          *
9774          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
9775          * views currently attached to the RecyclerView. Generally LayoutManager implementations
9776          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
9777          * so that the detached view may be rebound and reused.</p>
9778          *
9779          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
9780          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
9781          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
9782          * before the LayoutManager entry point method called by RecyclerView returns.</p>
9783          *
9784          * @param index Index of the child to detach
9785          */
detachViewAt(int index)9786         public void detachViewAt(int index) {
9787             detachViewInternal(index, getChildAt(index));
9788         }
9789 
detachViewInternal(int index, @NonNull View view)9790         private void detachViewInternal(int index, @NonNull View view) {
9791             if (DISPATCH_TEMP_DETACH) {
9792                 ViewCompat.dispatchStartTemporaryDetach(view);
9793             }
9794             mChildHelper.detachViewFromParent(index);
9795         }
9796 
9797         /**
9798          * Reattach a previously {@link #detachView(android.view.View) detached} view.
9799          * This method should not be used to reattach views that were previously
9800          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
9801          *
9802          * @param child Child to reattach
9803          * @param index Intended child index for child
9804          * @param lp    LayoutParams for child
9805          */
attachView(@onNull View child, int index, LayoutParams lp)9806         public void attachView(@NonNull View child, int index, LayoutParams lp) {
9807             ViewHolder vh = getChildViewHolderInt(child);
9808             if (vh.isRemoved()) {
9809                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
9810             } else {
9811                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
9812             }
9813             mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
9814             if (DISPATCH_TEMP_DETACH) {
9815                 ViewCompat.dispatchFinishTemporaryDetach(child);
9816             }
9817         }
9818 
9819         /**
9820          * Reattach a previously {@link #detachView(android.view.View) detached} view.
9821          * This method should not be used to reattach views that were previously
9822          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
9823          *
9824          * @param child Child to reattach
9825          * @param index Intended child index for child
9826          */
attachView(@onNull View child, int index)9827         public void attachView(@NonNull View child, int index) {
9828             attachView(child, index, (LayoutParams) child.getLayoutParams());
9829         }
9830 
9831         /**
9832          * Reattach a previously {@link #detachView(android.view.View) detached} view.
9833          * This method should not be used to reattach views that were previously
9834          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
9835          *
9836          * @param child Child to reattach
9837          */
attachView(@onNull View child)9838         public void attachView(@NonNull View child) {
9839             attachView(child, -1);
9840         }
9841 
9842         /**
9843          * Finish removing a view that was previously temporarily
9844          * {@link #detachView(android.view.View) detached}.
9845          *
9846          * @param child Detached child to remove
9847          */
removeDetachedView(@onNull View child)9848         public void removeDetachedView(@NonNull View child) {
9849             mRecyclerView.removeDetachedView(child, false);
9850         }
9851 
9852         /**
9853          * Moves a View from one position to another.
9854          *
9855          * @param fromIndex The View's initial index
9856          * @param toIndex   The View's target index
9857          */
moveView(int fromIndex, int toIndex)9858         public void moveView(int fromIndex, int toIndex) {
9859             View view = getChildAt(fromIndex);
9860             if (view == null) {
9861                 throw new IllegalArgumentException("Cannot move a child from non-existing index:"
9862                         + fromIndex + mRecyclerView.toString());
9863             }
9864             detachViewAt(fromIndex);
9865             attachView(view, toIndex);
9866         }
9867 
9868         /**
9869          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
9870          *
9871          * <p>Scrapping a view allows it to be rebound and reused to show updated or
9872          * different data.</p>
9873          *
9874          * @param child    Child to detach and scrap
9875          * @param recycler Recycler to deposit the new scrap view into
9876          */
detachAndScrapView(@onNull View child, @NonNull Recycler recycler)9877         public void detachAndScrapView(@NonNull View child, @NonNull Recycler recycler) {
9878             int index = mChildHelper.indexOfChild(child);
9879             scrapOrRecycleView(recycler, index, child);
9880         }
9881 
9882         /**
9883          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
9884          *
9885          * <p>Scrapping a view allows it to be rebound and reused to show updated or
9886          * different data.</p>
9887          *
9888          * @param index    Index of child to detach and scrap
9889          * @param recycler Recycler to deposit the new scrap view into
9890          */
detachAndScrapViewAt(int index, @NonNull Recycler recycler)9891         public void detachAndScrapViewAt(int index, @NonNull Recycler recycler) {
9892             final View child = getChildAt(index);
9893             scrapOrRecycleView(recycler, index, child);
9894         }
9895 
9896         /**
9897          * Remove a child view and recycle it using the given Recycler.
9898          *
9899          * @param child    Child to remove and recycle
9900          * @param recycler Recycler to use to recycle child
9901          */
removeAndRecycleView(@onNull View child, @NonNull Recycler recycler)9902         public void removeAndRecycleView(@NonNull View child, @NonNull Recycler recycler) {
9903             removeView(child);
9904             recycler.recycleView(child);
9905         }
9906 
9907         /**
9908          * Remove a child view and recycle it using the given Recycler.
9909          *
9910          * @param index    Index of child to remove and recycle
9911          * @param recycler Recycler to use to recycle child
9912          */
removeAndRecycleViewAt(int index, @NonNull Recycler recycler)9913         public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
9914             final View view = getChildAt(index);
9915             removeViewAt(index);
9916             recycler.recycleView(view);
9917         }
9918 
9919         /**
9920          * Return the current number of child views attached to the parent RecyclerView.
9921          * This does not include child views that were temporarily detached and/or scrapped.
9922          *
9923          * @return Number of attached children
9924          */
getChildCount()9925         public int getChildCount() {
9926             return mChildHelper != null ? mChildHelper.getChildCount() : 0;
9927         }
9928 
9929         /**
9930          * Return the child view at the given index
9931          *
9932          * @param index Index of child to return
9933          * @return Child view at index
9934          */
getChildAt(int index)9935         public @Nullable View getChildAt(int index) {
9936             return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
9937         }
9938 
9939         /**
9940          * Return the width measurement spec mode that is currently relevant to the LayoutManager.
9941          *
9942          * <p>This value is set only if the LayoutManager opts into the AutoMeasure api via
9943          * {@link #setAutoMeasureEnabled(boolean)}.
9944          *
9945          * <p>When RecyclerView is running a layout, this value is always set to
9946          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
9947          *
9948          * @return Width measure spec mode
9949          * @see View.MeasureSpec#getMode(int)
9950          */
getWidthMode()9951         public int getWidthMode() {
9952             return mWidthMode;
9953         }
9954 
9955         /**
9956          * Return the height measurement spec mode that is currently relevant to the LayoutManager.
9957          *
9958          * <p>This value is set only if the LayoutManager opts into the AutoMeasure api via
9959          * {@link #setAutoMeasureEnabled(boolean)}.
9960          *
9961          * <p>When RecyclerView is running a layout, this value is always set to
9962          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
9963          *
9964          * @return Height measure spec mode
9965          * @see View.MeasureSpec#getMode(int)
9966          */
getHeightMode()9967         public int getHeightMode() {
9968             return mHeightMode;
9969         }
9970 
9971         /**
9972          * Returns the width that is currently relevant to the LayoutManager.
9973          *
9974          * <p>This value is usually equal to the laid out width of the {@link RecyclerView} but may
9975          * reflect the current {@link android.view.View.MeasureSpec} width if the
9976          * {@link LayoutManager} is using AutoMeasure and the RecyclerView is in the process of
9977          * measuring. The LayoutManager must always use this method to retrieve the width relevant
9978          * to it at any given time.
9979          *
9980          * @return Width in pixels
9981          */
9982         @Px
getWidth()9983         public int getWidth() {
9984             return mWidth;
9985         }
9986 
9987         /**
9988          * Returns the height that is currently relevant to the LayoutManager.
9989          *
9990          * <p>This value is usually equal to the laid out height of the {@link RecyclerView} but may
9991          * reflect the current {@link android.view.View.MeasureSpec} height if the
9992          * {@link LayoutManager} is using AutoMeasure and the RecyclerView is in the process of
9993          * measuring. The LayoutManager must always use this method to retrieve the height relevant
9994          * to it at any given time.
9995          *
9996          * @return Height in pixels
9997          */
9998         @Px
getHeight()9999         public int getHeight() {
10000             return mHeight;
10001         }
10002 
10003         /**
10004          * Return the left padding of the parent RecyclerView
10005          *
10006          * @return Padding in pixels
10007          */
10008         @Px
getPaddingLeft()10009         public int getPaddingLeft() {
10010             return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
10011         }
10012 
10013         /**
10014          * Return the top padding of the parent RecyclerView
10015          *
10016          * @return Padding in pixels
10017          */
10018         @Px
getPaddingTop()10019         public int getPaddingTop() {
10020             return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
10021         }
10022 
10023         /**
10024          * Return the right padding of the parent RecyclerView
10025          *
10026          * @return Padding in pixels
10027          */
10028         @Px
getPaddingRight()10029         public int getPaddingRight() {
10030             return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
10031         }
10032 
10033         /**
10034          * Return the bottom padding of the parent RecyclerView
10035          *
10036          * @return Padding in pixels
10037          */
10038         @Px
getPaddingBottom()10039         public int getPaddingBottom() {
10040             return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
10041         }
10042 
10043         /**
10044          * Return the start padding of the parent RecyclerView
10045          *
10046          * @return Padding in pixels
10047          */
10048         @Px
getPaddingStart()10049         public int getPaddingStart() {
10050             return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;
10051         }
10052 
10053         /**
10054          * Return the end padding of the parent RecyclerView
10055          *
10056          * @return Padding in pixels
10057          */
10058         @Px
getPaddingEnd()10059         public int getPaddingEnd() {
10060             return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;
10061         }
10062 
10063         /**
10064          * Returns true if the RecyclerView this LayoutManager is bound to has focus.
10065          *
10066          * @return True if the RecyclerView has focus, false otherwise.
10067          * @see View#isFocused()
10068          */
isFocused()10069         public boolean isFocused() {
10070             return mRecyclerView != null && mRecyclerView.isFocused();
10071         }
10072 
10073         /**
10074          * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
10075          *
10076          * @return true if the RecyclerView has or contains focus
10077          * @see View#hasFocus()
10078          */
hasFocus()10079         public boolean hasFocus() {
10080             return mRecyclerView != null && mRecyclerView.hasFocus();
10081         }
10082 
10083         /**
10084          * Returns the item View which has or contains focus.
10085          *
10086          * @return A direct child of RecyclerView which has focus or contains the focused child.
10087          */
getFocusedChild()10088         public @Nullable View getFocusedChild() {
10089             if (mRecyclerView == null) {
10090                 return null;
10091             }
10092             final View focused = mRecyclerView.getFocusedChild();
10093             if (focused == null || mChildHelper.isHidden(focused)) {
10094                 return null;
10095             }
10096             return focused;
10097         }
10098 
10099         /**
10100          * Returns the number of items in the adapter bound to the parent RecyclerView.
10101          * <p>
10102          * Note that this number is not necessarily equal to
10103          * {@link State#getItemCount() State#getItemCount()}. In methods where {@link State} is
10104          * available, you should use {@link State#getItemCount() State#getItemCount()} instead.
10105          * For more details, check the documentation for
10106          * {@link State#getItemCount() State#getItemCount()}.
10107          *
10108          * @return The number of items in the bound adapter
10109          * @see State#getItemCount()
10110          */
getItemCount()10111         public int getItemCount() {
10112             final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
10113             return a != null ? a.getItemCount() : 0;
10114         }
10115 
10116         /**
10117          * Offset all child views attached to the parent RecyclerView by dx pixels along
10118          * the horizontal axis.
10119          *
10120          * @param dx Pixels to offset by
10121          */
offsetChildrenHorizontal(@x int dx)10122         public void offsetChildrenHorizontal(@Px int dx) {
10123             if (mRecyclerView != null) {
10124                 mRecyclerView.offsetChildrenHorizontal(dx);
10125             }
10126         }
10127 
10128         /**
10129          * Offset all child views attached to the parent RecyclerView by dy pixels along
10130          * the vertical axis.
10131          *
10132          * @param dy Pixels to offset by
10133          */
offsetChildrenVertical(@x int dy)10134         public void offsetChildrenVertical(@Px int dy) {
10135             if (mRecyclerView != null) {
10136                 mRecyclerView.offsetChildrenVertical(dy);
10137             }
10138         }
10139 
10140         /**
10141          * Flags a view so that it will not be scrapped or recycled.
10142          * <p>
10143          * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
10144          * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
10145          * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
10146          * ignore the child.
10147          * <p>
10148          * Before this child can be recycled again, you have to call
10149          * {@link #stopIgnoringView(View)}.
10150          * <p>
10151          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
10152          *
10153          * @param view View to ignore.
10154          * @see #stopIgnoringView(View)
10155          */
ignoreView(@onNull View view)10156         public void ignoreView(@NonNull View view) {
10157             if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
10158                 // checking this because calling this method on a recycled or detached view may
10159                 // cause loss of state.
10160                 throw new IllegalArgumentException("View should be fully attached to be ignored"
10161                         + mRecyclerView.exceptionLabel());
10162             }
10163             final ViewHolder vh = getChildViewHolderInt(view);
10164             vh.addFlags(ViewHolder.FLAG_IGNORE);
10165             mRecyclerView.mViewInfoStore.removeViewHolder(vh);
10166         }
10167 
10168         /**
10169          * View can be scrapped and recycled again.
10170          * <p>
10171          * Note that calling this method removes all information in the view holder.
10172          * <p>
10173          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
10174          *
10175          * @param view View to ignore.
10176          */
stopIgnoringView(@onNull View view)10177         public void stopIgnoringView(@NonNull View view) {
10178             final ViewHolder vh = getChildViewHolderInt(view);
10179             vh.stopIgnoring();
10180             vh.resetInternal();
10181             vh.addFlags(ViewHolder.FLAG_INVALID);
10182         }
10183 
10184         /**
10185          * Temporarily detach and scrap all currently attached child views. Views will be scrapped
10186          * into the given Recycler. The Recycler may prefer to reuse scrap views before
10187          * other views that were previously recycled.
10188          *
10189          * @param recycler Recycler to scrap views into
10190          */
detachAndScrapAttachedViews(@onNull Recycler recycler)10191         public void detachAndScrapAttachedViews(@NonNull Recycler recycler) {
10192             final int childCount = getChildCount();
10193             for (int i = childCount - 1; i >= 0; i--) {
10194                 final View v = getChildAt(i);
10195                 scrapOrRecycleView(recycler, i, v);
10196             }
10197         }
10198 
scrapOrRecycleView(Recycler recycler, int index, View view)10199         private void scrapOrRecycleView(Recycler recycler, int index, View view) {
10200             final ViewHolder viewHolder = getChildViewHolderInt(view);
10201             if (viewHolder.shouldIgnore()) {
10202                 if (sVerboseLoggingEnabled) {
10203                     Log.d(TAG, "ignoring view " + viewHolder);
10204                 }
10205                 return;
10206             }
10207             if (viewHolder.isInvalid() && !viewHolder.isRemoved()
10208                     && !mRecyclerView.mAdapter.hasStableIds()) {
10209                 removeViewAt(index);
10210                 recycler.recycleViewHolderInternal(viewHolder);
10211             } else {
10212                 detachViewAt(index);
10213                 recycler.scrapView(view);
10214                 mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
10215             }
10216         }
10217 
10218         /**
10219          * Recycles the scrapped views.
10220          * <p>
10221          * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
10222          * the expected behavior if scrapped views are used for animations. Otherwise, we need to
10223          * call remove and invalidate RecyclerView to ensure UI update.
10224          *
10225          * @param recycler Recycler
10226          */
removeAndRecycleScrapInt(Recycler recycler)10227         void removeAndRecycleScrapInt(Recycler recycler) {
10228             final int scrapCount = recycler.getScrapCount();
10229             // Loop backward, recycler might be changed by removeDetachedView()
10230             for (int i = scrapCount - 1; i >= 0; i--) {
10231                 final View scrap = recycler.getScrapViewAt(i);
10232                 final ViewHolder vh = getChildViewHolderInt(scrap);
10233                 if (vh.shouldIgnore()) {
10234                     continue;
10235                 }
10236                 // If the scrap view is animating, we need to cancel them first. If we cancel it
10237                 // here, ItemAnimator callback may recycle it which will cause double recycling.
10238                 // To avoid this, we mark it as not recyclable before calling the item animator.
10239                 // Since removeDetachedView calls a user API, a common mistake (ending animations on
10240                 // the view) may recycle it too, so we guard it before we call user APIs.
10241                 vh.setIsRecyclable(false);
10242                 if (vh.isTmpDetached()) {
10243                     mRecyclerView.removeDetachedView(scrap, false);
10244                 }
10245                 if (mRecyclerView.mItemAnimator != null) {
10246                     mRecyclerView.mItemAnimator.endAnimation(vh);
10247                 }
10248                 vh.setIsRecyclable(true);
10249                 recycler.quickRecycleScrapView(scrap);
10250             }
10251             recycler.clearScrap();
10252             if (scrapCount > 0) {
10253                 mRecyclerView.invalidate();
10254             }
10255         }
10256 
10257 
10258         /**
10259          * Measure a child view using standard measurement policy, taking the padding
10260          * of the parent RecyclerView and any added item decorations into account.
10261          *
10262          * <p>If the RecyclerView can be scrolled in either dimension the caller may
10263          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
10264          *
10265          * @param child      Child view to measure
10266          * @param widthUsed  Width in pixels currently consumed by other views, if relevant
10267          * @param heightUsed Height in pixels currently consumed by other views, if relevant
10268          */
measureChild(@onNull View child, int widthUsed, int heightUsed)10269         public void measureChild(@NonNull View child, int widthUsed, int heightUsed) {
10270             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
10271 
10272             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
10273             widthUsed += insets.left + insets.right;
10274             heightUsed += insets.top + insets.bottom;
10275             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
10276                     getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
10277                     canScrollHorizontally());
10278             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
10279                     getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
10280                     canScrollVertically());
10281             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
10282                 child.measure(widthSpec, heightSpec);
10283             }
10284         }
10285 
10286         /**
10287          * RecyclerView internally does its own View measurement caching which should help with
10288          * WRAP_CONTENT.
10289          * <p>
10290          * Use this method if the View is already measured once in this layout pass.
10291          */
shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp)10292         boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
10293             return !mMeasurementCacheEnabled
10294                     || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
10295                     || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
10296         }
10297 
10298         // we may consider making this public
10299 
10300         /**
10301          * RecyclerView internally does its own View measurement caching which should help with
10302          * WRAP_CONTENT.
10303          * <p>
10304          * Use this method if the View is not yet measured and you need to decide whether to
10305          * measure this View or not.
10306          */
shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp)10307         boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
10308             return child.isLayoutRequested()
10309                     || !mMeasurementCacheEnabled
10310                     || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
10311                     || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
10312         }
10313 
10314         /**
10315          * In addition to the View Framework's measurement cache, RecyclerView uses its own
10316          * additional measurement cache for its children to avoid re-measuring them when not
10317          * necessary. It is on by default but it can be turned off via
10318          * {@link #setMeasurementCacheEnabled(boolean)}.
10319          *
10320          * @return True if measurement cache is enabled, false otherwise.
10321          * @see #setMeasurementCacheEnabled(boolean)
10322          */
isMeasurementCacheEnabled()10323         public boolean isMeasurementCacheEnabled() {
10324             return mMeasurementCacheEnabled;
10325         }
10326 
10327         /**
10328          * Sets whether RecyclerView should use its own measurement cache for the children. This is
10329          * a more aggressive cache than the framework uses.
10330          *
10331          * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
10332          * @see #isMeasurementCacheEnabled()
10333          */
setMeasurementCacheEnabled(boolean measurementCacheEnabled)10334         public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
10335             mMeasurementCacheEnabled = measurementCacheEnabled;
10336         }
10337 
isMeasurementUpToDate(int childSize, int spec, int dimension)10338         private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
10339             final int specMode = MeasureSpec.getMode(spec);
10340             final int specSize = MeasureSpec.getSize(spec);
10341             if (dimension > 0 && childSize != dimension) {
10342                 return false;
10343             }
10344             switch (specMode) {
10345                 case MeasureSpec.UNSPECIFIED:
10346                     return true;
10347                 case MeasureSpec.AT_MOST:
10348                     return specSize >= childSize;
10349                 case MeasureSpec.EXACTLY:
10350                     return specSize == childSize;
10351             }
10352             return false;
10353         }
10354 
10355         /**
10356          * Measure a child view using standard measurement policy, taking the padding
10357          * of the parent RecyclerView, any added item decorations and the child margins
10358          * into account.
10359          *
10360          * <p>If the RecyclerView can be scrolled in either dimension the caller may
10361          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
10362          *
10363          * @param child      Child view to measure
10364          * @param widthUsed  Width in pixels currently consumed by other views, if relevant
10365          * @param heightUsed Height in pixels currently consumed by other views, if relevant
10366          */
measureChildWithMargins(@onNull View child, int widthUsed, int heightUsed)10367         public void measureChildWithMargins(@NonNull View child, int widthUsed, int heightUsed) {
10368             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
10369 
10370             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
10371             widthUsed += insets.left + insets.right;
10372             heightUsed += insets.top + insets.bottom;
10373 
10374             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
10375                     getPaddingLeft() + getPaddingRight()
10376                             + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
10377                     canScrollHorizontally());
10378             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
10379                     getPaddingTop() + getPaddingBottom()
10380                             + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
10381                     canScrollVertically());
10382             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
10383                 child.measure(widthSpec, heightSpec);
10384             }
10385         }
10386 
10387         /**
10388          * Calculate a MeasureSpec value for measuring a child view in one dimension.
10389          *
10390          * @param parentSize     Size of the parent view where the child will be placed
10391          * @param padding        Total space currently consumed by other elements of the parent
10392          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
10393          *                       Generally obtained from the child view's LayoutParams
10394          * @param canScroll      true if the parent RecyclerView can scroll in this dimension
10395          * @return a MeasureSpec value for the child view
10396          * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
10397          */
10398         @Deprecated
getChildMeasureSpec(int parentSize, int padding, int childDimension, boolean canScroll)10399         public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
10400                 boolean canScroll) {
10401             int size = Math.max(0, parentSize - padding);
10402             int resultSize = 0;
10403             int resultMode = 0;
10404             if (canScroll) {
10405                 if (childDimension >= 0) {
10406                     resultSize = childDimension;
10407                     resultMode = MeasureSpec.EXACTLY;
10408                 } else {
10409                     // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
10410                     // instead using UNSPECIFIED.
10411                     resultSize = 0;
10412                     resultMode = MeasureSpec.UNSPECIFIED;
10413                 }
10414             } else {
10415                 if (childDimension >= 0) {
10416                     resultSize = childDimension;
10417                     resultMode = MeasureSpec.EXACTLY;
10418                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
10419                     resultSize = size;
10420                     // TODO this should be my spec.
10421                     resultMode = MeasureSpec.EXACTLY;
10422                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
10423                     resultSize = size;
10424                     resultMode = MeasureSpec.AT_MOST;
10425                 }
10426             }
10427             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
10428         }
10429 
10430         /**
10431          * Calculate a MeasureSpec value for measuring a child view in one dimension.
10432          *
10433          * @param parentSize     Size of the parent view where the child will be placed
10434          * @param parentMode     The measurement spec mode of the parent
10435          * @param padding        Total space currently consumed by other elements of parent
10436          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
10437          *                       Generally obtained from the child view's LayoutParams
10438          * @param canScroll      true if the parent RecyclerView can scroll in this dimension
10439          * @return a MeasureSpec value for the child view
10440          */
getChildMeasureSpec(int parentSize, int parentMode, int padding, int childDimension, boolean canScroll)10441         public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
10442                 int childDimension, boolean canScroll) {
10443             int size = Math.max(0, parentSize - padding);
10444             int resultSize = 0;
10445             int resultMode = 0;
10446             if (canScroll) {
10447                 if (childDimension >= 0) {
10448                     resultSize = childDimension;
10449                     resultMode = MeasureSpec.EXACTLY;
10450                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
10451                     switch (parentMode) {
10452                         case MeasureSpec.AT_MOST:
10453                         case MeasureSpec.EXACTLY:
10454                             resultSize = size;
10455                             resultMode = parentMode;
10456                             break;
10457                         case MeasureSpec.UNSPECIFIED:
10458                             resultSize = 0;
10459                             resultMode = MeasureSpec.UNSPECIFIED;
10460                             break;
10461                     }
10462                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
10463                     resultSize = 0;
10464                     resultMode = MeasureSpec.UNSPECIFIED;
10465                 }
10466             } else {
10467                 if (childDimension >= 0) {
10468                     resultSize = childDimension;
10469                     resultMode = MeasureSpec.EXACTLY;
10470                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
10471                     resultSize = size;
10472                     resultMode = parentMode;
10473                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
10474                     resultSize = size;
10475                     if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
10476                         resultMode = MeasureSpec.AT_MOST;
10477                     } else {
10478                         resultMode = MeasureSpec.UNSPECIFIED;
10479                     }
10480 
10481                 }
10482             }
10483             //noinspection WrongConstant
10484             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
10485         }
10486 
10487         /**
10488          * Returns the measured width of the given child, plus the additional size of
10489          * any insets applied by {@link ItemDecoration ItemDecorations}.
10490          *
10491          * @param child Child view to query
10492          * @return child's measured width plus <code>ItemDecoration</code> insets
10493          * @see View#getMeasuredWidth()
10494          */
getDecoratedMeasuredWidth(@onNull View child)10495         public int getDecoratedMeasuredWidth(@NonNull View child) {
10496             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
10497             return child.getMeasuredWidth() + insets.left + insets.right;
10498         }
10499 
10500         /**
10501          * Returns the measured height of the given child, plus the additional size of
10502          * any insets applied by {@link ItemDecoration ItemDecorations}.
10503          *
10504          * @param child Child view to query
10505          * @return child's measured height plus <code>ItemDecoration</code> insets
10506          * @see View#getMeasuredHeight()
10507          */
getDecoratedMeasuredHeight(@onNull View child)10508         public int getDecoratedMeasuredHeight(@NonNull View child) {
10509             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
10510             return child.getMeasuredHeight() + insets.top + insets.bottom;
10511         }
10512 
10513         /**
10514          * Lay out the given child view within the RecyclerView using coordinates that
10515          * include any current {@link ItemDecoration ItemDecorations}.
10516          *
10517          * <p>LayoutManagers should prefer working in sizes and coordinates that include
10518          * item decoration insets whenever possible. This allows the LayoutManager to effectively
10519          * ignore decoration insets within measurement and layout code. See the following
10520          * methods:</p>
10521          * <ul>
10522          *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
10523          *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
10524          *     <li>{@link #measureChild(View, int, int)}</li>
10525          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
10526          *     <li>{@link #getDecoratedLeft(View)}</li>
10527          *     <li>{@link #getDecoratedTop(View)}</li>
10528          *     <li>{@link #getDecoratedRight(View)}</li>
10529          *     <li>{@link #getDecoratedBottom(View)}</li>
10530          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
10531          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
10532          * </ul>
10533          *
10534          * @param child  Child to lay out
10535          * @param left   Left edge, with item decoration insets included
10536          * @param top    Top edge, with item decoration insets included
10537          * @param right  Right edge, with item decoration insets included
10538          * @param bottom Bottom edge, with item decoration insets included
10539          * @see View#layout(int, int, int, int)
10540          * @see #layoutDecoratedWithMargins(View, int, int, int, int)
10541          */
layoutDecorated(@onNull View child, int left, int top, int right, int bottom)10542         public void layoutDecorated(@NonNull View child, int left, int top, int right, int bottom) {
10543             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
10544             child.layout(left + insets.left, top + insets.top, right - insets.right,
10545                     bottom - insets.bottom);
10546         }
10547 
10548         /**
10549          * Lay out the given child view within the RecyclerView using coordinates that
10550          * include any current {@link ItemDecoration ItemDecorations} and margins.
10551          *
10552          * <p>LayoutManagers should prefer working in sizes and coordinates that include
10553          * item decoration insets whenever possible. This allows the LayoutManager to effectively
10554          * ignore decoration insets within measurement and layout code. See the following
10555          * methods:</p>
10556          * <ul>
10557          *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
10558          *     <li>{@link #measureChild(View, int, int)}</li>
10559          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
10560          *     <li>{@link #getDecoratedLeft(View)}</li>
10561          *     <li>{@link #getDecoratedTop(View)}</li>
10562          *     <li>{@link #getDecoratedRight(View)}</li>
10563          *     <li>{@link #getDecoratedBottom(View)}</li>
10564          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
10565          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
10566          * </ul>
10567          *
10568          * @param child  Child to lay out
10569          * @param left   Left edge, with item decoration insets and left margin included
10570          * @param top    Top edge, with item decoration insets and top margin included
10571          * @param right  Right edge, with item decoration insets and right margin included
10572          * @param bottom Bottom edge, with item decoration insets and bottom margin included
10573          * @see View#layout(int, int, int, int)
10574          * @see #layoutDecorated(View, int, int, int, int)
10575          */
layoutDecoratedWithMargins(@onNull View child, int left, int top, int right, int bottom)10576         public void layoutDecoratedWithMargins(@NonNull View child, int left, int top, int right,
10577                 int bottom) {
10578             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
10579             final Rect insets = lp.mDecorInsets;
10580             child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
10581                     right - insets.right - lp.rightMargin,
10582                     bottom - insets.bottom - lp.bottomMargin);
10583         }
10584 
10585         /**
10586          * Calculates the bounding box of the View while taking into account its matrix changes
10587          * (translation, scale etc) with respect to the RecyclerView.
10588          * <p>
10589          * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
10590          * the View's matrix so that the decor offsets also go through the same transformation.
10591          *
10592          * @param child              The ItemView whose bounding box should be calculated.
10593          * @param includeDecorInsets True if the decor insets should be included in the bounding box
10594          * @param out                The rectangle into which the output will be written.
10595          */
getTransformedBoundingBox(@onNull View child, boolean includeDecorInsets, @NonNull Rect out)10596         public void getTransformedBoundingBox(@NonNull View child, boolean includeDecorInsets,
10597                 @NonNull Rect out) {
10598             if (includeDecorInsets) {
10599                 Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
10600                 out.set(-insets.left, -insets.top,
10601                         child.getWidth() + insets.right, child.getHeight() + insets.bottom);
10602             } else {
10603                 out.set(0, 0, child.getWidth(), child.getHeight());
10604             }
10605 
10606             if (mRecyclerView != null) {
10607                 final Matrix childMatrix = child.getMatrix();
10608                 if (childMatrix != null && !childMatrix.isIdentity()) {
10609                     final RectF tempRectF = mRecyclerView.mTempRectF;
10610                     tempRectF.set(out);
10611                     childMatrix.mapRect(tempRectF);
10612                     out.set(
10613                             (int) Math.floor(tempRectF.left),
10614                             (int) Math.floor(tempRectF.top),
10615                             (int) Math.ceil(tempRectF.right),
10616                             (int) Math.ceil(tempRectF.bottom)
10617                     );
10618                 }
10619             }
10620             out.offset(child.getLeft(), child.getTop());
10621         }
10622 
10623         /**
10624          * Returns the bounds of the view including its decoration and margins.
10625          *
10626          * @param view      The view element to check
10627          * @param outBounds A rect that will receive the bounds of the element including its
10628          *                  decoration and margins.
10629          */
getDecoratedBoundsWithMargins(@onNull View view, @NonNull Rect outBounds)10630         public void getDecoratedBoundsWithMargins(@NonNull View view, @NonNull Rect outBounds) {
10631             RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
10632         }
10633 
10634         /**
10635          * Returns the left edge of the given child view within its parent, offset by any applied
10636          * {@link ItemDecoration ItemDecorations}.
10637          *
10638          * @param child Child to query
10639          * @return Child left edge with offsets applied
10640          * @see #getLeftDecorationWidth(View)
10641          */
getDecoratedLeft(@onNull View child)10642         public int getDecoratedLeft(@NonNull View child) {
10643             return child.getLeft() - getLeftDecorationWidth(child);
10644         }
10645 
10646         /**
10647          * Returns the top edge of the given child view within its parent, offset by any applied
10648          * {@link ItemDecoration ItemDecorations}.
10649          *
10650          * @param child Child to query
10651          * @return Child top edge with offsets applied
10652          * @see #getTopDecorationHeight(View)
10653          */
getDecoratedTop(@onNull View child)10654         public int getDecoratedTop(@NonNull View child) {
10655             return child.getTop() - getTopDecorationHeight(child);
10656         }
10657 
10658         /**
10659          * Returns the right edge of the given child view within its parent, offset by any applied
10660          * {@link ItemDecoration ItemDecorations}.
10661          *
10662          * @param child Child to query
10663          * @return Child right edge with offsets applied
10664          * @see #getRightDecorationWidth(View)
10665          */
getDecoratedRight(@onNull View child)10666         public int getDecoratedRight(@NonNull View child) {
10667             return child.getRight() + getRightDecorationWidth(child);
10668         }
10669 
10670         /**
10671          * Returns the bottom edge of the given child view within its parent, offset by any applied
10672          * {@link ItemDecoration ItemDecorations}.
10673          *
10674          * @param child Child to query
10675          * @return Child bottom edge with offsets applied
10676          * @see #getBottomDecorationHeight(View)
10677          */
getDecoratedBottom(@onNull View child)10678         public int getDecoratedBottom(@NonNull View child) {
10679             return child.getBottom() + getBottomDecorationHeight(child);
10680         }
10681 
10682         /**
10683          * Calculates the item decor insets applied to the given child and updates the provided
10684          * Rect instance with the inset values.
10685          * <ul>
10686          *     <li>The Rect's left is set to the total width of left decorations.</li>
10687          *     <li>The Rect's top is set to the total height of top decorations.</li>
10688          *     <li>The Rect's right is set to the total width of right decorations.</li>
10689          *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
10690          * </ul>
10691          * <p>
10692          * Note that item decorations are automatically calculated when one of the LayoutManager's
10693          * measure child methods is called. If you need to measure the child with custom specs via
10694          * {@link View#measure(int, int)}, you can use this method to get decorations.
10695          *
10696          * @param child   The child view whose decorations should be calculated
10697          * @param outRect The Rect to hold result values
10698          */
calculateItemDecorationsForChild(@onNull View child, @NonNull Rect outRect)10699         public void calculateItemDecorationsForChild(@NonNull View child, @NonNull Rect outRect) {
10700             if (mRecyclerView == null) {
10701                 outRect.set(0, 0, 0, 0);
10702                 return;
10703             }
10704             Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
10705             outRect.set(insets);
10706         }
10707 
10708         /**
10709          * Returns the total height of item decorations applied to child's top.
10710          * <p>
10711          * Note that this value is not updated until the View is measured or
10712          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
10713          *
10714          * @param child Child to query
10715          * @return The total height of item decorations applied to the child's top.
10716          * @see #getDecoratedTop(View)
10717          * @see #calculateItemDecorationsForChild(View, Rect)
10718          */
getTopDecorationHeight(@onNull View child)10719         public int getTopDecorationHeight(@NonNull View child) {
10720             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
10721         }
10722 
10723         /**
10724          * Returns the total height of item decorations applied to child's bottom.
10725          * <p>
10726          * Note that this value is not updated until the View is measured or
10727          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
10728          *
10729          * @param child Child to query
10730          * @return The total height of item decorations applied to the child's bottom.
10731          * @see #getDecoratedBottom(View)
10732          * @see #calculateItemDecorationsForChild(View, Rect)
10733          */
getBottomDecorationHeight(@onNull View child)10734         public int getBottomDecorationHeight(@NonNull View child) {
10735             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
10736         }
10737 
10738         /**
10739          * Returns the total width of item decorations applied to child's left.
10740          * <p>
10741          * Note that this value is not updated until the View is measured or
10742          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
10743          *
10744          * @param child Child to query
10745          * @return The total width of item decorations applied to the child's left.
10746          * @see #getDecoratedLeft(View)
10747          * @see #calculateItemDecorationsForChild(View, Rect)
10748          */
getLeftDecorationWidth(@onNull View child)10749         public int getLeftDecorationWidth(@NonNull View child) {
10750             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
10751         }
10752 
10753         /**
10754          * Returns the total width of item decorations applied to child's right.
10755          * <p>
10756          * Note that this value is not updated until the View is measured or
10757          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
10758          *
10759          * @param child Child to query
10760          * @return The total width of item decorations applied to the child's right.
10761          * @see #getDecoratedRight(View)
10762          * @see #calculateItemDecorationsForChild(View, Rect)
10763          */
getRightDecorationWidth(@onNull View child)10764         public int getRightDecorationWidth(@NonNull View child) {
10765             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
10766         }
10767 
10768         /**
10769          * Called when searching for a focusable view in the given direction has failed
10770          * for the current content of the RecyclerView.
10771          *
10772          * <p>This is the LayoutManager's opportunity to populate views in the given direction
10773          * to fulfill the request if it can. The LayoutManager should attach and return
10774          * the view to be focused, if a focusable view in the given direction is found.
10775          * Otherwise, if all the existing (or the newly populated views) are unfocusable, it returns
10776          * the next unfocusable view to become visible on the screen. This unfocusable view is
10777          * typically the first view that's either partially or fully out of RV's padded bounded
10778          * area in the given direction. The default implementation returns null.</p>
10779          *
10780          * @param focused   The currently focused view
10781          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
10782          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
10783          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
10784          *                  or 0 for not applicable
10785          * @param recycler  The recycler to use for obtaining views for currently offscreen items
10786          * @param state     Transient state of RecyclerView
10787          * @return The chosen view to be focused if a focusable view is found, otherwise an
10788          * unfocusable view to become visible onto the screen, else null.
10789          */
onFocusSearchFailed(@onNull View focused, int direction, @NonNull Recycler recycler, @NonNull State state)10790         public @Nullable View onFocusSearchFailed(@NonNull View focused, int direction,
10791                 @NonNull Recycler recycler, @NonNull State state) {
10792             return null;
10793         }
10794 
10795         /**
10796          * This method gives a LayoutManager an opportunity to intercept the initial focus search
10797          * before the default behavior of {@link FocusFinder} is used. If this method returns
10798          * null FocusFinder will attempt to find a focusable child view. If it fails
10799          * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
10800          * will be called to give the LayoutManager an opportunity to add new views for items
10801          * that did not have attached views representing them. The LayoutManager should not add
10802          * or remove views from this method.
10803          *
10804          * @param focused   The currently focused view
10805          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
10806          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
10807          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
10808          * @return A descendant view to focus or null to fall back to default behavior.
10809          * The default implementation returns null.
10810          */
onInterceptFocusSearch(@onNull View focused, int direction)10811         public @Nullable View onInterceptFocusSearch(@NonNull View focused, int direction) {
10812             return null;
10813         }
10814 
10815         /**
10816          * Returns the scroll amount that brings the given rect in child's coordinate system within
10817          * the padded area of RecyclerView.
10818          *
10819          * @param child The direct child making the request.
10820          * @param rect  The rectangle in the child's coordinates the child
10821          *              wishes to be on the screen.
10822          * @return The array containing the scroll amount in x and y directions that brings the
10823          * given rect into RV's padded area.
10824          */
getChildRectangleOnScreenScrollAmount(View child, Rect rect)10825         private int[] getChildRectangleOnScreenScrollAmount(View child, Rect rect) {
10826             int[] out = new int[2];
10827             final int parentLeft = getPaddingLeft();
10828             final int parentTop = getPaddingTop();
10829             final int parentRight = getWidth() - getPaddingRight();
10830             final int parentBottom = getHeight() - getPaddingBottom();
10831             final int childLeft = child.getLeft() + rect.left - child.getScrollX();
10832             final int childTop = child.getTop() + rect.top - child.getScrollY();
10833             final int childRight = childLeft + rect.width();
10834             final int childBottom = childTop + rect.height();
10835 
10836             final int offScreenLeft = Math.min(0, childLeft - parentLeft);
10837             final int offScreenTop = Math.min(0, childTop - parentTop);
10838             final int offScreenRight = Math.max(0, childRight - parentRight);
10839             final int offScreenBottom = Math.max(0, childBottom - parentBottom);
10840 
10841             // Favor the "start" layout direction over the end when bringing one side or the other
10842             // of a large rect into view. If we decide to bring in end because start is already
10843             // visible, limit the scroll such that start won't go out of bounds.
10844             final int dx;
10845             if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
10846                 dx = offScreenRight != 0 ? offScreenRight
10847                         : Math.max(offScreenLeft, childRight - parentRight);
10848             } else {
10849                 dx = offScreenLeft != 0 ? offScreenLeft
10850                         : Math.min(childLeft - parentLeft, offScreenRight);
10851             }
10852 
10853             // Favor bringing the top into view over the bottom. If top is already visible and
10854             // we should scroll to make bottom visible, make sure top does not go out of bounds.
10855             final int dy = offScreenTop != 0 ? offScreenTop
10856                     : Math.min(childTop - parentTop, offScreenBottom);
10857             out[0] = dx;
10858             out[1] = dy;
10859             return out;
10860         }
10861 
10862         /**
10863          * Called when a child of the RecyclerView wants a particular rectangle to be positioned
10864          * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
10865          * android.graphics.Rect, boolean)} for more details.
10866          *
10867          * <p>The base implementation will attempt to perform a standard programmatic scroll
10868          * to bring the given rect into view, within the padded area of the RecyclerView.</p>
10869          *
10870          * @param parent    The parent RecyclerView.
10871          * @param child     The direct child making the request.
10872          * @param rect      The rectangle in the child's coordinates the child
10873          *                  wishes to be on the screen.
10874          * @param immediate True to forbid animated or delayed scrolling,
10875          *                  false otherwise
10876          * @return Whether the group scrolled to handle the operation
10877          */
requestChildRectangleOnScreen(@onNull RecyclerView parent, @NonNull View child, @NonNull Rect rect, boolean immediate)10878         public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
10879                 @NonNull View child, @NonNull Rect rect, boolean immediate) {
10880             return requestChildRectangleOnScreen(parent, child, rect, immediate, false);
10881         }
10882 
10883         /**
10884          * Requests that the given child of the RecyclerView be positioned onto the screen. This
10885          * method can be called for both unfocusable and focusable child views. For unfocusable
10886          * child views, focusedChildVisible is typically true in which case, layout manager
10887          * makes the child view visible only if the currently focused child stays in-bounds of RV.
10888          *
10889          * @param parent              The parent RecyclerView.
10890          * @param child               The direct child making the request.
10891          * @param rect                The rectangle in the child's coordinates the child
10892          *                            wishes to be on the screen.
10893          * @param immediate           True to forbid animated or delayed scrolling,
10894          *                            false otherwise
10895          * @param focusedChildVisible Whether the currently focused view must stay visible.
10896          * @return Whether the group scrolled to handle the operation
10897          */
requestChildRectangleOnScreen(@onNull RecyclerView parent, @NonNull View child, @NonNull Rect rect, boolean immediate, boolean focusedChildVisible)10898         public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
10899                 @NonNull View child, @NonNull Rect rect, boolean immediate,
10900                 boolean focusedChildVisible) {
10901             int[] scrollAmount = getChildRectangleOnScreenScrollAmount(child, rect
10902             );
10903             int dx = scrollAmount[0];
10904             int dy = scrollAmount[1];
10905             if (!focusedChildVisible || isFocusedChildVisibleAfterScrolling(parent, dx, dy)) {
10906                 if (dx != 0 || dy != 0) {
10907                     if (immediate) {
10908                         parent.scrollBy(dx, dy);
10909                     } else {
10910                         parent.smoothScrollBy(dx, dy);
10911                     }
10912                     return true;
10913                 }
10914             }
10915             return false;
10916         }
10917 
10918         /**
10919          * Returns whether the given child view is partially or fully visible within the padded
10920          * bounded area of RecyclerView, depending on the input parameters.
10921          * A view is partially visible if it has non-zero overlap with RV's padded bounded area.
10922          * If acceptEndPointInclusion flag is set to true, it's also considered partially
10923          * visible if it's located outside RV's bounds and it's hitting either RV's start or end
10924          * bounds.
10925          *
10926          * @param child                   The child view to be examined.
10927          * @param completelyVisible       If true, the method returns true if and only if the
10928          *                                child is
10929          *                                completely visible. If false, the method returns true
10930          *                                if and
10931          *                                only if the child is only partially visible (that is it
10932          *                                will
10933          *                                return false if the child is either completely visible
10934          *                                or out
10935          *                                of RV's bounds).
10936          * @param acceptEndPointInclusion If the view's endpoint intersection with RV's start of end
10937          *                                bounds is enough to consider it partially visible,
10938          *                                false otherwise.
10939          * @return True if the given child is partially or fully visible, false otherwise.
10940          */
isViewPartiallyVisible(@onNull View child, boolean completelyVisible, boolean acceptEndPointInclusion)10941         public boolean isViewPartiallyVisible(@NonNull View child, boolean completelyVisible,
10942                 boolean acceptEndPointInclusion) {
10943             int boundsFlag = (ViewBoundsCheck.FLAG_CVS_GT_PVS | ViewBoundsCheck.FLAG_CVS_EQ_PVS
10944                     | ViewBoundsCheck.FLAG_CVE_LT_PVE | ViewBoundsCheck.FLAG_CVE_EQ_PVE);
10945             boolean isViewFullyVisible = mHorizontalBoundCheck.isViewWithinBoundFlags(child,
10946                     boundsFlag)
10947                     && mVerticalBoundCheck.isViewWithinBoundFlags(child, boundsFlag);
10948             if (completelyVisible) {
10949                 return isViewFullyVisible;
10950             } else {
10951                 return !isViewFullyVisible;
10952             }
10953         }
10954 
10955         /**
10956          * Returns whether the currently focused child stays within RV's bounds with the given
10957          * amount of scrolling.
10958          *
10959          * @param parent The parent RecyclerView.
10960          * @param dx     The scrolling in x-axis direction to be performed.
10961          * @param dy     The scrolling in y-axis direction to be performed.
10962          * @return {@code false} if the focused child is not at least partially visible after
10963          * scrolling or no focused child exists, {@code true} otherwise.
10964          */
isFocusedChildVisibleAfterScrolling(RecyclerView parent, int dx, int dy)10965         private boolean isFocusedChildVisibleAfterScrolling(RecyclerView parent, int dx, int dy) {
10966             final View focusedChild = parent.getFocusedChild();
10967             if (focusedChild == null) {
10968                 return false;
10969             }
10970             final int parentLeft = getPaddingLeft();
10971             final int parentTop = getPaddingTop();
10972             final int parentRight = getWidth() - getPaddingRight();
10973             final int parentBottom = getHeight() - getPaddingBottom();
10974             final Rect bounds = mRecyclerView.mTempRect;
10975             getDecoratedBoundsWithMargins(focusedChild, bounds);
10976 
10977             if (bounds.left - dx >= parentRight || bounds.right - dx <= parentLeft
10978                     || bounds.top - dy >= parentBottom || bounds.bottom - dy <= parentTop) {
10979                 return false;
10980             }
10981             return true;
10982         }
10983 
10984         /**
10985          * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
10986          */
10987         @Deprecated
onRequestChildFocus(@onNull RecyclerView parent, @NonNull View child, @Nullable View focused)10988         public boolean onRequestChildFocus(@NonNull RecyclerView parent, @NonNull View child,
10989                 @Nullable View focused) {
10990             // eat the request if we are in the middle of a scroll or layout
10991             return isSmoothScrolling() || parent.isComputingLayout();
10992         }
10993 
10994         /**
10995          * Called when a descendant view of the RecyclerView requests focus.
10996          *
10997          * <p>A LayoutManager wishing to keep focused views aligned in a specific
10998          * portion of the view may implement that behavior in an override of this method.</p>
10999          *
11000          * <p>If the LayoutManager executes different behavior that should override the default
11001          * behavior of scrolling the focused child on screen instead of running alongside it,
11002          * this method should return true.</p>
11003          *
11004          * @param parent  The RecyclerView hosting this LayoutManager
11005          * @param state   Current state of RecyclerView
11006          * @param child   Direct child of the RecyclerView containing the newly focused view
11007          * @param focused The newly focused view. This may be the same view as child or it may be
11008          *                null
11009          * @return true if the default scroll behavior should be suppressed
11010          */
onRequestChildFocus(@onNull RecyclerView parent, @NonNull State state, @NonNull View child, @Nullable View focused)11011         public boolean onRequestChildFocus(@NonNull RecyclerView parent, @NonNull State state,
11012                 @NonNull View child, @Nullable View focused) {
11013             return onRequestChildFocus(parent, child, focused);
11014         }
11015 
11016         /**
11017          * Called if the RecyclerView this LayoutManager is bound to has a different adapter set via
11018          * {@link RecyclerView#setAdapter(Adapter)} or
11019          * {@link RecyclerView#swapAdapter(Adapter, boolean)}. The LayoutManager may use this
11020          * opportunity to clear caches and configure state such that it can relayout appropriately
11021          * with the new data and potentially new view types.
11022          *
11023          * <p>The default implementation removes all currently attached views.</p>
11024          *
11025          * @param oldAdapter The previous adapter instance. Will be null if there was previously no
11026          *                   adapter.
11027          * @param newAdapter The new adapter instance. Might be null if
11028          *                   {@link RecyclerView#setAdapter(RecyclerView.Adapter)} is called with
11029          *                   {@code null}.
11030          */
onAdapterChanged(@ullable Adapter oldAdapter, @Nullable Adapter newAdapter)11031         public void onAdapterChanged(@Nullable Adapter oldAdapter, @Nullable Adapter newAdapter) {
11032         }
11033 
11034         /**
11035          * Called to populate focusable views within the RecyclerView.
11036          *
11037          * <p>The LayoutManager implementation should return <code>true</code> if the default
11038          * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
11039          * suppressed.</p>
11040          *
11041          * <p>The default implementation returns <code>false</code> to trigger RecyclerView
11042          * to fall back to the default ViewGroup behavior.</p>
11043          *
11044          * @param recyclerView  The RecyclerView hosting this LayoutManager
11045          * @param views         List of output views. This method should add valid focusable views
11046          *                      to this list.
11047          * @param direction     One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
11048          *                      {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
11049          *                      {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
11050          * @param focusableMode The type of focusables to be added.
11051          * @return true to suppress the default behavior, false to add default focusables after
11052          * this method returns.
11053          * @see #FOCUSABLES_ALL
11054          * @see #FOCUSABLES_TOUCH_MODE
11055          */
onAddFocusables(@onNull RecyclerView recyclerView, @NonNull ArrayList<View> views, int direction, int focusableMode)11056         public boolean onAddFocusables(@NonNull RecyclerView recyclerView,
11057                 @NonNull ArrayList<View> views, int direction, int focusableMode) {
11058             return false;
11059         }
11060 
11061         /**
11062          * Called in response to a call to {@link Adapter#notifyDataSetChanged()} or
11063          * {@link RecyclerView#swapAdapter(Adapter, boolean)} ()} and signals that the the entire
11064          * data set has changed.
11065          */
onItemsChanged(@onNull RecyclerView recyclerView)11066         public void onItemsChanged(@NonNull RecyclerView recyclerView) {
11067         }
11068 
11069         /**
11070          * Called when items have been added to the adapter. The LayoutManager may choose to
11071          * requestLayout if the inserted items would require refreshing the currently visible set
11072          * of child views. (e.g. currently empty space would be filled by appended items, etc.)
11073          */
onItemsAdded(@onNull RecyclerView recyclerView, int positionStart, int itemCount)11074         public void onItemsAdded(@NonNull RecyclerView recyclerView, int positionStart,
11075                 int itemCount) {
11076         }
11077 
11078         /**
11079          * Called when items have been removed from the adapter.
11080          */
onItemsRemoved(@onNull RecyclerView recyclerView, int positionStart, int itemCount)11081         public void onItemsRemoved(@NonNull RecyclerView recyclerView, int positionStart,
11082                 int itemCount) {
11083         }
11084 
11085         /**
11086          * Called when items have been changed in the adapter.
11087          * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
11088          * instead, then this callback will not be invoked.
11089          */
onItemsUpdated(@onNull RecyclerView recyclerView, int positionStart, int itemCount)11090         public void onItemsUpdated(@NonNull RecyclerView recyclerView, int positionStart,
11091                 int itemCount) {
11092         }
11093 
11094         /**
11095          * Called when items have been changed in the adapter and with optional payload.
11096          * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
11097          */
onItemsUpdated(@onNull RecyclerView recyclerView, int positionStart, int itemCount, @Nullable Object payload)11098         public void onItemsUpdated(@NonNull RecyclerView recyclerView, int positionStart,
11099                 int itemCount, @Nullable Object payload) {
11100             onItemsUpdated(recyclerView, positionStart, itemCount);
11101         }
11102 
11103         /**
11104          * Called when an item is moved withing the adapter.
11105          * <p>
11106          * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
11107          * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
11108          * is called.
11109          */
onItemsMoved(@onNull RecyclerView recyclerView, int from, int to, int itemCount)11110         public void onItemsMoved(@NonNull RecyclerView recyclerView, int from, int to,
11111                 int itemCount) {
11112 
11113         }
11114 
11115 
11116         /**
11117          * <p>Override this method if you want to support scroll bars.</p>
11118          *
11119          * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
11120          *
11121          * <p>Default implementation returns 0.</p>
11122          *
11123          * @param state Current state of RecyclerView
11124          * @return The horizontal extent of the scrollbar's thumb
11125          * @see RecyclerView#computeHorizontalScrollExtent()
11126          */
computeHorizontalScrollExtent(@onNull State state)11127         public int computeHorizontalScrollExtent(@NonNull State state) {
11128             return 0;
11129         }
11130 
11131         /**
11132          * <p>Override this method if you want to support scroll bars.</p>
11133          *
11134          * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
11135          *
11136          * <p>Default implementation returns 0.</p>
11137          *
11138          * @param state Current State of RecyclerView where you can find total item count
11139          * @return The horizontal offset of the scrollbar's thumb
11140          * @see RecyclerView#computeHorizontalScrollOffset()
11141          */
computeHorizontalScrollOffset(@onNull State state)11142         public int computeHorizontalScrollOffset(@NonNull State state) {
11143             return 0;
11144         }
11145 
11146         /**
11147          * <p>Override this method if you want to support scroll bars.</p>
11148          *
11149          * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
11150          *
11151          * <p>Default implementation returns 0.</p>
11152          *
11153          * @param state Current State of RecyclerView where you can find total item count
11154          * @return The total horizontal range represented by the horizontal scrollbar
11155          * @see RecyclerView#computeHorizontalScrollRange()
11156          */
computeHorizontalScrollRange(@onNull State state)11157         public int computeHorizontalScrollRange(@NonNull State state) {
11158             return 0;
11159         }
11160 
11161         /**
11162          * <p>Override this method if you want to support scroll bars.</p>
11163          *
11164          * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
11165          *
11166          * <p>Default implementation returns 0.</p>
11167          *
11168          * @param state Current state of RecyclerView
11169          * @return The vertical extent of the scrollbar's thumb
11170          * @see RecyclerView#computeVerticalScrollExtent()
11171          */
computeVerticalScrollExtent(@onNull State state)11172         public int computeVerticalScrollExtent(@NonNull State state) {
11173             return 0;
11174         }
11175 
11176         /**
11177          * <p>Override this method if you want to support scroll bars.</p>
11178          *
11179          * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
11180          *
11181          * <p>Default implementation returns 0.</p>
11182          *
11183          * @param state Current State of RecyclerView where you can find total item count
11184          * @return The vertical offset of the scrollbar's thumb
11185          * @see RecyclerView#computeVerticalScrollOffset()
11186          */
computeVerticalScrollOffset(@onNull State state)11187         public int computeVerticalScrollOffset(@NonNull State state) {
11188             return 0;
11189         }
11190 
11191         /**
11192          * <p>Override this method if you want to support scroll bars.</p>
11193          *
11194          * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
11195          *
11196          * <p>Default implementation returns 0.</p>
11197          *
11198          * @param state Current State of RecyclerView where you can find total item count
11199          * @return The total vertical range represented by the vertical scrollbar
11200          * @see RecyclerView#computeVerticalScrollRange()
11201          */
computeVerticalScrollRange(@onNull State state)11202         public int computeVerticalScrollRange(@NonNull State state) {
11203             return 0;
11204         }
11205 
11206         /**
11207          * Measure the attached RecyclerView. Implementations must call
11208          * {@link #setMeasuredDimension(int, int)} before returning.
11209          * <p>
11210          * It is strongly advised to use the AutoMeasure mechanism by overriding
11211          * {@link #isAutoMeasureEnabled()} to return true as AutoMeasure handles all the standard
11212          * measure cases including when the RecyclerView's layout_width or layout_height have been
11213          * set to wrap_content.  If {@link #isAutoMeasureEnabled()} is overridden to return true,
11214          * this method should not be overridden.
11215          * <p>
11216          * The default implementation will handle EXACTLY measurements and respect
11217          * the minimum width and height properties of the host RecyclerView if measured
11218          * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
11219          * will consume all available space.
11220          *
11221          * @param recycler   Recycler
11222          * @param state      Transient state of RecyclerView
11223          * @param widthSpec  Width {@link android.view.View.MeasureSpec}
11224          * @param heightSpec Height {@link android.view.View.MeasureSpec}
11225          * @see #isAutoMeasureEnabled()
11226          * @see #setMeasuredDimension(int, int)
11227          */
onMeasure(@onNull Recycler recycler, @NonNull State state, int widthSpec, int heightSpec)11228         public void onMeasure(@NonNull Recycler recycler, @NonNull State state, int widthSpec,
11229                 int heightSpec) {
11230             mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
11231         }
11232 
11233         /**
11234          * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
11235          * host RecyclerView.
11236          *
11237          * @param widthSize  Measured width
11238          * @param heightSize Measured height
11239          */
setMeasuredDimension(int widthSize, int heightSize)11240         public void setMeasuredDimension(int widthSize, int heightSize) {
11241             mRecyclerView.setMeasuredDimension(widthSize, heightSize);
11242         }
11243 
11244         /**
11245          * @return The host RecyclerView's {@link View#getMinimumWidth()}
11246          */
11247         @Px
getMinimumWidth()11248         public int getMinimumWidth() {
11249             return ViewCompat.getMinimumWidth(mRecyclerView);
11250         }
11251 
11252         /**
11253          * @return The host RecyclerView's {@link View#getMinimumHeight()}
11254          */
11255         @Px
getMinimumHeight()11256         public int getMinimumHeight() {
11257             return ViewCompat.getMinimumHeight(mRecyclerView);
11258         }
11259 
11260         /**
11261          * <p>Called when the LayoutManager should save its state. This is a good time to save your
11262          * scroll position, configuration and anything else that may be required to restore the same
11263          * layout state if the LayoutManager is recreated.</p>
11264          * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
11265          * restore. This will let you share information between your LayoutManagers but it is also
11266          * your responsibility to make sure they use the same parcelable class.</p>
11267          *
11268          * @return Necessary information for LayoutManager to be able to restore its state
11269          */
onSaveInstanceState()11270         public @Nullable Parcelable onSaveInstanceState() {
11271             return null;
11272         }
11273 
11274         /**
11275          * Called when the RecyclerView is ready to restore the state based on a previous
11276          * RecyclerView.
11277          *
11278          * Notice that this might happen after an actual layout, based on how Adapter prefers to
11279          * restore State. See {@link Adapter#getStateRestorationPolicy()} for more information.
11280          *
11281          * @param state The parcelable that was returned by the previous LayoutManager's
11282          *              {@link #onSaveInstanceState()} method.
11283          */
11284         @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
onRestoreInstanceState(Parcelable state)11285         public void onRestoreInstanceState(Parcelable state) {
11286 
11287         }
11288 
stopSmoothScroller()11289         void stopSmoothScroller() {
11290             if (mSmoothScroller != null) {
11291                 mSmoothScroller.stop();
11292             }
11293         }
11294 
onSmoothScrollerStopped(SmoothScroller smoothScroller)11295         void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
11296             if (mSmoothScroller == smoothScroller) {
11297                 mSmoothScroller = null;
11298             }
11299         }
11300 
11301         /**
11302          * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
11303          *
11304          * @param state The new scroll state for RecyclerView
11305          */
onScrollStateChanged(int state)11306         public void onScrollStateChanged(int state) {
11307         }
11308 
11309         /**
11310          * Removes all views and recycles them using the given recycler.
11311          * <p>
11312          * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
11313          * <p>
11314          * If a View is marked as "ignored", it is not removed nor recycled.
11315          *
11316          * @param recycler Recycler to use to recycle children
11317          * @see #removeAndRecycleView(View, Recycler)
11318          * @see #removeAndRecycleViewAt(int, Recycler)
11319          * @see #ignoreView(View)
11320          */
removeAndRecycleAllViews(@onNull Recycler recycler)11321         public void removeAndRecycleAllViews(@NonNull Recycler recycler) {
11322             for (int i = getChildCount() - 1; i >= 0; i--) {
11323                 final View view = getChildAt(i);
11324                 if (!getChildViewHolderInt(view).shouldIgnore()) {
11325                     removeAndRecycleViewAt(i, recycler);
11326                 }
11327             }
11328         }
11329 
11330         // called by accessibility delegate
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info)11331         void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
11332             onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
11333         }
11334 
11335         /**
11336          * Called by the AccessibilityDelegate when the information about the current layout should
11337          * be populated.
11338          * <p>
11339          * Default implementation adds a {@link
11340          * androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}.
11341          * <p>
11342          * You should override
11343          * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
11344          * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
11345          * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
11346          * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
11347          * more accurate accessibility information.
11348          *
11349          * @param recycler The Recycler that can be used to convert view positions into adapter
11350          *                 positions
11351          * @param state    The current state of RecyclerView
11352          * @param info     The info that should be filled by the LayoutManager
11353          * @see View#onInitializeAccessibilityNodeInfo(
11354          *android.view.accessibility.AccessibilityNodeInfo)
11355          * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
11356          * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
11357          * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
11358          * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
11359          */
onInitializeAccessibilityNodeInfo(@onNull Recycler recycler, @NonNull State state, @NonNull AccessibilityNodeInfoCompat info)11360         public void onInitializeAccessibilityNodeInfo(@NonNull Recycler recycler,
11361                 @NonNull State state, @NonNull AccessibilityNodeInfoCompat info) {
11362             if (mRecyclerView.canScrollVertically(-1) || mRecyclerView.canScrollHorizontally(-1)) {
11363                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
11364                 info.setScrollable(true);
11365                 info.setGranularScrollingSupported(true);
11366             }
11367             if (mRecyclerView.canScrollVertically(1) || mRecyclerView.canScrollHorizontally(1)) {
11368                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
11369                 info.setScrollable(true);
11370                 info.setGranularScrollingSupported(true);
11371             }
11372             final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo =
11373                     AccessibilityNodeInfoCompat.CollectionInfoCompat
11374                             .obtain(getRowCountForAccessibility(recycler, state),
11375                                     getColumnCountForAccessibility(recycler, state),
11376                                     isLayoutHierarchical(recycler, state),
11377                                     getSelectionModeForAccessibility(recycler, state));
11378             info.setCollectionInfo(collectionInfo);
11379         }
11380 
11381         // called by accessibility delegate
onInitializeAccessibilityEvent(@onNull AccessibilityEvent event)11382         public void onInitializeAccessibilityEvent(@NonNull AccessibilityEvent event) {
11383             onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
11384         }
11385 
11386         /**
11387          * Called by the accessibility delegate to initialize an accessibility event.
11388          * <p>
11389          * Default implementation adds item count and scroll information to the event.
11390          *
11391          * @param recycler The Recycler that can be used to convert view positions into adapter
11392          *                 positions
11393          * @param state    The current state of RecyclerView
11394          * @param event    The event instance to initialize
11395          * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
11396          */
onInitializeAccessibilityEvent(@onNull Recycler recycler, @NonNull State state, @NonNull AccessibilityEvent event)11397         public void onInitializeAccessibilityEvent(@NonNull Recycler recycler, @NonNull State state,
11398                 @NonNull AccessibilityEvent event) {
11399             if (mRecyclerView == null || event == null) {
11400                 return;
11401             }
11402             event.setScrollable(mRecyclerView.canScrollVertically(1)
11403                     || mRecyclerView.canScrollVertically(-1)
11404                     || mRecyclerView.canScrollHorizontally(-1)
11405                     || mRecyclerView.canScrollHorizontally(1));
11406 
11407             if (mRecyclerView.mAdapter != null) {
11408                 event.setItemCount(mRecyclerView.mAdapter.getItemCount());
11409             }
11410         }
11411 
11412         // called by accessibility delegate
onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info)11413         void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) {
11414             final ViewHolder vh = getChildViewHolderInt(host);
11415             // avoid trying to create accessibility node info for removed children
11416             if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
11417                 onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
11418                         mRecyclerView.mState, host, info);
11419             }
11420         }
11421 
11422         /**
11423          * Called by the AccessibilityDelegate when the accessibility information for a specific
11424          * item should be populated.
11425          * <p>
11426          * Default implementation adds basic positioning information about the item.
11427          *
11428          * @param recycler The Recycler that can be used to convert view positions into adapter
11429          *                 positions
11430          * @param state    The current state of RecyclerView
11431          * @param host     The child for which accessibility node info should be populated
11432          * @param info     The info to fill out about the item
11433          * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
11434          * android.view.accessibility.AccessibilityNodeInfo)
11435          */
onInitializeAccessibilityNodeInfoForItem(@onNull Recycler recycler, @NonNull State state, @NonNull View host, @NonNull AccessibilityNodeInfoCompat info)11436         public void onInitializeAccessibilityNodeInfoForItem(@NonNull Recycler recycler,
11437                 @NonNull State state, @NonNull View host,
11438                 @NonNull AccessibilityNodeInfoCompat info) {
11439             int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
11440             int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
11441             final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo =
11442                     AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
11443                             columnIndexGuess, 1, false, false);
11444             info.setCollectionItemInfo(itemInfo);
11445         }
11446 
11447         /**
11448          * A LayoutManager can call this method to force RecyclerView to run simple animations in
11449          * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
11450          * change).
11451          * <p>
11452          * Note that, calling this method will not guarantee that RecyclerView will run animations
11453          * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
11454          * not run any animations but will still clear this flag after the layout is complete.
11455          */
requestSimpleAnimationsInNextLayout()11456         public void requestSimpleAnimationsInNextLayout() {
11457             mRequestedSimpleAnimations = true;
11458         }
11459 
11460         /**
11461          * Returns the selection mode for accessibility. Should be
11462          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE},
11463          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or
11464          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}.
11465          * <p>
11466          * Default implementation returns
11467          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
11468          *
11469          * @param recycler The Recycler that can be used to convert view positions into adapter
11470          *                 positions
11471          * @param state    The current state of RecyclerView
11472          * @return Selection mode for accessibility. Default implementation returns
11473          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
11474          */
getSelectionModeForAccessibility(@onNull Recycler recycler, @NonNull State state)11475         public int getSelectionModeForAccessibility(@NonNull Recycler recycler,
11476                 @NonNull State state) {
11477             return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
11478         }
11479 
11480         /**
11481          * Returns the number of rows for accessibility.
11482          * <p>
11483          * Default implementation returns the number of items in the adapter if LayoutManager
11484          * supports vertical scrolling or 1 if LayoutManager does not support vertical
11485          * scrolling.
11486          *
11487          * @param recycler The Recycler that can be used to convert view positions into adapter
11488          *                 positions
11489          * @param state    The current state of RecyclerView
11490          * @return The number of rows in LayoutManager for accessibility.
11491          */
getRowCountForAccessibility(@onNull Recycler recycler, @NonNull State state)11492         public int getRowCountForAccessibility(@NonNull Recycler recycler, @NonNull State state) {
11493             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
11494                 return 1;
11495             }
11496             return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
11497         }
11498 
11499         /**
11500          * Returns the number of columns for accessibility.
11501          * <p>
11502          * Default implementation returns the number of items in the adapter if LayoutManager
11503          * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
11504          * scrolling.
11505          *
11506          * @param recycler The Recycler that can be used to convert view positions into adapter
11507          *                 positions
11508          * @param state    The current state of RecyclerView
11509          * @return The number of rows in LayoutManager for accessibility.
11510          */
getColumnCountForAccessibility(@onNull Recycler recycler, @NonNull State state)11511         public int getColumnCountForAccessibility(@NonNull Recycler recycler,
11512                 @NonNull State state) {
11513             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
11514                 return 1;
11515             }
11516             return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
11517         }
11518 
11519         /**
11520          * Returns whether layout is hierarchical or not to be used for accessibility.
11521          * <p>
11522          * Default implementation returns false.
11523          *
11524          * @param recycler The Recycler that can be used to convert view positions into adapter
11525          *                 positions
11526          * @param state    The current state of RecyclerView
11527          * @return True if layout is hierarchical.
11528          */
isLayoutHierarchical(@onNull Recycler recycler, @NonNull State state)11529         public boolean isLayoutHierarchical(@NonNull Recycler recycler, @NonNull State state) {
11530             return false;
11531         }
11532 
11533         // called by accessibility delegate
performAccessibilityAction(int action, @Nullable Bundle args)11534         boolean performAccessibilityAction(int action, @Nullable Bundle args) {
11535             return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
11536                     action, args);
11537         }
11538 
11539         /**
11540          * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
11541          *
11542          * @param recycler The Recycler that can be used to convert view positions into adapter
11543          *                 positions
11544          * @param state    The current state of RecyclerView
11545          * @param action   The action to perform
11546          * @param args     Optional action arguments
11547          * @see View#performAccessibilityAction(int, android.os.Bundle)
11548          */
performAccessibilityAction(@onNull Recycler recycler, @NonNull State state, int action, @Nullable Bundle args)11549         public boolean performAccessibilityAction(@NonNull Recycler recycler, @NonNull State state,
11550                 int action, @Nullable Bundle args) {
11551             if (mRecyclerView == null) {
11552                 return false;
11553             }
11554             int vScroll = 0, hScroll = 0;
11555             int height = getHeight();
11556             int width = getWidth();
11557             Rect rect = new Rect();
11558             // Gets the visible rect on the screen except for the rotation or scale cases which
11559             // might affect the result.
11560             if (mRecyclerView.getMatrix().isIdentity() && mRecyclerView.getGlobalVisibleRect(
11561                     rect)) {
11562                 height = rect.height();
11563                 width = rect.width();
11564             }
11565 
11566             switch (action) {
11567                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
11568                     if (mRecyclerView.canScrollVertically(-1)) {
11569                         vScroll = -(height - getPaddingTop() - getPaddingBottom());
11570                     }
11571                     if (mRecyclerView.canScrollHorizontally(-1)) {
11572                         hScroll = -(width - getPaddingLeft() - getPaddingRight());
11573                     }
11574                     break;
11575                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
11576                     if (mRecyclerView.canScrollVertically(1)) {
11577                         vScroll = height - getPaddingTop() - getPaddingBottom();
11578                     }
11579                     if (mRecyclerView.canScrollHorizontally(1)) {
11580                         hScroll = width - getPaddingLeft() - getPaddingRight();
11581                     }
11582                     break;
11583             }
11584 
11585             if (vScroll == 0 && hScroll == 0) {
11586                 return false;
11587             }
11588 
11589             float granularScrollAmount = 1F; // The default value.
11590 
11591             if (args != null) {
11592                 granularScrollAmount = args.getFloat(
11593                         AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT, 1F);
11594                 if (granularScrollAmount < 0) {
11595                     if (sDebugAssertionsEnabled) {
11596                         throw new IllegalArgumentException(
11597                                 "attempting to use ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT with a "
11598                                         + "negative value (" + granularScrollAmount + ")");
11599                     }
11600                     return false;
11601                 }
11602             }
11603 
11604             if (Float.compare(granularScrollAmount, Float.POSITIVE_INFINITY) == 0) {
11605                 // Assume that the client wants to scroll as far as possible. For
11606                 // ACTION_SCROLL_BACKWARD, this means scrolling to the beginning of the collection.
11607                 // For ACTION_SCROLL_FORWARD, this means scrolling to the end of the collection.
11608 
11609                 if (mRecyclerView.mAdapter == null) {
11610                     return false;
11611                 }
11612                 switch (action) {
11613                     case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
11614                         mRecyclerView.smoothScrollToPosition(0);
11615                         break;
11616                     case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
11617                         mRecyclerView.smoothScrollToPosition(
11618                                 mRecyclerView.mAdapter.getItemCount() - 1);
11619                         break;
11620                 }
11621                 return true;
11622             }
11623 
11624             // No adjustments needed to scroll values if granular scroll amount is 1F, which is
11625             // the default, or 0F, which is undefined.
11626             if (Float.compare(1F, granularScrollAmount) != 0 && Float.compare(0F,
11627                     granularScrollAmount) != 0) {
11628                 hScroll = (int) (hScroll * granularScrollAmount);
11629                 vScroll = (int) (vScroll * granularScrollAmount);
11630             }
11631 
11632             mRecyclerView.smoothScrollBy(hScroll, vScroll, null, UNDEFINED_DURATION, true);
11633             return true;
11634         }
11635 
11636         // called by accessibility delegate
performAccessibilityActionForItem(@onNull View view, int action, @Nullable Bundle args)11637         boolean performAccessibilityActionForItem(@NonNull View view, int action,
11638                 @Nullable Bundle args) {
11639             return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
11640                     view, action, args);
11641         }
11642 
11643         /**
11644          * Called by AccessibilityDelegate when an accessibility action is requested on one of the
11645          * children of LayoutManager.
11646          * <p>
11647          * Default implementation does not do anything.
11648          *
11649          * @param recycler The Recycler that can be used to convert view positions into adapter
11650          *                 positions
11651          * @param state    The current state of RecyclerView
11652          * @param view     The child view on which the action is performed
11653          * @param action   The action to perform
11654          * @param args     Optional action arguments
11655          * @return true if action is handled
11656          * @see View#performAccessibilityAction(int, android.os.Bundle)
11657          */
performAccessibilityActionForItem(@onNull Recycler recycler, @NonNull State state, @NonNull View view, int action, @Nullable Bundle args)11658         public boolean performAccessibilityActionForItem(@NonNull Recycler recycler,
11659                 @NonNull State state, @NonNull View view, int action, @Nullable Bundle args) {
11660             return false;
11661         }
11662 
11663         /**
11664          * Parse the xml attributes to get the most common properties used by layout managers.
11665          *
11666          * {@link android.R.attr#orientation}
11667          * {@link androidx.recyclerview.R.attr#spanCount}
11668          * {@link androidx.recyclerview.R.attr#reverseLayout}
11669          * {@link androidx.recyclerview.R.attr#stackFromEnd}
11670          *
11671          * @return an object containing the properties as specified in the attrs.
11672          */
getProperties(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)11673         public static Properties getProperties(@NonNull Context context,
11674                 @Nullable AttributeSet attrs,
11675                 int defStyleAttr, int defStyleRes) {
11676             Properties properties = new Properties();
11677             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
11678                     defStyleAttr, defStyleRes);
11679             properties.orientation = a.getInt(R.styleable.RecyclerView_android_orientation,
11680                     DEFAULT_ORIENTATION);
11681             properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
11682             properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
11683             properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
11684             a.recycle();
11685             return properties;
11686         }
11687 
setExactMeasureSpecsFrom(RecyclerView recyclerView)11688         void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
11689             setMeasureSpecs(
11690                     MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
11691                     MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
11692             );
11693         }
11694 
11695         /**
11696          * Internal API to allow LayoutManagers to be measured twice.
11697          * <p>
11698          * This is not public because LayoutManagers should be able to handle their layouts in one
11699          * pass but it is very convenient to make existing LayoutManagers support wrapping content
11700          * when both orientations are undefined.
11701          * <p>
11702          * This API will be removed after default LayoutManagers properly implement wrap content in
11703          * non-scroll orientation.
11704          */
shouldMeasureTwice()11705         boolean shouldMeasureTwice() {
11706             return false;
11707         }
11708 
hasFlexibleChildInBothOrientations()11709         boolean hasFlexibleChildInBothOrientations() {
11710             final int childCount = getChildCount();
11711             for (int i = 0; i < childCount; i++) {
11712                 final View child = getChildAt(i);
11713                 final ViewGroup.LayoutParams lp = child.getLayoutParams();
11714                 if (lp.width < 0 && lp.height < 0) {
11715                     return true;
11716                 }
11717             }
11718             return false;
11719         }
11720 
11721         /**
11722          * Some general properties that a LayoutManager may want to use.
11723          */
11724         public static class Properties {
11725             /** {@link android.R.attr#orientation} */
11726             public int orientation;
11727             /** {@link androidx.recyclerview.R.attr#spanCount} */
11728             public int spanCount;
11729             /** {@link androidx.recyclerview.R.attr#reverseLayout} */
11730             public boolean reverseLayout;
11731             /** {@link androidx.recyclerview.R.attr#stackFromEnd} */
11732             public boolean stackFromEnd;
11733         }
11734     }
11735 
11736     /**
11737      * An ItemDecoration allows the application to add a special drawing and layout offset
11738      * to specific item views from the adapter's data set. This can be useful for drawing dividers
11739      * between items, highlights, visual grouping boundaries and more.
11740      *
11741      * <p>All ItemDecorations are drawn in the order they were added, before the item
11742      * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
11743      * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
11744      * RecyclerView.State)}.</p>
11745      */
11746     public abstract static class ItemDecoration {
11747         /**
11748          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
11749          * Any content drawn by this method will be drawn before the item views are drawn,
11750          * and will thus appear underneath the views.
11751          *
11752          * @param c      Canvas to draw into
11753          * @param parent RecyclerView this ItemDecoration is drawing into
11754          * @param state  The current state of RecyclerView
11755          */
onDraw(@onNull Canvas c, @NonNull RecyclerView parent, @NonNull State state)11756         public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull State state) {
11757             onDraw(c, parent);
11758         }
11759 
11760         /**
11761          * @deprecated Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
11762          */
11763         @Deprecated
onDraw(@onNull Canvas c, @NonNull RecyclerView parent)11764         public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent) {
11765         }
11766 
11767         /**
11768          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
11769          * Any content drawn by this method will be drawn after the item views are drawn
11770          * and will thus appear over the views.
11771          *
11772          * @param c      Canvas to draw into
11773          * @param parent RecyclerView this ItemDecoration is drawing into
11774          * @param state  The current state of RecyclerView.
11775          */
onDrawOver(@onNull Canvas c, @NonNull RecyclerView parent, @NonNull State state)11776         public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent,
11777                 @NonNull State state) {
11778             onDrawOver(c, parent);
11779         }
11780 
11781         /**
11782          * @deprecated Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
11783          */
11784         @Deprecated
onDrawOver(@onNull Canvas c, @NonNull RecyclerView parent)11785         public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent) {
11786         }
11787 
11788 
11789         /**
11790          * @deprecated Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
11791          */
11792         @Deprecated
getItemOffsets(@onNull Rect outRect, int itemPosition, @NonNull RecyclerView parent)11793         public void getItemOffsets(@NonNull Rect outRect, int itemPosition,
11794                 @NonNull RecyclerView parent) {
11795             outRect.set(0, 0, 0, 0);
11796         }
11797 
11798         /**
11799          * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
11800          * the number of pixels that the item view should be inset by, similar to padding or margin.
11801          * The default implementation sets the bounds of outRect to 0 and returns.
11802          *
11803          * <p>
11804          * If this ItemDecoration does not affect the positioning of item views, it should set
11805          * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
11806          * before returning.
11807          *
11808          * <p>
11809          * If you need to access Adapter for additional data, you can call
11810          * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
11811          * View.
11812          *
11813          * @param outRect Rect to receive the output.
11814          * @param view    The child view to decorate
11815          * @param parent  RecyclerView this ItemDecoration is decorating
11816          * @param state   The current state of RecyclerView.
11817          */
getItemOffsets(@onNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull State state)11818         public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
11819                 @NonNull RecyclerView parent, @NonNull State state) {
11820             getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
11821                     parent);
11822         }
11823     }
11824 
11825     /**
11826      * An OnItemTouchListener allows the application to intercept touch events in progress at the
11827      * view hierarchy level of the RecyclerView before those touch events are considered for
11828      * RecyclerView's own scrolling behavior.
11829      *
11830      * <p>This can be useful for applications that wish to implement various forms of gestural
11831      * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
11832      * a touch interaction already in progress even if the RecyclerView is already handling that
11833      * gesture stream itself for the purposes of scrolling.</p>
11834      *
11835      * @see SimpleOnItemTouchListener
11836      */
11837     public interface OnItemTouchListener {
11838         /**
11839          * Silently observe and/or take over touch events sent to the RecyclerView
11840          * before they are handled by either the RecyclerView itself or its child views.
11841          *
11842          * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
11843          * in the order in which each listener was added, before any other touch processing
11844          * by the RecyclerView itself or child views occurs.</p>
11845          *
11846          * @param rv The RecyclerView whose scroll state has changed.
11847          * @param e MotionEvent describing the touch event. All coordinates are in
11848          *          the RecyclerView's coordinate system.
11849          * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
11850          * to continue with the current behavior and continue observing future events in
11851          * the gesture.
11852          */
onInterceptTouchEvent(@onNull RecyclerView rv, @NonNull MotionEvent e)11853         boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
11854 
11855         /**
11856          * Process a touch event as part of a gesture that was claimed by returning true from
11857          * a previous call to {@link #onInterceptTouchEvent}.
11858          *
11859          * @param rv The RecyclerView whose scroll state has changed.
11860          * @param e MotionEvent describing the touch event. All coordinates are in
11861          *          the RecyclerView's coordinate system.
11862          */
onTouchEvent(@onNull RecyclerView rv, @NonNull MotionEvent e)11863         void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
11864 
11865         /**
11866          * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
11867          * intercept touch events with
11868          * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
11869          *
11870          * @param disallowIntercept True if the child does not want the parent to
11871          *                          intercept touch events.
11872          * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
11873          */
onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)11874         void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
11875     }
11876 
11877     /**
11878      * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies
11879      * and default return values.
11880      * <p>
11881      * You may prefer to extend this class if you don't need to override all methods. Another
11882      * benefit of using this class is future compatibility. As the interface may change, we'll
11883      * always provide a default implementation on this class so that your code won't break when
11884      * you update to a new version of the support library.
11885      */
11886     public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
11887         /** {@inheritDoc} */
11888         @Override
onInterceptTouchEvent(@onNull RecyclerView rv, @NonNull MotionEvent e)11889         public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
11890             return false;
11891         }
11892 
11893         /** {@inheritDoc} */
11894         @Override
onTouchEvent(@onNull RecyclerView rv, @NonNull MotionEvent e)11895         public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
11896         }
11897 
11898         /** {@inheritDoc} */
11899         @Override
onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)11900         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
11901         }
11902     }
11903 
11904 
11905     /**
11906      * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
11907      * has occurred on that RecyclerView.
11908      * <p>
11909      *
11910      * @see RecyclerView#addOnScrollListener(OnScrollListener)
11911      * @see RecyclerView#clearOnChildAttachStateChangeListeners()
11912      */
11913     public abstract static class OnScrollListener {
11914         /**
11915          * Callback method to be invoked when RecyclerView's scroll state changes.
11916          *
11917          * @param recyclerView The RecyclerView whose scroll state has changed.
11918          * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
11919          *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
11920          */
onScrollStateChanged(@onNull RecyclerView recyclerView, int newState)11921         public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
11922         }
11923 
11924         /**
11925          * Callback method to be invoked when the RecyclerView has been scrolled. This will be
11926          * called after the scroll has completed.
11927          * <p>
11928          * This callback will also be called if visible item range changes after a layout
11929          * calculation. In that case, dx and dy will be 0.
11930          *
11931          * @param recyclerView The RecyclerView which scrolled.
11932          * @param dx           The amount of horizontal scroll.
11933          * @param dy           The amount of vertical scroll.
11934          */
onScrolled(@onNull RecyclerView recyclerView, int dx, int dy)11935         public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
11936         }
11937     }
11938 
11939     /**
11940      * A RecyclerListener can be set on a RecyclerView to receive messages whenever
11941      * a view is recycled.
11942      *
11943      * @see RecyclerView#setRecyclerListener(RecyclerListener)
11944      */
11945     public interface RecyclerListener {
11946 
11947         /**
11948          * This method is called whenever the view in the ViewHolder is recycled.
11949          *
11950          * RecyclerView calls this method right before clearing ViewHolder's internal data and
11951          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
11952          * before being recycled, you can call {@link ViewHolder#getBindingAdapterPosition()} to get
11953          * its adapter position.
11954          *
11955          * @param holder The ViewHolder containing the view that was recycled
11956          */
onViewRecycled(@onNull ViewHolder holder)11957         void onViewRecycled(@NonNull ViewHolder holder);
11958     }
11959 
11960     /**
11961      * A Listener interface that can be attached to a RecylcerView to get notified
11962      * whenever a ViewHolder is attached to or detached from RecyclerView.
11963      */
11964     public interface OnChildAttachStateChangeListener {
11965 
11966         /**
11967          * Called when a view is attached to the RecyclerView.
11968          *
11969          * @param view The View which is attached to the RecyclerView
11970          */
onChildViewAttachedToWindow(@onNull View view)11971         void onChildViewAttachedToWindow(@NonNull View view);
11972 
11973         /**
11974          * Called when a view is detached from RecyclerView.
11975          *
11976          * @param view The View which is being detached from the RecyclerView
11977          */
onChildViewDetachedFromWindow(@onNull View view)11978         void onChildViewDetachedFromWindow(@NonNull View view);
11979     }
11980 
11981     /**
11982      * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
11983      *
11984      * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
11985      * potentially expensive {@link View#findViewById(int)} results.</p>
11986      *
11987      * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
11988      * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
11989      * their own custom ViewHolder implementations to store data that makes binding view contents
11990      * easier. Implementations should assume that individual item views will hold strong references
11991      * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
11992      * strong references to extra off-screen item views for caching purposes</p>
11993      */
11994     public abstract static class ViewHolder {
11995         public final @NonNull View itemView;
11996         WeakReference<RecyclerView> mNestedRecyclerView;
11997         int mPosition = NO_POSITION;
11998         int mOldPosition = NO_POSITION;
11999         long mItemId = NO_ID;
12000         int mItemViewType = INVALID_TYPE;
12001         int mPreLayoutPosition = NO_POSITION;
12002 
12003         // The item that this holder is shadowing during an item change event/animation
12004         ViewHolder mShadowedHolder = null;
12005         // The item that is shadowing this holder during an item change event/animation
12006         ViewHolder mShadowingHolder = null;
12007 
12008         /**
12009          * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
12010          * are all valid.
12011          */
12012         static final int FLAG_BOUND = 1 << 0;
12013 
12014         /**
12015          * The data this ViewHolder's view reflects is stale and needs to be rebound
12016          * by the adapter. mPosition and mItemId are consistent.
12017          */
12018         static final int FLAG_UPDATE = 1 << 1;
12019 
12020         /**
12021          * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
12022          * are not to be trusted and may no longer match the item view type.
12023          * This ViewHolder must be fully rebound to different data.
12024          */
12025         static final int FLAG_INVALID = 1 << 2;
12026 
12027         /**
12028          * This ViewHolder points at data that represents an item previously removed from the
12029          * data set. Its view may still be used for things like outgoing animations.
12030          */
12031         static final int FLAG_REMOVED = 1 << 3;
12032 
12033         /**
12034          * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
12035          * and is intended to keep views around during animations.
12036          */
12037         static final int FLAG_NOT_RECYCLABLE = 1 << 4;
12038 
12039         /**
12040          * This ViewHolder is returned from scrap which means we are expecting an addView call
12041          * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
12042          * the end of the layout pass and then recycled by RecyclerView if it is not added back to
12043          * the RecyclerView.
12044          */
12045         static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
12046 
12047         /**
12048          * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
12049          * it unless LayoutManager is replaced.
12050          * It is still fully visible to the LayoutManager.
12051          */
12052         static final int FLAG_IGNORE = 1 << 7;
12053 
12054         /**
12055          * When the View is detached form the parent, we set this flag so that we can take correct
12056          * action when we need to remove it or add it back.
12057          */
12058         static final int FLAG_TMP_DETACHED = 1 << 8;
12059 
12060         /**
12061          * Set when we can no longer determine the adapter position of this ViewHolder until it is
12062          * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
12063          * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
12064          * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
12065          * re-calculated.
12066          */
12067         static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
12068 
12069         /**
12070          * Set when a addChangePayload(null) is called
12071          */
12072         static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
12073 
12074         /**
12075          * Used by ItemAnimator when a ViewHolder's position changes
12076          */
12077         static final int FLAG_MOVED = 1 << 11;
12078 
12079         /**
12080          * Used by ItemAnimator when a ViewHolder appears in pre-layout
12081          */
12082         static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
12083 
12084         static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
12085 
12086         /**
12087          * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
12088          * hidden list (as if it was scrap) without being recycled in between.
12089          *
12090          * When a ViewHolder is hidden, there are 2 paths it can be re-used:
12091          * a) Animation ends, view is recycled and used from the recycle pool.
12092          * b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
12093          *
12094          * This flag is used to represent "case b" where the ViewHolder is reused without being
12095          * recycled (thus "bounced" from the hidden list). This state requires special handling
12096          * because the ViewHolder must be added to pre layout maps for animations as if it was
12097          * already there.
12098          */
12099         static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
12100 
12101         int mFlags;
12102 
12103         private static final List<Object> FULLUPDATE_PAYLOADS = Collections.emptyList();
12104 
12105         List<Object> mPayloads = null;
12106         List<Object> mUnmodifiedPayloads = null;
12107 
12108         private int mIsRecyclableCount = 0;
12109 
12110         // If non-null, view is currently considered scrap and may be reused for other data by the
12111         // scrap container.
12112         Recycler mScrapContainer = null;
12113         // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
12114         boolean mInChangeScrap = false;
12115 
12116         // Saves isImportantForAccessibility value for the view item while it's in hidden state and
12117         // marked as unimportant for accessibility.
12118         private int mWasImportantForAccessibilityBeforeHidden =
12119                 View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
12120         // set if we defer the accessibility state change of the view holder
12121         @VisibleForTesting
12122         int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
12123 
12124         /**
12125          * Is set when VH is bound from the adapter and cleaned right before it is sent to
12126          * {@link RecycledViewPool}.
12127          */
12128         RecyclerView mOwnerRecyclerView;
12129 
12130         // The last adapter that bound this ViewHolder. It is cleaned before VH is recycled.
12131         Adapter<? extends ViewHolder> mBindingAdapter;
12132 
ViewHolder(@onNull View itemView)12133         public ViewHolder(@NonNull View itemView) {
12134             if (itemView == null) {
12135                 throw new IllegalArgumentException("itemView may not be null");
12136             }
12137             this.itemView = itemView;
12138         }
12139 
flagRemovedAndOffsetPosition(int newPosition, int offset, boolean applyToPreLayout)12140         void flagRemovedAndOffsetPosition(int newPosition, int offset, boolean applyToPreLayout) {
12141             addFlags(ViewHolder.FLAG_REMOVED);
12142             offsetPosition(offset, applyToPreLayout);
12143             mPosition = newPosition;
12144         }
12145 
offsetPosition(int offset, boolean applyToPreLayout)12146         void offsetPosition(int offset, boolean applyToPreLayout) {
12147             if (mOldPosition == NO_POSITION) {
12148                 mOldPosition = mPosition;
12149             }
12150             if (mPreLayoutPosition == NO_POSITION) {
12151                 mPreLayoutPosition = mPosition;
12152             }
12153             if (applyToPreLayout) {
12154                 mPreLayoutPosition += offset;
12155             }
12156             mPosition += offset;
12157             if (itemView.getLayoutParams() != null) {
12158                 ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
12159             }
12160         }
12161 
clearOldPosition()12162         void clearOldPosition() {
12163             mOldPosition = NO_POSITION;
12164             mPreLayoutPosition = NO_POSITION;
12165         }
12166 
saveOldPosition()12167         void saveOldPosition() {
12168             if (mOldPosition == NO_POSITION) {
12169                 mOldPosition = mPosition;
12170             }
12171         }
12172 
shouldIgnore()12173         boolean shouldIgnore() {
12174             return (mFlags & FLAG_IGNORE) != 0;
12175         }
12176 
12177         /**
12178          * @see #getLayoutPosition()
12179          * @see #getBindingAdapterPosition()
12180          * @see #getAbsoluteAdapterPosition()
12181          * @deprecated This method is deprecated because its meaning is ambiguous due to the async
12182          * handling of adapter updates. You should use {@link #getLayoutPosition()},
12183          * {@link #getBindingAdapterPosition()} or {@link #getAbsoluteAdapterPosition()}
12184          * depending on your use case.
12185          */
12186         @Deprecated
getPosition()12187         public final int getPosition() {
12188             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
12189         }
12190 
12191         /**
12192          * Returns the position of the ViewHolder in terms of the latest layout pass.
12193          * <p>
12194          * This position is mostly used by RecyclerView components to be consistent while
12195          * RecyclerView lazily processes adapter updates.
12196          * <p>
12197          * For performance and animation reasons, RecyclerView batches all adapter updates until the
12198          * next layout pass. This may cause mismatches between the Adapter position of the item and
12199          * the position it had in the latest layout calculations.
12200          * <p>
12201          * LayoutManagers should always call this method while doing calculations based on item
12202          * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
12203          * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
12204          * of the item.
12205          * <p>
12206          * If LayoutManager needs to call an external method that requires the adapter position of
12207          * the item, it can use {@link #getAbsoluteAdapterPosition()} or
12208          * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
12209          *
12210          * @return Returns the adapter position of the ViewHolder in the latest layout pass.
12211          * @see #getBindingAdapterPosition()
12212          * @see #getAbsoluteAdapterPosition()
12213          */
getLayoutPosition()12214         public final int getLayoutPosition() {
12215             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
12216         }
12217 
12218 
12219         /**
12220          * @return {@link #getBindingAdapterPosition()}
12221          * @deprecated This method is confusing when adapters nest other adapters.
12222          * If you are calling this in the context of an Adapter, you probably want to call
12223          * {@link #getBindingAdapterPosition()} or if you want the position as {@link RecyclerView}
12224          * sees it, you should call {@link #getAbsoluteAdapterPosition()}.
12225          */
12226         @Deprecated
getAdapterPosition()12227         public final int getAdapterPosition() {
12228             return getBindingAdapterPosition();
12229         }
12230 
12231         /**
12232          * Returns the Adapter position of the item represented by this ViewHolder with respect to
12233          * the {@link Adapter} that bound it.
12234          * <p>
12235          * Note that this might be different than the {@link #getLayoutPosition()} if there are
12236          * pending adapter updates but a new layout pass has not happened yet.
12237          * <p>
12238          * RecyclerView does not handle any adapter updates until the next layout traversal. This
12239          * may create temporary inconsistencies between what user sees on the screen and what
12240          * adapter contents have. This inconsistency is not important since it will be less than
12241          * 16ms but it might be a problem if you want to use ViewHolder position to access the
12242          * adapter. Sometimes, you may need to get the exact adapter position to do
12243          * some actions in response to user events. In that case, you should use this method which
12244          * will calculate the Adapter position of the ViewHolder.
12245          * <p>
12246          * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
12247          * next layout pass, the return value of this method will be {@link #NO_POSITION}.
12248          * <p>
12249          * If the {@link Adapter} that bound this {@link ViewHolder} is inside another
12250          * {@link Adapter} (e.g. {@link ConcatAdapter}), this position might be different than
12251          * {@link #getAbsoluteAdapterPosition()}. If you would like to know the position that
12252          * {@link RecyclerView} considers (e.g. for saved state), you should use
12253          * {@link #getAbsoluteAdapterPosition()}.
12254          *
12255          * @return The adapter position of the item if it still exists in the adapter.
12256          * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
12257          * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
12258          * layout pass or the ViewHolder has already been recycled.
12259          * @see #getAbsoluteAdapterPosition()
12260          * @see #getLayoutPosition()
12261          */
getBindingAdapterPosition()12262         public final int getBindingAdapterPosition() {
12263             if (mBindingAdapter == null) {
12264                 return NO_POSITION;
12265             }
12266             if (mOwnerRecyclerView == null) {
12267                 return NO_POSITION;
12268             }
12269             @SuppressWarnings("unchecked")
12270             Adapter<? extends ViewHolder> rvAdapter = mOwnerRecyclerView.getAdapter();
12271             if (rvAdapter == null) {
12272                 return NO_POSITION;
12273             }
12274             int globalPosition = mOwnerRecyclerView.getAdapterPositionInRecyclerView(this);
12275             if (globalPosition == NO_POSITION) {
12276                 return NO_POSITION;
12277             }
12278             return rvAdapter.findRelativeAdapterPositionIn(mBindingAdapter, this, globalPosition);
12279         }
12280 
12281         /**
12282          * Returns the Adapter position of the item represented by this ViewHolder with respect to
12283          * the {@link RecyclerView}'s {@link Adapter}. If the {@link Adapter} that bound this
12284          * {@link ViewHolder} is inside another adapter (e.g. {@link ConcatAdapter}), this
12285          * position might be different and will include
12286          * the offsets caused by other adapters in the {@link ConcatAdapter}.
12287          * <p>
12288          * Note that this might be different than the {@link #getLayoutPosition()} if there are
12289          * pending adapter updates but a new layout pass has not happened yet.
12290          * <p>
12291          * RecyclerView does not handle any adapter updates until the next layout traversal. This
12292          * may create temporary inconsistencies between what user sees on the screen and what
12293          * adapter contents have. This inconsistency is not important since it will be less than
12294          * 16ms but it might be a problem if you want to use ViewHolder position to access the
12295          * adapter. Sometimes, you may need to get the exact adapter position to do
12296          * some actions in response to user events. In that case, you should use this method which
12297          * will calculate the Adapter position of the ViewHolder.
12298          * <p>
12299          * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
12300          * next layout pass, the return value of this method will be {@link #NO_POSITION}.
12301          * <p>
12302          * Note that if you are querying the position as {@link RecyclerView} sees, you should use
12303          * {@link #getAbsoluteAdapterPosition()} (e.g. you want to use it to save scroll
12304          * state). If you are querying the position to access the {@link Adapter} contents,
12305          * you should use {@link #getBindingAdapterPosition()}.
12306          *
12307          * @return The adapter position of the item from {@link RecyclerView}'s perspective if it
12308          * still exists in the adapter and bound to a valid item.
12309          * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
12310          * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
12311          * layout pass or the ViewHolder has already been recycled.
12312          * @see #getBindingAdapterPosition()
12313          * @see #getLayoutPosition()
12314          */
getAbsoluteAdapterPosition()12315         public final int getAbsoluteAdapterPosition() {
12316             if (mOwnerRecyclerView == null) {
12317                 return NO_POSITION;
12318             }
12319             return mOwnerRecyclerView.getAdapterPositionInRecyclerView(this);
12320         }
12321 
12322         /**
12323          * Returns the {@link Adapter} that last bound this {@link ViewHolder}.
12324          * Might return {@code null} if this {@link ViewHolder} is not bound to any adapter.
12325          *
12326          * @return The {@link Adapter} that last bound this {@link ViewHolder} or {@code null} if
12327          * this {@link ViewHolder} is not bound by any adapter (e.g. recycled).
12328          */
getBindingAdapter()12329         public final @Nullable Adapter<? extends ViewHolder> getBindingAdapter() {
12330             return mBindingAdapter;
12331         }
12332 
12333         /**
12334          * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
12335          * to perform animations.
12336          * <p>
12337          * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
12338          * adapter index in the previous layout.
12339          *
12340          * @return The previous adapter index of the Item represented by this ViewHolder or
12341          * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
12342          * complete).
12343          */
getOldPosition()12344         public final int getOldPosition() {
12345             return mOldPosition;
12346         }
12347 
12348         /**
12349          * Returns The itemId represented by this ViewHolder.
12350          *
12351          * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
12352          * otherwise
12353          */
getItemId()12354         public final long getItemId() {
12355             return mItemId;
12356         }
12357 
12358         /**
12359          * @return The view type of this ViewHolder.
12360          */
getItemViewType()12361         public final int getItemViewType() {
12362             return mItemViewType;
12363         }
12364 
isScrap()12365         boolean isScrap() {
12366             return mScrapContainer != null;
12367         }
12368 
unScrap()12369         void unScrap() {
12370             mScrapContainer.unscrapView(this);
12371         }
12372 
wasReturnedFromScrap()12373         boolean wasReturnedFromScrap() {
12374             return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
12375         }
12376 
clearReturnedFromScrapFlag()12377         void clearReturnedFromScrapFlag() {
12378             mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
12379         }
12380 
clearTmpDetachFlag()12381         void clearTmpDetachFlag() {
12382             mFlags = mFlags & ~FLAG_TMP_DETACHED;
12383         }
12384 
stopIgnoring()12385         void stopIgnoring() {
12386             mFlags = mFlags & ~FLAG_IGNORE;
12387         }
12388 
setScrapContainer(Recycler recycler, boolean isChangeScrap)12389         void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
12390             mScrapContainer = recycler;
12391             mInChangeScrap = isChangeScrap;
12392         }
12393 
isInvalid()12394         boolean isInvalid() {
12395             return (mFlags & FLAG_INVALID) != 0;
12396         }
12397 
needsUpdate()12398         boolean needsUpdate() {
12399             return (mFlags & FLAG_UPDATE) != 0;
12400         }
12401 
isBound()12402         boolean isBound() {
12403             return (mFlags & FLAG_BOUND) != 0;
12404         }
12405 
isRemoved()12406         boolean isRemoved() {
12407             return (mFlags & FLAG_REMOVED) != 0;
12408         }
12409 
hasAnyOfTheFlags(int flags)12410         boolean hasAnyOfTheFlags(int flags) {
12411             return (mFlags & flags) != 0;
12412         }
12413 
isTmpDetached()12414         boolean isTmpDetached() {
12415             return (mFlags & FLAG_TMP_DETACHED) != 0;
12416         }
12417 
isAttachedToTransitionOverlay()12418         boolean isAttachedToTransitionOverlay() {
12419             return itemView.getParent() != null && itemView.getParent() != mOwnerRecyclerView;
12420         }
12421 
isAdapterPositionUnknown()12422         boolean isAdapterPositionUnknown() {
12423             return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
12424         }
12425 
setFlags(int flags, int mask)12426         void setFlags(int flags, int mask) {
12427             mFlags = (mFlags & ~mask) | (flags & mask);
12428         }
12429 
addFlags(int flags)12430         void addFlags(int flags) {
12431             mFlags |= flags;
12432         }
12433 
addChangePayload(Object payload)12434         void addChangePayload(Object payload) {
12435             if (payload == null) {
12436                 addFlags(FLAG_ADAPTER_FULLUPDATE);
12437             } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
12438                 createPayloadsIfNeeded();
12439                 mPayloads.add(payload);
12440             }
12441         }
12442 
createPayloadsIfNeeded()12443         private void createPayloadsIfNeeded() {
12444             if (mPayloads == null) {
12445                 mPayloads = new ArrayList<Object>();
12446                 mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
12447             }
12448         }
12449 
clearPayload()12450         void clearPayload() {
12451             if (mPayloads != null) {
12452                 mPayloads.clear();
12453             }
12454             mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
12455         }
12456 
getUnmodifiedPayloads()12457         List<Object> getUnmodifiedPayloads() {
12458             if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
12459                 if (mPayloads == null || mPayloads.size() == 0) {
12460                     // Initial state,  no update being called.
12461                     return FULLUPDATE_PAYLOADS;
12462                 }
12463                 // there are none-null payloads
12464                 return mUnmodifiedPayloads;
12465             } else {
12466                 // a full update has been called.
12467                 return FULLUPDATE_PAYLOADS;
12468             }
12469         }
12470 
resetInternal()12471         void resetInternal() {
12472             if (sDebugAssertionsEnabled && isTmpDetached()) {
12473                 throw new IllegalStateException("Attempting to reset temp-detached ViewHolder: "
12474                         + this + ". ViewHolders should be fully detached before resetting.");
12475             }
12476 
12477             mFlags = 0;
12478             mPosition = NO_POSITION;
12479             mOldPosition = NO_POSITION;
12480             mItemId = NO_ID;
12481             mPreLayoutPosition = NO_POSITION;
12482             mIsRecyclableCount = 0;
12483             mShadowedHolder = null;
12484             mShadowingHolder = null;
12485             clearPayload();
12486             mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
12487             mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
12488             clearNestedRecyclerViewIfNotNested(this);
12489         }
12490 
12491         /**
12492          * Called when the child view enters the hidden state
12493          */
onEnteredHiddenState(RecyclerView parent)12494         void onEnteredHiddenState(RecyclerView parent) {
12495             // While the view item is in hidden state, make it invisible for the accessibility.
12496             if (mPendingAccessibilityState != PENDING_ACCESSIBILITY_STATE_NOT_SET) {
12497                 mWasImportantForAccessibilityBeforeHidden = mPendingAccessibilityState;
12498             } else {
12499                 mWasImportantForAccessibilityBeforeHidden =
12500                         itemView.getImportantForAccessibility();
12501             }
12502             parent.setChildImportantForAccessibilityInternal(this,
12503                     View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
12504         }
12505 
12506         /**
12507          * Called when the child view leaves the hidden state
12508          */
onLeftHiddenState(RecyclerView parent)12509         void onLeftHiddenState(RecyclerView parent) {
12510             parent.setChildImportantForAccessibilityInternal(this,
12511                     mWasImportantForAccessibilityBeforeHidden);
12512             mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
12513         }
12514 
12515         @Override
toString()12516         public String toString() {
12517             String className =
12518                     getClass().isAnonymousClass() ? "ViewHolder" : getClass().getSimpleName();
12519             final StringBuilder sb = new StringBuilder(className + "{"
12520                     + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
12521                     + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
12522             if (isScrap()) {
12523                 sb.append(" scrap ")
12524                         .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
12525             }
12526             if (isInvalid()) sb.append(" invalid");
12527             if (!isBound()) sb.append(" unbound");
12528             if (needsUpdate()) sb.append(" update");
12529             if (isRemoved()) sb.append(" removed");
12530             if (shouldIgnore()) sb.append(" ignored");
12531             if (isTmpDetached()) sb.append(" tmpDetached");
12532             if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
12533             if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
12534 
12535             if (itemView.getParent() == null) sb.append(" no parent");
12536             sb.append("}");
12537             return sb.toString();
12538         }
12539 
12540         /**
12541          * Informs the recycler whether this item can be recycled. Views which are not
12542          * recyclable will not be reused for other items until setIsRecyclable() is
12543          * later set to true. Calls to setIsRecyclable() should always be paired (one
12544          * call to setIsRecyclabe(false) should always be matched with a later call to
12545          * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
12546          * reference-counted.
12547          *
12548          * @param recyclable Whether this item is available to be recycled. Default value
12549          *                   is true.
12550          * @see #isRecyclable()
12551          */
setIsRecyclable(boolean recyclable)12552         public final void setIsRecyclable(boolean recyclable) {
12553             mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
12554             if (mIsRecyclableCount < 0) {
12555                 mIsRecyclableCount = 0;
12556                 if (sDebugAssertionsEnabled) {
12557                     throw new RuntimeException("isRecyclable decremented below 0: "
12558                             + "unmatched pair of setIsRecyable() calls for " + this);
12559                 }
12560                 Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: "
12561                         + "unmatched pair of setIsRecyable() calls for " + this);
12562             } else if (!recyclable && mIsRecyclableCount == 1) {
12563                 mFlags |= FLAG_NOT_RECYCLABLE;
12564             } else if (recyclable && mIsRecyclableCount == 0) {
12565                 mFlags &= ~FLAG_NOT_RECYCLABLE;
12566             }
12567             if (sVerboseLoggingEnabled) {
12568                 Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
12569             }
12570         }
12571 
12572         /**
12573          * @return true if this item is available to be recycled, false otherwise.
12574          * @see #setIsRecyclable(boolean)
12575          */
isRecyclable()12576         public final boolean isRecyclable() {
12577             return (mFlags & FLAG_NOT_RECYCLABLE) == 0
12578                     && !ViewCompat.hasTransientState(itemView);
12579         }
12580 
12581         /**
12582          * Returns whether we have animations referring to this view holder or not.
12583          * This is similar to isRecyclable flag but does not check transient state.
12584          */
shouldBeKeptAsChild()12585         boolean shouldBeKeptAsChild() {
12586             return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
12587         }
12588 
12589         /**
12590          * @return True if ViewHolder is not referenced by RecyclerView animations but has
12591          * transient state which will prevent it from being recycled.
12592          */
doesTransientStatePreventRecycling()12593         boolean doesTransientStatePreventRecycling() {
12594             return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView);
12595         }
12596 
isUpdated()12597         boolean isUpdated() {
12598             return (mFlags & FLAG_UPDATE) != 0;
12599         }
12600     }
12601 
12602     /**
12603      * This method is here so that we can control the important for a11y changes and test it.
12604      */
12605     @VisibleForTesting
setChildImportantForAccessibilityInternal(ViewHolder viewHolder, int importantForAccessibility)12606     boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
12607             int importantForAccessibility) {
12608         if (isComputingLayout()) {
12609             viewHolder.mPendingAccessibilityState = importantForAccessibility;
12610             mPendingAccessibilityImportanceChange.add(viewHolder);
12611             return false;
12612         }
12613         viewHolder.itemView.setImportantForAccessibility(importantForAccessibility);
12614         return true;
12615     }
12616 
dispatchPendingImportantForAccessibilityChanges()12617     void dispatchPendingImportantForAccessibilityChanges() {
12618         for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
12619             ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
12620             if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
12621                 continue;
12622             }
12623             int state = viewHolder.mPendingAccessibilityState;
12624             if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
12625                 //noinspection WrongConstant
12626                 viewHolder.itemView.setImportantForAccessibility(state);
12627                 viewHolder.mPendingAccessibilityState =
12628                         ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
12629             }
12630         }
12631         mPendingAccessibilityImportanceChange.clear();
12632     }
12633 
getAdapterPositionInRecyclerView(ViewHolder viewHolder)12634     int getAdapterPositionInRecyclerView(ViewHolder viewHolder) {
12635         if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
12636                 | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
12637                 || !viewHolder.isBound()) {
12638             return RecyclerView.NO_POSITION;
12639         }
12640         return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
12641     }
12642 
12643     @VisibleForTesting
initFastScroller(StateListDrawable verticalThumbDrawable, Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable, Drawable horizontalTrackDrawable)12644     void initFastScroller(StateListDrawable verticalThumbDrawable,
12645             Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable,
12646             Drawable horizontalTrackDrawable) {
12647         if (verticalThumbDrawable == null || verticalTrackDrawable == null
12648                 || horizontalThumbDrawable == null || horizontalTrackDrawable == null) {
12649             throw new IllegalArgumentException(
12650                     "Trying to set fast scroller without both required drawables."
12651                             + exceptionLabel());
12652         }
12653 
12654         Resources resources = getContext().getResources();
12655         new FastScroller(this, verticalThumbDrawable, verticalTrackDrawable,
12656                 horizontalThumbDrawable, horizontalTrackDrawable,
12657                 resources.getDimensionPixelSize(R.dimen.fastscroll_default_thickness),
12658                 resources.getDimensionPixelSize(R.dimen.fastscroll_minimum_range),
12659                 resources.getDimensionPixelOffset(R.dimen.fastscroll_margin));
12660     }
12661 
12662     // NestedScrollingChild
12663 
12664     @Override
setNestedScrollingEnabled(boolean enabled)12665     public void setNestedScrollingEnabled(boolean enabled) {
12666         getScrollingChildHelper().setNestedScrollingEnabled(enabled);
12667     }
12668 
12669     @Override
isNestedScrollingEnabled()12670     public boolean isNestedScrollingEnabled() {
12671         return getScrollingChildHelper().isNestedScrollingEnabled();
12672     }
12673 
12674     @Override
startNestedScroll(int axes)12675     public boolean startNestedScroll(int axes) {
12676         return getScrollingChildHelper().startNestedScroll(axes);
12677     }
12678 
12679     @Override
startNestedScroll(int axes, int type)12680     public boolean startNestedScroll(int axes, int type) {
12681         return getScrollingChildHelper().startNestedScroll(axes, type);
12682     }
12683 
12684     @Override
stopNestedScroll()12685     public void stopNestedScroll() {
12686         getScrollingChildHelper().stopNestedScroll();
12687     }
12688 
12689     @Override
stopNestedScroll(int type)12690     public void stopNestedScroll(int type) {
12691         getScrollingChildHelper().stopNestedScroll(type);
12692     }
12693 
12694     @Override
hasNestedScrollingParent()12695     public boolean hasNestedScrollingParent() {
12696         return getScrollingChildHelper().hasNestedScrollingParent();
12697     }
12698 
12699     @Override
hasNestedScrollingParent(int type)12700     public boolean hasNestedScrollingParent(int type) {
12701         return getScrollingChildHelper().hasNestedScrollingParent(type);
12702     }
12703 
12704     @Override
dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow)12705     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
12706             int dyUnconsumed, int[] offsetInWindow) {
12707         return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
12708                 dxUnconsumed, dyUnconsumed, offsetInWindow);
12709     }
12710 
12711     @Override
dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow, int type)12712     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
12713             int dyUnconsumed, int[] offsetInWindow, int type) {
12714         return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
12715                 dxUnconsumed, dyUnconsumed, offsetInWindow, type);
12716     }
12717 
12718     @Override
dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow, int type, int @NonNull [] consumed)12719     public final void dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
12720             int dyUnconsumed, int[] offsetInWindow, int type, int @NonNull [] consumed) {
12721         getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
12722                 dxUnconsumed, dyUnconsumed, offsetInWindow, type, consumed);
12723     }
12724 
12725     @Override
dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow)12726     public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
12727         return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
12728     }
12729 
12730     @Override
dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow, int type)12731     public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
12732             int type) {
12733         return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow,
12734                 type);
12735     }
12736 
12737     @Override
dispatchNestedFling(float velocityX, float velocityY, boolean consumed)12738     public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
12739         return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
12740     }
12741 
12742     @Override
dispatchNestedPreFling(float velocityX, float velocityY)12743     public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
12744         return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
12745     }
12746 
12747     /**
12748      * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
12749      * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
12750      * to create their own subclass of this <code>LayoutParams</code> class
12751      * to store any additional required per-child view metadata about the layout.
12752      */
12753     public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
12754         ViewHolder mViewHolder;
12755         final Rect mDecorInsets = new Rect();
12756         boolean mInsetsDirty = true;
12757         // Flag is set to true if the view is bound while it is detached from RV.
12758         // In this case, we need to manually call invalidate after view is added to guarantee that
12759         // invalidation is populated through the View hierarchy
12760         boolean mPendingInvalidate = false;
12761 
LayoutParams(Context c, AttributeSet attrs)12762         public LayoutParams(Context c, AttributeSet attrs) {
12763             super(c, attrs);
12764         }
12765 
LayoutParams(int width, int height)12766         public LayoutParams(int width, int height) {
12767             super(width, height);
12768         }
12769 
LayoutParams(MarginLayoutParams source)12770         public LayoutParams(MarginLayoutParams source) {
12771             super(source);
12772         }
12773 
LayoutParams(ViewGroup.LayoutParams source)12774         public LayoutParams(ViewGroup.LayoutParams source) {
12775             super(source);
12776         }
12777 
LayoutParams(LayoutParams source)12778         public LayoutParams(LayoutParams source) {
12779             super((ViewGroup.LayoutParams) source);
12780         }
12781 
12782         /**
12783          * Returns true if the view this LayoutParams is attached to needs to have its content
12784          * updated from the corresponding adapter.
12785          *
12786          * @return true if the view should have its content updated
12787          */
viewNeedsUpdate()12788         public boolean viewNeedsUpdate() {
12789             return mViewHolder.needsUpdate();
12790         }
12791 
12792         /**
12793          * Returns true if the view this LayoutParams is attached to is now representing
12794          * potentially invalid data. A LayoutManager should scrap/recycle it.
12795          *
12796          * @return true if the view is invalid
12797          */
isViewInvalid()12798         public boolean isViewInvalid() {
12799             return mViewHolder.isInvalid();
12800         }
12801 
12802         /**
12803          * Returns true if the adapter data item corresponding to the view this LayoutParams
12804          * is attached to has been removed from the data set. A LayoutManager may choose to
12805          * treat it differently in order to animate its outgoing or disappearing state.
12806          *
12807          * @return true if the item the view corresponds to was removed from the data set
12808          */
isItemRemoved()12809         public boolean isItemRemoved() {
12810             return mViewHolder.isRemoved();
12811         }
12812 
12813         /**
12814          * Returns true if the adapter data item corresponding to the view this LayoutParams
12815          * is attached to has been changed in the data set. A LayoutManager may choose to
12816          * treat it differently in order to animate its changing state.
12817          *
12818          * @return true if the item the view corresponds to was changed in the data set
12819          */
isItemChanged()12820         public boolean isItemChanged() {
12821             return mViewHolder.isUpdated();
12822         }
12823 
12824         /**
12825          * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
12826          */
12827         @Deprecated
getViewPosition()12828         public int getViewPosition() {
12829             return mViewHolder.getPosition();
12830         }
12831 
12832         /**
12833          * Returns the adapter position that the view this LayoutParams is attached to corresponds
12834          * to as of latest layout calculation.
12835          *
12836          * @return the adapter position this view as of latest layout pass
12837          */
getViewLayoutPosition()12838         public int getViewLayoutPosition() {
12839             return mViewHolder.getLayoutPosition();
12840         }
12841 
12842         /**
12843          * @deprecated This method is confusing when nested adapters are used.
12844          * If you are calling from the context of an {@link Adapter},
12845          * use {@link #getBindingAdapterPosition()}. If you need the position that
12846          * {@link RecyclerView} sees, use {@link #getAbsoluteAdapterPosition()}.
12847          */
12848         @Deprecated
getViewAdapterPosition()12849         public int getViewAdapterPosition() {
12850             return mViewHolder.getBindingAdapterPosition();
12851         }
12852 
12853         /**
12854          * Returns the up-to-date adapter position that the view this LayoutParams is attached to
12855          * corresponds to in the {@link RecyclerView}. If the {@link RecyclerView} has an
12856          * {@link Adapter} that merges other adapters, this position will be with respect to the
12857          * adapter that is assigned to the {@link RecyclerView}.
12858          *
12859          * @return the up-to-date adapter position this view with respect to the RecyclerView. It
12860          * may return {@link RecyclerView#NO_POSITION} if item represented by this View has been
12861          * removed or
12862          * its up-to-date position cannot be calculated.
12863          */
getAbsoluteAdapterPosition()12864         public int getAbsoluteAdapterPosition() {
12865             return mViewHolder.getAbsoluteAdapterPosition();
12866         }
12867 
12868         /**
12869          * Returns the up-to-date adapter position that the view this LayoutParams is attached to
12870          * corresponds to with respect to the {@link Adapter} that bound this View.
12871          *
12872          * @return the up-to-date adapter position this view relative to the {@link Adapter} that
12873          * bound this View. It may return {@link RecyclerView#NO_POSITION} if item represented by
12874          * this View has been removed or its up-to-date position cannot be calculated.
12875          */
getBindingAdapterPosition()12876         public int getBindingAdapterPosition() {
12877             return mViewHolder.getBindingAdapterPosition();
12878         }
12879     }
12880 
12881     /**
12882      * Observer base class for watching changes to an {@link Adapter}.
12883      * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
12884      */
12885     public abstract static class AdapterDataObserver {
onChanged()12886         public void onChanged() {
12887             // Do nothing
12888         }
12889 
onItemRangeChanged(int positionStart, int itemCount)12890         public void onItemRangeChanged(int positionStart, int itemCount) {
12891             // do nothing
12892         }
12893 
onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload)12894         public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
12895             // fallback to onItemRangeChanged(positionStart, itemCount) if app
12896             // does not override this method.
12897             onItemRangeChanged(positionStart, itemCount);
12898         }
12899 
onItemRangeInserted(int positionStart, int itemCount)12900         public void onItemRangeInserted(int positionStart, int itemCount) {
12901             // do nothing
12902         }
12903 
onItemRangeRemoved(int positionStart, int itemCount)12904         public void onItemRangeRemoved(int positionStart, int itemCount) {
12905             // do nothing
12906         }
12907 
onItemRangeMoved(int fromPosition, int toPosition, int itemCount)12908         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
12909             // do nothing
12910         }
12911 
12912         /**
12913          * Called when the {@link Adapter.StateRestorationPolicy} of the {@link Adapter} changed.
12914          * When this method is called, the Adapter might be ready to restore its state if it has
12915          * not already been restored.
12916          *
12917          * @see Adapter#getStateRestorationPolicy()
12918          * @see Adapter#setStateRestorationPolicy(Adapter.StateRestorationPolicy)
12919          */
onStateRestorationPolicyChanged()12920         public void onStateRestorationPolicyChanged() {
12921             // do nothing
12922         }
12923     }
12924 
12925     /**
12926      * Base class for smooth scrolling. Handles basic tracking of the target view position and
12927      * provides methods to trigger a programmatic scroll.
12928      *
12929      * <p>An instance of SmoothScroller is only intended to be used once.  You should create a new
12930      * instance for each call to {@link LayoutManager#startSmoothScroll(SmoothScroller)}.
12931      *
12932      * @see LinearSmoothScroller
12933      */
12934     public abstract static class SmoothScroller {
12935 
12936         private int mTargetPosition = RecyclerView.NO_POSITION;
12937 
12938         private RecyclerView mRecyclerView;
12939 
12940         private LayoutManager mLayoutManager;
12941 
12942         private boolean mPendingInitialRun;
12943 
12944         private boolean mRunning;
12945 
12946         private View mTargetView;
12947 
12948         private final Action mRecyclingAction;
12949 
12950         private boolean mStarted;
12951 
SmoothScroller()12952         public SmoothScroller() {
12953             mRecyclingAction = new Action(0, 0);
12954         }
12955 
12956         /**
12957          * Starts a smooth scroll for the given target position.
12958          * <p>In each animation step, {@link RecyclerView} will check
12959          * for the target view and call either
12960          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
12961          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
12962          * SmoothScroller is stopped.</p>
12963          *
12964          * <p>Note that if RecyclerView finds the target view, it will automatically stop the
12965          * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
12966          * stop calling SmoothScroller in each animation step.</p>
12967          */
start(RecyclerView recyclerView, LayoutManager layoutManager)12968         void start(RecyclerView recyclerView, LayoutManager layoutManager) {
12969 
12970             // Stop any previous ViewFlinger animations now because we are about to start a new one.
12971             recyclerView.mViewFlinger.stop();
12972 
12973             if (mStarted) {
12974                 Log.w(TAG, "An instance of " + this.getClass().getSimpleName() + " was started "
12975                         + "more than once. Each instance of" + this.getClass().getSimpleName() + " "
12976                         + "is intended to only be used once. You should create a new instance for "
12977                         + "each use.");
12978             }
12979 
12980             mRecyclerView = recyclerView;
12981             mLayoutManager = layoutManager;
12982             if (mTargetPosition == RecyclerView.NO_POSITION) {
12983                 throw new IllegalArgumentException("Invalid target position");
12984             }
12985             mRecyclerView.mState.mTargetPosition = mTargetPosition;
12986             mRunning = true;
12987             mPendingInitialRun = true;
12988             mTargetView = findViewByPosition(getTargetPosition());
12989             onStart();
12990             mRecyclerView.mViewFlinger.postOnAnimation();
12991 
12992             mStarted = true;
12993         }
12994 
setTargetPosition(int targetPosition)12995         public void setTargetPosition(int targetPosition) {
12996             mTargetPosition = targetPosition;
12997         }
12998 
12999         /**
13000          * Compute the scroll vector for a given target position.
13001          * <p>
13002          * This method can return null if the layout manager cannot calculate a scroll vector
13003          * for the given position (e.g. it has no current scroll position).
13004          *
13005          * @param targetPosition the position to which the scroller is scrolling
13006          * @return the scroll vector for a given target position
13007          */
computeScrollVectorForPosition(int targetPosition)13008         public @Nullable PointF computeScrollVectorForPosition(int targetPosition) {
13009             LayoutManager layoutManager = getLayoutManager();
13010             if (layoutManager instanceof ScrollVectorProvider) {
13011                 return ((ScrollVectorProvider) layoutManager)
13012                         .computeScrollVectorForPosition(targetPosition);
13013             }
13014             Log.w(TAG, "You should override computeScrollVectorForPosition when the LayoutManager"
13015                     + " does not implement " + ScrollVectorProvider.class.getCanonicalName());
13016             return null;
13017         }
13018 
13019         /**
13020          * @return The LayoutManager to which this SmoothScroller is attached. Will return
13021          * <code>null</code> after the SmoothScroller is stopped.
13022          */
getLayoutManager()13023         public @Nullable LayoutManager getLayoutManager() {
13024             return mLayoutManager;
13025         }
13026 
13027         /**
13028          * Stops running the SmoothScroller in each animation callback. Note that this does not
13029          * cancel any existing {@link Action} updated by
13030          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
13031          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
13032          */
stop()13033         protected final void stop() {
13034             if (!mRunning) {
13035                 return;
13036             }
13037             mRunning = false;
13038             onStop();
13039             mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
13040             mTargetView = null;
13041             mTargetPosition = RecyclerView.NO_POSITION;
13042             mPendingInitialRun = false;
13043             // trigger a cleanup
13044             mLayoutManager.onSmoothScrollerStopped(this);
13045             // clear references to avoid any potential leak by a custom smooth scroller
13046             mLayoutManager = null;
13047             mRecyclerView = null;
13048         }
13049 
13050         /**
13051          * Returns true if SmoothScroller has been started but has not received the first
13052          * animation
13053          * callback yet.
13054          *
13055          * @return True if this SmoothScroller is waiting to start
13056          */
isPendingInitialRun()13057         public boolean isPendingInitialRun() {
13058             return mPendingInitialRun;
13059         }
13060 
13061 
13062         /**
13063          * @return True if SmoothScroller is currently active
13064          */
isRunning()13065         public boolean isRunning() {
13066             return mRunning;
13067         }
13068 
13069         /**
13070          * Returns the adapter position of the target item
13071          *
13072          * @return Adapter position of the target item or
13073          * {@link RecyclerView#NO_POSITION} if no target view is set.
13074          */
getTargetPosition()13075         public int getTargetPosition() {
13076             return mTargetPosition;
13077         }
13078 
onAnimation(int dx, int dy)13079         void onAnimation(int dx, int dy) {
13080             final RecyclerView recyclerView = mRecyclerView;
13081             if (mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
13082                 stop();
13083             }
13084 
13085             // The following if block exists to have the LayoutManager scroll 1 pixel in the correct
13086             // direction in order to cause the LayoutManager to draw two pages worth of views so
13087             // that the target view may be found before scrolling any further.  This is done to
13088             // prevent an initial scroll distance from scrolling past the view, which causes a
13089             // jittery looking animation.
13090             if (mPendingInitialRun && mTargetView == null && mLayoutManager != null) {
13091                 PointF pointF = computeScrollVectorForPosition(mTargetPosition);
13092                 if (pointF != null && (pointF.x != 0 || pointF.y != 0)) {
13093                     recyclerView.scrollStep(
13094                             (int) Math.signum(pointF.x),
13095                             (int) Math.signum(pointF.y),
13096                             null);
13097                 }
13098             }
13099 
13100             mPendingInitialRun = false;
13101 
13102             if (mTargetView != null) {
13103                 // verify target position
13104                 if (getChildPosition(mTargetView) == mTargetPosition) {
13105                     onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
13106                     mRecyclingAction.runIfNecessary(recyclerView);
13107                     stop();
13108                 } else {
13109                     Log.e(TAG, "Passed over target position while smooth scrolling.");
13110                     mTargetView = null;
13111                 }
13112             }
13113             if (mRunning) {
13114                 onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
13115                 boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
13116                 mRecyclingAction.runIfNecessary(recyclerView);
13117                 if (hadJumpTarget) {
13118                     // It is not stopped so needs to be restarted
13119                     if (mRunning) {
13120                         mPendingInitialRun = true;
13121                         recyclerView.mViewFlinger.postOnAnimation();
13122                     }
13123                 }
13124             }
13125         }
13126 
13127         /**
13128          * @see RecyclerView#getChildLayoutPosition(android.view.View)
13129          */
getChildPosition(View view)13130         public int getChildPosition(View view) {
13131             return mRecyclerView.getChildLayoutPosition(view);
13132         }
13133 
13134         /**
13135          * @see RecyclerView.LayoutManager#getChildCount()
13136          */
getChildCount()13137         public int getChildCount() {
13138             return mRecyclerView.mLayout.getChildCount();
13139         }
13140 
13141         /**
13142          * @see RecyclerView.LayoutManager#findViewByPosition(int)
13143          */
findViewByPosition(int position)13144         public View findViewByPosition(int position) {
13145             return mRecyclerView.mLayout.findViewByPosition(position);
13146         }
13147 
13148         /**
13149          * @see RecyclerView#scrollToPosition(int)
13150          * @deprecated Use {@link Action#jumpTo(int)}.
13151          */
13152         @Deprecated
instantScrollToPosition(int position)13153         public void instantScrollToPosition(int position) {
13154             mRecyclerView.scrollToPosition(position);
13155         }
13156 
onChildAttachedToWindow(View child)13157         protected void onChildAttachedToWindow(View child) {
13158             if (getChildPosition(child) == getTargetPosition()) {
13159                 mTargetView = child;
13160                 if (sVerboseLoggingEnabled) {
13161                     Log.d(TAG, "smooth scroll target view has been attached");
13162                 }
13163             }
13164         }
13165 
13166         /**
13167          * Normalizes the vector.
13168          *
13169          * @param scrollVector The vector that points to the target scroll position
13170          */
normalize(@onNull PointF scrollVector)13171         protected void normalize(@NonNull PointF scrollVector) {
13172             final float magnitude = (float) Math.sqrt(scrollVector.x * scrollVector.x
13173                     + scrollVector.y * scrollVector.y);
13174             scrollVector.x /= magnitude;
13175             scrollVector.y /= magnitude;
13176         }
13177 
13178         /**
13179          * Called when smooth scroll is started. This might be a good time to do setup.
13180          */
onStart()13181         protected abstract void onStart();
13182 
13183         /**
13184          * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
13185          *
13186          * @see #stop()
13187          */
onStop()13188         protected abstract void onStop();
13189 
13190         /**
13191          * <p>RecyclerView will call this method each time it scrolls until it can find the target
13192          * position in the layout.</p>
13193          * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
13194          * provided {@link Action} to define the next scroll.</p>
13195          *
13196          * @param dx     Last scroll amount horizontally
13197          * @param dy     Last scroll amount vertically
13198          * @param state  Transient state of RecyclerView
13199          * @param action If you want to trigger a new smooth scroll and cancel the previous one,
13200          *               update this object.
13201          */
onSeekTargetStep(@x int dx, @Px int dy, @NonNull State state, @NonNull Action action)13202         protected abstract void onSeekTargetStep(@Px int dx, @Px int dy, @NonNull State state,
13203                 @NonNull Action action);
13204 
13205         /**
13206          * Called when the target position is laid out. This is the last callback SmoothScroller
13207          * will receive and it should update the provided {@link Action} to define the scroll
13208          * details towards the target view.
13209          *
13210          * @param targetView The view element which render the target position.
13211          * @param state      Transient state of RecyclerView
13212          * @param action     Action instance that you should update to define final scroll action
13213          *                   towards the targetView
13214          */
onTargetFound(@onNull View targetView, @NonNull State state, @NonNull Action action)13215         protected abstract void onTargetFound(@NonNull View targetView, @NonNull State state,
13216                 @NonNull Action action);
13217 
13218         /**
13219          * Holds information about a smooth scroll request by a {@link SmoothScroller}.
13220          */
13221         public static class Action {
13222 
13223             public static final int UNDEFINED_DURATION = RecyclerView.UNDEFINED_DURATION;
13224 
13225             private int mDx;
13226 
13227             private int mDy;
13228 
13229             private int mDuration;
13230 
13231             private int mJumpToPosition = NO_POSITION;
13232 
13233             private Interpolator mInterpolator;
13234 
13235             private boolean mChanged = false;
13236 
13237             // we track this variable to inform custom implementer if they are updating the action
13238             // in every animation callback
13239             private int mConsecutiveUpdates = 0;
13240 
13241             /**
13242              * @param dx Pixels to scroll horizontally
13243              * @param dy Pixels to scroll vertically
13244              */
Action(@x int dx, @Px int dy)13245             public Action(@Px int dx, @Px int dy) {
13246                 this(dx, dy, UNDEFINED_DURATION, null);
13247             }
13248 
13249             /**
13250              * @param dx       Pixels to scroll horizontally
13251              * @param dy       Pixels to scroll vertically
13252              * @param duration Duration of the animation in milliseconds
13253              */
Action(@x int dx, @Px int dy, int duration)13254             public Action(@Px int dx, @Px int dy, int duration) {
13255                 this(dx, dy, duration, null);
13256             }
13257 
13258             /**
13259              * @param dx           Pixels to scroll horizontally
13260              * @param dy           Pixels to scroll vertically
13261              * @param duration     Duration of the animation in milliseconds
13262              * @param interpolator Interpolator to be used when calculating scroll position in each
13263              *                     animation step
13264              */
Action(@x int dx, @Px int dy, int duration, @Nullable Interpolator interpolator)13265             public Action(@Px int dx, @Px int dy, int duration,
13266                     @Nullable Interpolator interpolator) {
13267                 mDx = dx;
13268                 mDy = dy;
13269                 mDuration = duration;
13270                 mInterpolator = interpolator;
13271             }
13272 
13273             /**
13274              * Instead of specifying pixels to scroll, use the target position to jump using
13275              * {@link RecyclerView#scrollToPosition(int)}.
13276              * <p>
13277              * You may prefer using this method if scroll target is really far away and you prefer
13278              * to jump to a location and smooth scroll afterwards.
13279              * <p>
13280              * Note that calling this method takes priority over other update methods such as
13281              * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
13282              * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
13283              * {@link #jumpTo(int)}, the other changes will not be considered for this animation
13284              * frame.
13285              *
13286              * @param targetPosition The target item position to scroll to using instant scrolling.
13287              */
jumpTo(int targetPosition)13288             public void jumpTo(int targetPosition) {
13289                 mJumpToPosition = targetPosition;
13290             }
13291 
hasJumpTarget()13292             boolean hasJumpTarget() {
13293                 return mJumpToPosition >= 0;
13294             }
13295 
runIfNecessary(RecyclerView recyclerView)13296             void runIfNecessary(RecyclerView recyclerView) {
13297                 if (mJumpToPosition >= 0) {
13298                     final int position = mJumpToPosition;
13299                     mJumpToPosition = NO_POSITION;
13300                     recyclerView.jumpToPositionForSmoothScroller(position);
13301                     mChanged = false;
13302                     return;
13303                 }
13304                 if (mChanged) {
13305                     validate();
13306                     recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration, mInterpolator);
13307                     mConsecutiveUpdates++;
13308                     if (mConsecutiveUpdates > 10) {
13309                         // A new action is being set in every animation step. This looks like a bad
13310                         // implementation. Inform developer.
13311                         Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
13312                                 + " you are not changing it unless necessary");
13313                     }
13314                     mChanged = false;
13315                 } else {
13316                     mConsecutiveUpdates = 0;
13317                 }
13318             }
13319 
validate()13320             private void validate() {
13321                 if (mInterpolator != null && mDuration < 1) {
13322                     throw new IllegalStateException("If you provide an interpolator, you must"
13323                             + " set a positive duration");
13324                 } else if (mDuration < 1) {
13325                     throw new IllegalStateException("Scroll duration must be a positive number");
13326                 }
13327             }
13328 
13329             @Px
getDx()13330             public int getDx() {
13331                 return mDx;
13332             }
13333 
setDx(@x int dx)13334             public void setDx(@Px int dx) {
13335                 mChanged = true;
13336                 mDx = dx;
13337             }
13338 
13339             @Px
getDy()13340             public int getDy() {
13341                 return mDy;
13342             }
13343 
setDy(@x int dy)13344             public void setDy(@Px int dy) {
13345                 mChanged = true;
13346                 mDy = dy;
13347             }
13348 
getDuration()13349             public int getDuration() {
13350                 return mDuration;
13351             }
13352 
setDuration(int duration)13353             public void setDuration(int duration) {
13354                 mChanged = true;
13355                 mDuration = duration;
13356             }
13357 
getInterpolator()13358             public @Nullable Interpolator getInterpolator() {
13359                 return mInterpolator;
13360             }
13361 
13362             /**
13363              * Sets the interpolator to calculate scroll steps
13364              *
13365              * @param interpolator The interpolator to use. If you specify an interpolator, you must
13366              *                     also set the duration.
13367              * @see #setDuration(int)
13368              */
setInterpolator(@ullable Interpolator interpolator)13369             public void setInterpolator(@Nullable Interpolator interpolator) {
13370                 mChanged = true;
13371                 mInterpolator = interpolator;
13372             }
13373 
13374             /**
13375              * Updates the action with given parameters.
13376              *
13377              * @param dx           Pixels to scroll horizontally
13378              * @param dy           Pixels to scroll vertically
13379              * @param duration     Duration of the animation in milliseconds
13380              * @param interpolator Interpolator to be used when calculating scroll position in each
13381              *                     animation step
13382              */
update(@x int dx, @Px int dy, int duration, @Nullable Interpolator interpolator)13383             public void update(@Px int dx, @Px int dy, int duration,
13384                     @Nullable Interpolator interpolator) {
13385                 mDx = dx;
13386                 mDy = dy;
13387                 mDuration = duration;
13388                 mInterpolator = interpolator;
13389                 mChanged = true;
13390             }
13391         }
13392 
13393         /**
13394          * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
13395          * to provide a hint to a {@link SmoothScroller} about the location of the target position.
13396          */
13397         public interface ScrollVectorProvider {
13398             /**
13399              * Should calculate the vector that points to the direction where the target position
13400              * can be found.
13401              * <p>
13402              * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
13403              * the target position.
13404              * <p>
13405              * The magnitude of the vector is not important. It is always normalized before being
13406              * used by the {@link LinearSmoothScroller}.
13407              * <p>
13408              * LayoutManager should not check whether the position exists in the adapter or not.
13409              *
13410              * @param targetPosition the target position to which the returned vector should point
13411              * @return the scroll vector for a given position.
13412              */
computeScrollVectorForPosition(int targetPosition)13413             @Nullable PointF computeScrollVectorForPosition(int targetPosition);
13414         }
13415     }
13416 
13417     static class AdapterDataObservable extends Observable<AdapterDataObserver> {
hasObservers()13418         public boolean hasObservers() {
13419             return !mObservers.isEmpty();
13420         }
13421 
notifyChanged()13422         public void notifyChanged() {
13423             // since onChanged() is implemented by the app, it could do anything, including
13424             // removing itself from {@link mObservers} - and that could cause problems if
13425             // an iterator is used on the ArrayList {@link mObservers}.
13426             // to avoid such problems, just march thru the list in the reverse order.
13427             for (int i = mObservers.size() - 1; i >= 0; i--) {
13428                 mObservers.get(i).onChanged();
13429             }
13430         }
13431 
notifyStateRestorationPolicyChanged()13432         public void notifyStateRestorationPolicyChanged() {
13433             for (int i = mObservers.size() - 1; i >= 0; i--) {
13434                 mObservers.get(i).onStateRestorationPolicyChanged();
13435             }
13436         }
13437 
notifyItemRangeChanged(int positionStart, int itemCount)13438         public void notifyItemRangeChanged(int positionStart, int itemCount) {
13439             notifyItemRangeChanged(positionStart, itemCount, null);
13440         }
13441 
notifyItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload)13442         public void notifyItemRangeChanged(int positionStart, int itemCount,
13443                 @Nullable Object payload) {
13444             // since onItemRangeChanged() is implemented by the app, it could do anything, including
13445             // removing itself from {@link mObservers} - and that could cause problems if
13446             // an iterator is used on the ArrayList {@link mObservers}.
13447             // to avoid such problems, just march thru the list in the reverse order.
13448             for (int i = mObservers.size() - 1; i >= 0; i--) {
13449                 mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
13450             }
13451         }
13452 
notifyItemRangeInserted(int positionStart, int itemCount)13453         public void notifyItemRangeInserted(int positionStart, int itemCount) {
13454             // since onItemRangeInserted() is implemented by the app, it could do anything,
13455             // including removing itself from {@link mObservers} - and that could cause problems if
13456             // an iterator is used on the ArrayList {@link mObservers}.
13457             // to avoid such problems, just march thru the list in the reverse order.
13458             for (int i = mObservers.size() - 1; i >= 0; i--) {
13459                 mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
13460             }
13461         }
13462 
notifyItemRangeRemoved(int positionStart, int itemCount)13463         public void notifyItemRangeRemoved(int positionStart, int itemCount) {
13464             // since onItemRangeRemoved() is implemented by the app, it could do anything, including
13465             // removing itself from {@link mObservers} - and that could cause problems if
13466             // an iterator is used on the ArrayList {@link mObservers}.
13467             // to avoid such problems, just march thru the list in the reverse order.
13468             for (int i = mObservers.size() - 1; i >= 0; i--) {
13469                 mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
13470             }
13471         }
13472 
notifyItemMoved(int fromPosition, int toPosition)13473         public void notifyItemMoved(int fromPosition, int toPosition) {
13474             for (int i = mObservers.size() - 1; i >= 0; i--) {
13475                 mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
13476             }
13477         }
13478     }
13479 
13480     /**
13481      * This is public so that the CREATOR can be accessed on cold launch.
13482      *
13483      */
13484     @RestrictTo(LIBRARY)
13485     public static class SavedState extends AbsSavedState {
13486 
13487         Parcelable mLayoutState;
13488 
13489         /**
13490          * called by CREATOR
13491          */
13492         @SuppressWarnings("deprecation")
SavedState(Parcel in, ClassLoader loader)13493         SavedState(Parcel in, ClassLoader loader) {
13494             super(in, loader);
13495             mLayoutState = in.readParcelable(
13496                     loader != null ? loader : LayoutManager.class.getClassLoader());
13497         }
13498 
13499         /**
13500          * Called by onSaveInstanceState
13501          */
SavedState(Parcelable superState)13502         SavedState(Parcelable superState) {
13503             super(superState);
13504         }
13505 
13506         @Override
writeToParcel(Parcel dest, int flags)13507         public void writeToParcel(Parcel dest, int flags) {
13508             super.writeToParcel(dest, flags);
13509             dest.writeParcelable(mLayoutState, 0);
13510         }
13511 
copyFrom(SavedState other)13512         void copyFrom(SavedState other) {
13513             mLayoutState = other.mLayoutState;
13514         }
13515 
13516         public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
13517             @Override
13518             public SavedState createFromParcel(Parcel in, ClassLoader loader) {
13519                 return new SavedState(in, loader);
13520             }
13521 
13522             @Override
13523             public SavedState createFromParcel(Parcel in) {
13524                 return new SavedState(in, null);
13525             }
13526 
13527             @Override
13528             public SavedState[] newArray(int size) {
13529                 return new SavedState[size];
13530             }
13531         };
13532     }
13533 
13534     /**
13535      * <p>Contains useful information about the current RecyclerView state like target scroll
13536      * position or view focus. State object can also keep arbitrary data, identified by resource
13537      * ids.</p>
13538      * <p>Often times, RecyclerView components will need to pass information between each other.
13539      * To provide a well defined data bus between components, RecyclerView passes the same State
13540      * object to component callbacks and these components can use it to exchange data.</p>
13541      * <p>If you implement custom components, you can use State's put/get/remove methods to pass
13542      * data between your components without needing to manage their lifecycles.</p>
13543      */
13544     public static class State {
13545         static final int STEP_START = 1;
13546         static final int STEP_LAYOUT = 1 << 1;
13547         static final int STEP_ANIMATIONS = 1 << 2;
13548 
assertLayoutStep(int accepted)13549         void assertLayoutStep(int accepted) {
13550             if ((accepted & mLayoutStep) == 0) {
13551                 throw new IllegalStateException("Layout state should be one of "
13552                         + Integer.toBinaryString(accepted) + " but it is "
13553                         + Integer.toBinaryString(mLayoutStep));
13554             }
13555         }
13556 
13557 
13558         /** Owned by SmoothScroller */
13559         int mTargetPosition = RecyclerView.NO_POSITION;
13560 
13561         private SparseArray<Object> mData;
13562 
13563         ////////////////////////////////////////////////////////////////////////////////////////////
13564         // Fields below are carried from one layout pass to the next
13565         ////////////////////////////////////////////////////////////////////////////////////////////
13566 
13567         /**
13568          * Number of items adapter had in the previous layout.
13569          */
13570         int mPreviousLayoutItemCount = 0;
13571 
13572         /**
13573          * Number of items that were NOT laid out but has been deleted from the adapter after the
13574          * previous layout.
13575          */
13576         int mDeletedInvisibleItemCountSincePreviousLayout = 0;
13577 
13578         ////////////////////////////////////////////////////////////////////////////////////////////
13579         // Fields below must be updated or cleared before they are used (generally before a pass)
13580         ////////////////////////////////////////////////////////////////////////////////////////////
13581 
13582         @IntDef(flag = true, value = {
13583                 STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
13584         })
13585         @Retention(RetentionPolicy.SOURCE)
13586         @interface LayoutState {
13587         }
13588 
13589         @LayoutState
13590         int mLayoutStep = STEP_START;
13591 
13592         /**
13593          * Number of items adapter has.
13594          */
13595         int mItemCount = 0;
13596 
13597         boolean mStructureChanged = false;
13598 
13599         /**
13600          * True if the associated {@link RecyclerView} is in the pre-layout step where it is having
13601          * its {@link LayoutManager} layout items where they will be at the beginning of a set of
13602          * predictive item animations.
13603          */
13604         boolean mInPreLayout = false;
13605 
13606         boolean mTrackOldChangeHolders = false;
13607 
13608         boolean mIsMeasuring = false;
13609 
13610         ////////////////////////////////////////////////////////////////////////////////////////////
13611         // Fields below are always reset outside of the pass (or passes) that use them
13612         ////////////////////////////////////////////////////////////////////////////////////////////
13613 
13614         boolean mRunSimpleAnimations = false;
13615 
13616         boolean mRunPredictiveAnimations = false;
13617 
13618         /**
13619          * This data is saved before a layout calculation happens. After the layout is finished,
13620          * if the previously focused view has been replaced with another view for the same item, we
13621          * move the focus to the new item automatically.
13622          */
13623         int mFocusedItemPosition;
13624         long mFocusedItemId;
13625         // when a sub child has focus, record its id and see if we can directly request focus on
13626         // that one instead
13627         int mFocusedSubChildId;
13628 
13629         int mRemainingScrollHorizontal;
13630         int mRemainingScrollVertical;
13631 
13632         ////////////////////////////////////////////////////////////////////////////////////////////
13633 
13634         /**
13635          * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
13636          * prior to any layout passes.
13637          *
13638          * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
13639          * that Recycler#getViewForPosition() can function safely.</p>
13640          */
prepareForNestedPrefetch(Adapter adapter)13641         void prepareForNestedPrefetch(Adapter adapter) {
13642             mLayoutStep = STEP_START;
13643             mItemCount = adapter.getItemCount();
13644             mInPreLayout = false;
13645             mTrackOldChangeHolders = false;
13646             mIsMeasuring = false;
13647         }
13648 
13649         /**
13650          * Returns true if the RecyclerView is currently measuring the layout. This value is
13651          * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
13652          * has non-exact measurement specs.
13653          * <p>
13654          * Note that if the LayoutManager supports predictive animations and it is calculating the
13655          * pre-layout step, this value will be {@code false} even if the RecyclerView is in
13656          * {@code onMeasure} call. This is because pre-layout means the previous state of the
13657          * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
13658          * LayoutManager is always guaranteed to receive another call to
13659          * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
13660          *
13661          * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
13662          */
isMeasuring()13663         public boolean isMeasuring() {
13664             return mIsMeasuring;
13665         }
13666 
13667         /**
13668          * Returns true if the {@link RecyclerView} is in the pre-layout step where it is having its
13669          * {@link LayoutManager} layout items where they will be at the beginning of a set of
13670          * predictive item animations.
13671          */
isPreLayout()13672         public boolean isPreLayout() {
13673             return mInPreLayout;
13674         }
13675 
13676         /**
13677          * Returns whether RecyclerView will run predictive animations in this layout pass
13678          * or not.
13679          *
13680          * @return true if RecyclerView is calculating predictive animations to be run at the end
13681          * of the layout pass.
13682          */
willRunPredictiveAnimations()13683         public boolean willRunPredictiveAnimations() {
13684             return mRunPredictiveAnimations;
13685         }
13686 
13687         /**
13688          * Returns whether RecyclerView will run simple animations in this layout pass
13689          * or not.
13690          *
13691          * @return true if RecyclerView is calculating simple animations to be run at the end of
13692          * the layout pass.
13693          */
willRunSimpleAnimations()13694         public boolean willRunSimpleAnimations() {
13695             return mRunSimpleAnimations;
13696         }
13697 
13698         /**
13699          * Removes the mapping from the specified id, if there was any.
13700          *
13701          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
13702          *                   preserve cross functionality and avoid conflicts.
13703          */
remove(int resourceId)13704         public void remove(int resourceId) {
13705             if (mData == null) {
13706                 return;
13707             }
13708             mData.remove(resourceId);
13709         }
13710 
13711         /**
13712          * Gets the Object mapped from the specified id, or <code>null</code>
13713          * if no such data exists.
13714          *
13715          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
13716          *                   to
13717          *                   preserve cross functionality and avoid conflicts.
13718          */
13719         @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
get(int resourceId)13720         public <T> T get(int resourceId) {
13721             if (mData == null) {
13722                 return null;
13723             }
13724             return (T) mData.get(resourceId);
13725         }
13726 
13727         /**
13728          * Adds a mapping from the specified id to the specified value, replacing the previous
13729          * mapping from the specified key if there was one.
13730          *
13731          * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
13732          *                   preserve cross functionality and avoid conflicts.
13733          * @param data       The data you want to associate with the resourceId.
13734          */
put(int resourceId, Object data)13735         public void put(int resourceId, Object data) {
13736             if (mData == null) {
13737                 mData = new SparseArray<Object>();
13738             }
13739             mData.put(resourceId, data);
13740         }
13741 
13742         /**
13743          * If scroll is triggered to make a certain item visible, this value will return the
13744          * adapter index of that item.
13745          *
13746          * @return Adapter index of the target item or
13747          * {@link RecyclerView#NO_POSITION} if there is no target
13748          * position.
13749          */
getTargetScrollPosition()13750         public int getTargetScrollPosition() {
13751             return mTargetPosition;
13752         }
13753 
13754         /**
13755          * Returns if current scroll has a target position.
13756          *
13757          * @return true if scroll is being triggered to make a certain position visible
13758          * @see #getTargetScrollPosition()
13759          */
hasTargetScrollPosition()13760         public boolean hasTargetScrollPosition() {
13761             return mTargetPosition != RecyclerView.NO_POSITION;
13762         }
13763 
13764         /**
13765          * @return true if the structure of the data set has changed since the last call to
13766          * onLayoutChildren, false otherwise
13767          */
didStructureChange()13768         public boolean didStructureChange() {
13769             return mStructureChanged;
13770         }
13771 
13772         /**
13773          * Returns the total number of items that can be laid out. Note that this number is not
13774          * necessarily equal to the number of items in the adapter, so you should always use this
13775          * number for your position calculations and never access the adapter directly.
13776          * <p>
13777          * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
13778          * data changes on existing Views. These calculations are used to decide which animations
13779          * should be run.
13780          * <p>
13781          * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
13782          * present the correct state to LayoutManager in pre-layout pass.
13783          * <p>
13784          * For example, a newly added item is not included in pre-layout item count because
13785          * pre-layout reflects the contents of the adapter before the item is added. Behind the
13786          * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
13787          * LayoutManager does not know about the new item's existence in pre-layout. The item will
13788          * be available in second layout pass and will be included in the item count. Similar
13789          * adjustments are made for moved and removed items as well.
13790          * <p>
13791          * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
13792          *
13793          * @return The number of items currently available
13794          * @see LayoutManager#getItemCount()
13795          */
getItemCount()13796         public int getItemCount() {
13797             return mInPreLayout
13798                     ? (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout)
13799                     : mItemCount;
13800         }
13801 
13802         /**
13803          * Returns remaining horizontal scroll distance of an ongoing scroll animation(fling/
13804          * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
13805          * other than {@link #SCROLL_STATE_SETTLING}.
13806          *
13807          * @return Remaining horizontal scroll distance
13808          */
getRemainingScrollHorizontal()13809         public int getRemainingScrollHorizontal() {
13810             return mRemainingScrollHorizontal;
13811         }
13812 
13813         /**
13814          * Returns remaining vertical scroll distance of an ongoing scroll animation(fling/
13815          * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
13816          * other than {@link #SCROLL_STATE_SETTLING}.
13817          *
13818          * @return Remaining vertical scroll distance
13819          */
getRemainingScrollVertical()13820         public int getRemainingScrollVertical() {
13821             return mRemainingScrollVertical;
13822         }
13823 
13824         @Override
toString()13825         public String toString() {
13826             return "State{"
13827                     + "mTargetPosition=" + mTargetPosition
13828                     + ", mData=" + mData
13829                     + ", mItemCount=" + mItemCount
13830                     + ", mIsMeasuring=" + mIsMeasuring
13831                     + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
13832                     + ", mDeletedInvisibleItemCountSincePreviousLayout="
13833                     + mDeletedInvisibleItemCountSincePreviousLayout
13834                     + ", mStructureChanged=" + mStructureChanged
13835                     + ", mInPreLayout=" + mInPreLayout
13836                     + ", mRunSimpleAnimations=" + mRunSimpleAnimations
13837                     + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
13838                     + '}';
13839         }
13840     }
13841 
13842     /**
13843      * This class defines the behavior of fling if the developer wishes to handle it.
13844      * <p>
13845      * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
13846      *
13847      * @see #setOnFlingListener(OnFlingListener)
13848      */
13849     public abstract static class OnFlingListener {
13850 
13851         /**
13852          * Override this to handle a fling given the velocities in both x and y directions.
13853          * Note that this method will only be called if the associated {@link LayoutManager}
13854          * supports scrolling and the fling is not handled by nested scrolls first.
13855          *
13856          * @param velocityX the fling velocity on the X axis
13857          * @param velocityY the fling velocity on the Y axis
13858          * @return true if the fling was handled, false otherwise.
13859          */
onFling(int velocityX, int velocityY)13860         public abstract boolean onFling(int velocityX, int velocityY);
13861     }
13862 
13863     /**
13864      * Internal listener that manages items after animations finish. This is how items are
13865      * retained (not recycled) during animations, but allowed to be recycled afterwards.
13866      * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
13867      * method on the animator's listener when it is done animating any item.
13868      */
13869     private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
13870 
ItemAnimatorRestoreListener()13871         ItemAnimatorRestoreListener() {
13872         }
13873 
13874         @Override
onAnimationFinished(ViewHolder item)13875         public void onAnimationFinished(ViewHolder item) {
13876             item.setIsRecyclable(true);
13877             if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
13878                 item.mShadowedHolder = null;
13879             }
13880             // always null this because an OldViewHolder can never become NewViewHolder w/o being
13881             // recycled.
13882             item.mShadowingHolder = null;
13883             if (!item.shouldBeKeptAsChild()) {
13884                 if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
13885                     removeDetachedView(item.itemView, false);
13886                 }
13887             }
13888         }
13889     }
13890 
13891     /**
13892      * This class defines the animations that take place on items as changes are made
13893      * to the adapter.
13894      *
13895      * Subclasses of ItemAnimator can be used to implement custom animations for actions on
13896      * ViewHolder items. The RecyclerView will manage retaining these items while they
13897      * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
13898      * when a ViewHolder's animation is finished. In other words, there must be a matching
13899      * {@link #dispatchAnimationFinished(ViewHolder)} call for each
13900      * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
13901      * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
13902      * animateChange()}
13903      * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
13904      * and
13905      * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
13906      * animateDisappearance()} call.
13907      *
13908      * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
13909      *
13910      * @see #setItemAnimator(ItemAnimator)
13911      */
13912     @SuppressWarnings("UnusedParameters")
13913     public abstract static class ItemAnimator {
13914 
13915         /**
13916          * The Item represented by this ViewHolder is updated.
13917          * <p>
13918          *
13919          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
13920          */
13921         public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
13922 
13923         /**
13924          * The Item represented by this ViewHolder is removed from the adapter.
13925          * <p>
13926          *
13927          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
13928          */
13929         public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
13930 
13931         /**
13932          * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
13933          * represented by this ViewHolder is invalid.
13934          * <p>
13935          *
13936          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
13937          */
13938         public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
13939 
13940         /**
13941          * The position of the Item represented by this ViewHolder has been changed. This flag is
13942          * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
13943          * any adapter change that may have a side effect on this item. (e.g. The item before this
13944          * one has been removed from the Adapter).
13945          * <p>
13946          *
13947          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
13948          */
13949         public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
13950 
13951         /**
13952          * This ViewHolder was not laid out but has been added to the layout in pre-layout state
13953          * by the {@link LayoutManager}. This means that the item was already in the Adapter but
13954          * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
13955          * to add new items in pre-layout to specify their virtual location when they are invisible
13956          * (e.g. to specify the item should <i>animate in</i> from below the visible area).
13957          * <p>
13958          *
13959          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
13960          */
13961         public static final int FLAG_APPEARED_IN_PRE_LAYOUT =
13962                 ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
13963 
13964         /**
13965          * The set of flags that might be passed to
13966          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
13967          */
13968         @IntDef(flag = true, value = {
13969                 FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
13970                 FLAG_APPEARED_IN_PRE_LAYOUT
13971         })
13972         @Retention(RetentionPolicy.SOURCE)
13973         public @interface AdapterChanges {
13974         }
13975 
13976         private ItemAnimatorListener mListener = null;
13977         private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
13978                 new ArrayList<ItemAnimatorFinishedListener>();
13979 
13980         private long mAddDuration = 120;
13981         private long mRemoveDuration = 120;
13982         private long mMoveDuration = 250;
13983         private long mChangeDuration = 250;
13984 
13985         /**
13986          * Gets the current duration for which all move animations will run.
13987          *
13988          * @return The current move duration
13989          */
getMoveDuration()13990         public long getMoveDuration() {
13991             return mMoveDuration;
13992         }
13993 
13994         /**
13995          * Sets the duration for which all move animations will run.
13996          *
13997          * @param moveDuration The move duration
13998          */
setMoveDuration(long moveDuration)13999         public void setMoveDuration(long moveDuration) {
14000             mMoveDuration = moveDuration;
14001         }
14002 
14003         /**
14004          * Gets the current duration for which all add animations will run.
14005          *
14006          * @return The current add duration
14007          */
getAddDuration()14008         public long getAddDuration() {
14009             return mAddDuration;
14010         }
14011 
14012         /**
14013          * Sets the duration for which all add animations will run.
14014          *
14015          * @param addDuration The add duration
14016          */
setAddDuration(long addDuration)14017         public void setAddDuration(long addDuration) {
14018             mAddDuration = addDuration;
14019         }
14020 
14021         /**
14022          * Gets the current duration for which all remove animations will run.
14023          *
14024          * @return The current remove duration
14025          */
getRemoveDuration()14026         public long getRemoveDuration() {
14027             return mRemoveDuration;
14028         }
14029 
14030         /**
14031          * Sets the duration for which all remove animations will run.
14032          *
14033          * @param removeDuration The remove duration
14034          */
setRemoveDuration(long removeDuration)14035         public void setRemoveDuration(long removeDuration) {
14036             mRemoveDuration = removeDuration;
14037         }
14038 
14039         /**
14040          * Gets the current duration for which all change animations will run.
14041          *
14042          * @return The current change duration
14043          */
getChangeDuration()14044         public long getChangeDuration() {
14045             return mChangeDuration;
14046         }
14047 
14048         /**
14049          * Sets the duration for which all change animations will run.
14050          *
14051          * @param changeDuration The change duration
14052          */
setChangeDuration(long changeDuration)14053         public void setChangeDuration(long changeDuration) {
14054             mChangeDuration = changeDuration;
14055         }
14056 
14057         /**
14058          * Internal only:
14059          * Sets the listener that must be called when the animator is finished
14060          * animating the item (or immediately if no animation happens). This is set
14061          * internally and is not intended to be set by external code.
14062          *
14063          * @param listener The listener that must be called.
14064          */
setListener(ItemAnimatorListener listener)14065         void setListener(ItemAnimatorListener listener) {
14066             mListener = listener;
14067         }
14068 
14069         /**
14070          * Called by the RecyclerView before the layout begins. Item animator should record
14071          * necessary information about the View before it is potentially rebound, moved or removed.
14072          * <p>
14073          * The data returned from this method will be passed to the related <code>animate**</code>
14074          * methods.
14075          * <p>
14076          * Note that this method may be called after pre-layout phase if LayoutManager adds new
14077          * Views to the layout in pre-layout pass.
14078          * <p>
14079          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
14080          * the View and the adapter change flags.
14081          *
14082          * @param state       The current State of RecyclerView which includes some useful data
14083          *                    about the layout that will be calculated.
14084          * @param viewHolder  The ViewHolder whose information should be recorded.
14085          * @param changeFlags Additional information about what changes happened in the Adapter
14086          *                    about the Item represented by this ViewHolder. For instance, if
14087          *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
14088          * @param payloads    The payload list that was previously passed to
14089          *                    {@link Adapter#notifyItemChanged(int, Object)} or
14090          *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
14091          * @return An ItemHolderInfo instance that preserves necessary information about the
14092          * ViewHolder. This object will be passed back to related <code>animate**</code> methods
14093          * after layout is complete.
14094          * @see #recordPostLayoutInformation(State, ViewHolder)
14095          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14096          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14097          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
14098          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14099          */
recordPreLayoutInformation(@onNull State state, @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags, @NonNull List<Object> payloads)14100         public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
14101                 @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
14102                 @NonNull List<Object> payloads) {
14103             return obtainHolderInfo().setFrom(viewHolder);
14104         }
14105 
14106         /**
14107          * Called by the RecyclerView after the layout is complete. Item animator should record
14108          * necessary information about the View's final state.
14109          * <p>
14110          * The data returned from this method will be passed to the related <code>animate**</code>
14111          * methods.
14112          * <p>
14113          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
14114          * the View.
14115          *
14116          * @param state      The current State of RecyclerView which includes some useful data about
14117          *                   the layout that will be calculated.
14118          * @param viewHolder The ViewHolder whose information should be recorded.
14119          * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
14120          * This object will be passed back to related <code>animate**</code> methods when
14121          * RecyclerView decides how items should be animated.
14122          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
14123          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14124          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14125          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
14126          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14127          */
recordPostLayoutInformation(@onNull State state, @NonNull ViewHolder viewHolder)14128         public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
14129                 @NonNull ViewHolder viewHolder) {
14130             return obtainHolderInfo().setFrom(viewHolder);
14131         }
14132 
14133         /**
14134          * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
14135          * <p>
14136          * This means that the View was a child of the LayoutManager when layout started but has
14137          * been removed by the LayoutManager. It might have been removed from the adapter or simply
14138          * become invisible due to other factors. You can distinguish these two cases by checking
14139          * the change flags that were passed to
14140          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
14141          * <p>
14142          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
14143          * animation callback method which will be called by the RecyclerView depends on the
14144          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
14145          * LayoutManager's decision whether to layout the changed version of a disappearing
14146          * ViewHolder or not. RecyclerView will call
14147          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
14148          * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
14149          * returns {@code false} from
14150          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
14151          * LayoutManager lays out a new disappearing view that holds the updated information.
14152          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
14153          * <p>
14154          * If LayoutManager supports predictive animations, it might provide a target disappear
14155          * location for the View by laying it out in that location. When that happens,
14156          * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
14157          * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
14158          * <p>
14159          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
14160          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
14161          * decides not to animate the view).
14162          *
14163          * @param viewHolder     The ViewHolder which should be animated
14164          * @param preLayoutInfo  The information that was returned from
14165          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
14166          * @param postLayoutInfo The information that was returned from
14167          *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
14168          *                       null if the LayoutManager did not layout the item.
14169          * @return true if a later call to {@link #runPendingAnimations()} is requested,
14170          * false otherwise.
14171          */
animateDisappearance(@onNull ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo)14172         public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
14173                 @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
14174 
14175         /**
14176          * Called by the RecyclerView when a ViewHolder is added to the layout.
14177          * <p>
14178          * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
14179          * but has  been added by the LayoutManager. It might be newly added to the adapter or
14180          * simply become visible due to other factors.
14181          * <p>
14182          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
14183          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
14184          * decides not to animate the view).
14185          *
14186          * @param viewHolder     The ViewHolder which should be animated
14187          * @param preLayoutInfo  The information that was returned from
14188          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
14189          *                       Might be null if Item was just added to the adapter or
14190          *                       LayoutManager does not support predictive animations or it could
14191          *                       not predict that this ViewHolder will become visible.
14192          * @param postLayoutInfo The information that was returned from {@link
14193          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
14194          * @return true if a later call to {@link #runPendingAnimations()} is requested,
14195          * false otherwise.
14196          */
animateAppearance(@onNull ViewHolder viewHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)14197         public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
14198                 @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
14199 
14200         /**
14201          * Called by the RecyclerView when a ViewHolder is present in both before and after the
14202          * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
14203          * for it or a {@link Adapter#notifyDataSetChanged()} call.
14204          * <p>
14205          * This ViewHolder still represents the same data that it was representing when the layout
14206          * started but its position / size may be changed by the LayoutManager.
14207          * <p>
14208          * If the Item's layout position didn't change, RecyclerView still calls this method because
14209          * it does not track this information (or does not necessarily know that an animation is
14210          * not required). Your ItemAnimator should handle this case and if there is nothing to
14211          * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
14212          * <code>false</code>.
14213          * <p>
14214          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
14215          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
14216          * decides not to animate the view).
14217          *
14218          * @param viewHolder     The ViewHolder which should be animated
14219          * @param preLayoutInfo  The information that was returned from
14220          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
14221          * @param postLayoutInfo The information that was returned from {@link
14222          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
14223          * @return true if a later call to {@link #runPendingAnimations()} is requested,
14224          * false otherwise.
14225          */
animatePersistence(@onNull ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)14226         public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
14227                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
14228 
14229         /**
14230          * Called by the RecyclerView when an adapter item is present both before and after the
14231          * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
14232          * for it. This method may also be called when
14233          * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
14234          * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
14235          * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
14236          * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
14237          * called for the new ViewHolder and the old one will be recycled.
14238          * <p>
14239          * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
14240          * a good possibility that item contents didn't really change but it is rebound from the
14241          * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
14242          * screen didn't change and your animator should handle this case as well and avoid creating
14243          * unnecessary animations.
14244          * <p>
14245          * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
14246          * previous presentation of the item as-is and supply a new ViewHolder for the updated
14247          * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
14248          * This is useful if you don't know the contents of the Item and would like
14249          * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
14250          * <p>
14251          * When you are writing a custom item animator for your layout, it might be more performant
14252          * and elegant to re-use the same ViewHolder and animate the content changes manually.
14253          * <p>
14254          * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
14255          * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
14256          * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
14257          * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
14258          * which represent the same Item. In that case, only the new ViewHolder is visible
14259          * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
14260          * <p>
14261          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
14262          * ViewHolder when their animation is complete
14263          * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
14264          * animate the view).
14265          * <p>
14266          * If oldHolder and newHolder are the same instance, you should call
14267          * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
14268          * <p>
14269          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
14270          * animation callback method which will be called by the RecyclerView depends on the
14271          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
14272          * LayoutManager's decision whether to layout the changed version of a disappearing
14273          * ViewHolder or not. RecyclerView will call
14274          * {@code animateChange} instead of
14275          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14276          * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
14277          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
14278          * LayoutManager lays out a new disappearing view that holds the updated information.
14279          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
14280          *
14281          * @param oldHolder      The ViewHolder before the layout is started, might be the same
14282          *                       instance with newHolder.
14283          * @param newHolder      The ViewHolder after the layout is finished, might be the same
14284          *                       instance with oldHolder.
14285          * @param preLayoutInfo  The information that was returned from
14286          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
14287          * @param postLayoutInfo The information that was returned from {@link
14288          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
14289          * @return true if a later call to {@link #runPendingAnimations()} is requested,
14290          * false otherwise.
14291          */
animateChange(@onNull ViewHolder oldHolder, @NonNull ViewHolder newHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)14292         public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
14293                 @NonNull ViewHolder newHolder,
14294                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
14295 
14296         @AdapterChanges
buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder)14297         static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
14298             int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
14299             if (viewHolder.isInvalid()) {
14300                 return FLAG_INVALIDATED;
14301             }
14302             if ((flags & FLAG_INVALIDATED) == 0) {
14303                 final int oldPos = viewHolder.getOldPosition();
14304                 final int pos = viewHolder.getAbsoluteAdapterPosition();
14305                 if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
14306                     flags |= FLAG_MOVED;
14307                 }
14308             }
14309             return flags;
14310         }
14311 
14312         /**
14313          * Called when there are pending animations waiting to be started. This state
14314          * is governed by the return values from
14315          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14316          * animateAppearance()},
14317          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
14318          * animateChange()}
14319          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14320          * animatePersistence()}, and
14321          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14322          * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
14323          * called later to start the associated animations. runPendingAnimations() will be scheduled
14324          * to be run on the next frame.
14325          */
runPendingAnimations()14326         public abstract void runPendingAnimations();
14327 
14328         /**
14329          * Method called when an animation on a view should be ended immediately.
14330          * This could happen when other events, like scrolling, occur, so that
14331          * animating views can be quickly put into their proper end locations.
14332          * Implementations should ensure that any animations running on the item
14333          * are canceled and affected properties are set to their end values.
14334          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
14335          * animation since the animations are effectively done when this method is called.
14336          *
14337          * @param item The item for which an animation should be stopped.
14338          */
endAnimation(@onNull ViewHolder item)14339         public abstract void endAnimation(@NonNull ViewHolder item);
14340 
14341         /**
14342          * Method called when all item animations should be ended immediately.
14343          * This could happen when other events, like scrolling, occur, so that
14344          * animating views can be quickly put into their proper end locations.
14345          * Implementations should ensure that any animations running on any items
14346          * are canceled and affected properties are set to their end values.
14347          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
14348          * animation since the animations are effectively done when this method is called.
14349          */
endAnimations()14350         public abstract void endAnimations();
14351 
14352         /**
14353          * Method which returns whether there are any item animations currently running.
14354          * This method can be used to determine whether to delay other actions until
14355          * animations end.
14356          *
14357          * @return true if there are any item animations currently running, false otherwise.
14358          */
isRunning()14359         public abstract boolean isRunning();
14360 
14361         /**
14362          * Method to be called by subclasses when an animation is finished.
14363          * <p>
14364          * For each call RecyclerView makes to
14365          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14366          * animateAppearance()},
14367          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14368          * animatePersistence()}, or
14369          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14370          * animateDisappearance()}, there
14371          * should
14372          * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
14373          * <p>
14374          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
14375          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
14376          * and <code>newHolder</code>  (if they are not the same instance).
14377          *
14378          * @param viewHolder The ViewHolder whose animation is finished.
14379          * @see #onAnimationFinished(ViewHolder)
14380          */
dispatchAnimationFinished(@onNull ViewHolder viewHolder)14381         public final void dispatchAnimationFinished(@NonNull ViewHolder viewHolder) {
14382             onAnimationFinished(viewHolder);
14383             if (mListener != null) {
14384                 mListener.onAnimationFinished(viewHolder);
14385             }
14386         }
14387 
14388         /**
14389          * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
14390          * ItemAnimator.
14391          *
14392          * @param viewHolder The ViewHolder whose animation is finished. There might still be other
14393          *                   animations running on this ViewHolder.
14394          * @see #dispatchAnimationFinished(ViewHolder)
14395          */
onAnimationFinished(@onNull ViewHolder viewHolder)14396         public void onAnimationFinished(@NonNull ViewHolder viewHolder) {
14397         }
14398 
14399         /**
14400          * Method to be called by subclasses when an animation is started.
14401          * <p>
14402          * For each call RecyclerView makes to
14403          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14404          * animateAppearance()},
14405          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14406          * animatePersistence()}, or
14407          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
14408          * animateDisappearance()}, there should be a matching
14409          * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
14410          * <p>
14411          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
14412          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
14413          * and <code>newHolder</code> (if they are not the same instance).
14414          * <p>
14415          * If your ItemAnimator decides not to animate a ViewHolder, it should call
14416          * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
14417          * {@link #dispatchAnimationStarted(ViewHolder)}.
14418          *
14419          * @param viewHolder The ViewHolder whose animation is starting.
14420          * @see #onAnimationStarted(ViewHolder)
14421          */
dispatchAnimationStarted(@onNull ViewHolder viewHolder)14422         public final void dispatchAnimationStarted(@NonNull ViewHolder viewHolder) {
14423             onAnimationStarted(viewHolder);
14424         }
14425 
14426         /**
14427          * Called when a new animation is started on the given ViewHolder.
14428          *
14429          * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
14430          *                   might already be animating and this might be another animation.
14431          * @see #dispatchAnimationStarted(ViewHolder)
14432          */
onAnimationStarted(@onNull ViewHolder viewHolder)14433         public void onAnimationStarted(@NonNull ViewHolder viewHolder) {
14434 
14435         }
14436 
14437         /**
14438          * Like {@link #isRunning()}, this method returns whether there are any item
14439          * animations currently running. Additionally, the listener passed in will be called
14440          * when there are no item animations running, either immediately (before the method
14441          * returns) if no animations are currently running, or when the currently running
14442          * animations are {@link #dispatchAnimationsFinished() finished}.
14443          *
14444          * <p>Note that the listener is transient - it is either called immediately and not
14445          * stored at all, or stored only until it is called when running animations
14446          * are finished sometime later.</p>
14447          *
14448          * @param listener A listener to be called immediately if no animations are running
14449          *                 or later when currently-running animations have finished. A null
14450          *                 listener is
14451          *                 equivalent to calling {@link #isRunning()}.
14452          * @return true if there are any item animations currently running, false otherwise.
14453          */
isRunning(@ullable ItemAnimatorFinishedListener listener)14454         public final boolean isRunning(@Nullable ItemAnimatorFinishedListener listener) {
14455             boolean running = isRunning();
14456             if (listener != null) {
14457                 if (!running) {
14458                     listener.onAnimationsFinished();
14459                 } else {
14460                     mFinishedListeners.add(listener);
14461                 }
14462             }
14463             return running;
14464         }
14465 
14466         /**
14467          * When an item is changed, ItemAnimator can decide whether it wants to re-use
14468          * the same ViewHolder for animations or RecyclerView should create a copy of the
14469          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
14470          * <p>
14471          * Note that this method will only be called if the {@link ViewHolder} still has the same
14472          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
14473          * both {@link ViewHolder}s in the
14474          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
14475          * <p>
14476          * If your application is using change payloads, you can override
14477          * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
14478          *
14479          * @param viewHolder The ViewHolder which represents the changed item's old content.
14480          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
14481          * RecyclerView should create a new ViewHolder and pass this ViewHolder to the
14482          * ItemAnimator to animate. Default implementation returns <code>true</code>.
14483          * @see #canReuseUpdatedViewHolder(ViewHolder, List)
14484          */
canReuseUpdatedViewHolder(@onNull ViewHolder viewHolder)14485         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
14486             return true;
14487         }
14488 
14489         /**
14490          * When an item is changed, ItemAnimator can decide whether it wants to re-use
14491          * the same ViewHolder for animations or RecyclerView should create a copy of the
14492          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
14493          * <p>
14494          * Note that this method will only be called if the {@link ViewHolder} still has the same
14495          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
14496          * both {@link ViewHolder}s in the
14497          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
14498          *
14499          * @param viewHolder The ViewHolder which represents the changed item's old content.
14500          * @param payloads   A non-null list of merged payloads that were sent with change
14501          *                   notifications. Can be empty if the adapter is invalidated via
14502          *                   {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
14503          *                   payloads will be passed into
14504          *                   {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
14505          *                   method <b>if</b> this method returns <code>true</code>.
14506          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
14507          * RecyclerView should create a new ViewHolder and pass this ViewHolder to the
14508          * ItemAnimator to animate. Default implementation calls
14509          * {@link #canReuseUpdatedViewHolder(ViewHolder)}.
14510          * @see #canReuseUpdatedViewHolder(ViewHolder)
14511          */
canReuseUpdatedViewHolder(@onNull ViewHolder viewHolder, @NonNull List<Object> payloads)14512         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
14513                 @NonNull List<Object> payloads) {
14514             return canReuseUpdatedViewHolder(viewHolder);
14515         }
14516 
14517         /**
14518          * This method should be called by ItemAnimator implementations to notify
14519          * any listeners that all pending and active item animations are finished.
14520          */
dispatchAnimationsFinished()14521         public final void dispatchAnimationsFinished() {
14522             final int count = mFinishedListeners.size();
14523             for (int i = 0; i < count; ++i) {
14524                 mFinishedListeners.get(i).onAnimationsFinished();
14525             }
14526             mFinishedListeners.clear();
14527         }
14528 
14529         /**
14530          * Returns a new {@link ItemHolderInfo} which will be used to store information about the
14531          * ViewHolder. This information will later be passed into <code>animate**</code> methods.
14532          * <p>
14533          * You can override this method if you want to extend {@link ItemHolderInfo} and provide
14534          * your own instances.
14535          *
14536          * @return A new {@link ItemHolderInfo}.
14537          */
obtainHolderInfo()14538         public @NonNull ItemHolderInfo obtainHolderInfo() {
14539             return new ItemHolderInfo();
14540         }
14541 
14542         /**
14543          * The interface to be implemented by listeners to animation events from this
14544          * ItemAnimator. This is used internally and is not intended for developers to
14545          * create directly.
14546          */
14547         interface ItemAnimatorListener {
onAnimationFinished(@onNull ViewHolder item)14548             void onAnimationFinished(@NonNull ViewHolder item);
14549         }
14550 
14551         /**
14552          * This interface is used to inform listeners when all pending or running animations
14553          * in an ItemAnimator are finished. This can be used, for example, to delay an action
14554          * in a data set until currently-running animations are complete.
14555          *
14556          * @see #isRunning(ItemAnimatorFinishedListener)
14557          */
14558         public interface ItemAnimatorFinishedListener {
14559             /**
14560              * Notifies when all pending or running animations in an ItemAnimator are finished.
14561              */
onAnimationsFinished()14562             void onAnimationsFinished();
14563         }
14564 
14565         /**
14566          * A simple data structure that holds information about an item's bounds.
14567          * This information is used in calculating item animations. Default implementation of
14568          * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
14569          * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
14570          * structure. You can extend this class if you would like to keep more information about
14571          * the Views.
14572          * <p>
14573          * If you want to provide your own implementation but still use `super` methods to record
14574          * basic information, you can override {@link #obtainHolderInfo()} to provide your own
14575          * instances.
14576          */
14577         public static class ItemHolderInfo {
14578 
14579             /**
14580              * The left edge of the View (excluding decorations)
14581              */
14582             public int left;
14583 
14584             /**
14585              * The top edge of the View (excluding decorations)
14586              */
14587             public int top;
14588 
14589             /**
14590              * The right edge of the View (excluding decorations)
14591              */
14592             public int right;
14593 
14594             /**
14595              * The bottom edge of the View (excluding decorations)
14596              */
14597             public int bottom;
14598 
14599             /**
14600              * The change flags that were passed to
14601              * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
14602              */
14603             @AdapterChanges
14604             public int changeFlags;
14605 
ItemHolderInfo()14606             public ItemHolderInfo() {
14607             }
14608 
14609             /**
14610              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
14611              * the given ViewHolder. Clears all {@link #changeFlags}.
14612              *
14613              * @param holder The ViewHolder whose bounds should be copied.
14614              * @return This {@link ItemHolderInfo}
14615              */
setFrom(RecyclerView.@onNull ViewHolder holder)14616             public @NonNull ItemHolderInfo setFrom(RecyclerView.@NonNull ViewHolder holder) {
14617                 return setFrom(holder, 0);
14618             }
14619 
14620             /**
14621              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
14622              * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
14623              *
14624              * @param holder The ViewHolder whose bounds should be copied.
14625              * @param flags  The adapter change flags that were passed into
14626              *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
14627              *               List)}.
14628              * @return This {@link ItemHolderInfo}
14629              */
setFrom(RecyclerView.@onNull ViewHolder holder, @AdapterChanges int flags)14630             public @NonNull ItemHolderInfo setFrom(RecyclerView.@NonNull ViewHolder holder,
14631                     @AdapterChanges int flags) {
14632                 final View view = holder.itemView;
14633                 this.left = view.getLeft();
14634                 this.top = view.getTop();
14635                 this.right = view.getRight();
14636                 this.bottom = view.getBottom();
14637                 return this;
14638             }
14639         }
14640     }
14641 
14642     @Override
getChildDrawingOrder(int childCount, int i)14643     protected int getChildDrawingOrder(int childCount, int i) {
14644         if (mChildDrawingOrderCallback == null) {
14645             return super.getChildDrawingOrder(childCount, i);
14646         } else {
14647             return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
14648         }
14649     }
14650 
14651     /**
14652      * A callback interface that can be used to alter the drawing order of RecyclerView children.
14653      * <p>
14654      * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
14655      * that applies to that method also applies to this callback. For example, changing the drawing
14656      * order of two views will not have any effect if their elevation values are different since
14657      * elevation overrides the result of this callback.
14658      */
14659     public interface ChildDrawingOrderCallback {
14660         /**
14661          * Returns the index of the child to draw for this iteration. Override this
14662          * if you want to change the drawing order of children. By default, it
14663          * returns i.
14664          *
14665          * @param childCount The total number of children.
14666          * @param i The current iteration.
14667          * @return The index of the child to draw this iteration.
14668          * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
14669          */
onGetChildDrawingOrder(int childCount, int i)14670         int onGetChildDrawingOrder(int childCount, int i);
14671     }
14672 
getScrollingChildHelper()14673     private NestedScrollingChildHelper getScrollingChildHelper() {
14674         if (mScrollingChildHelper == null) {
14675             mScrollingChildHelper = new NestedScrollingChildHelper(this);
14676         }
14677         return mScrollingChildHelper;
14678     }
14679 
getScrollFeedbackProvider()14680     private ScrollFeedbackProviderCompat getScrollFeedbackProvider() {
14681         if (mScrollFeedbackProvider == null) {
14682             mScrollFeedbackProvider = ScrollFeedbackProviderCompat.createProvider(this);
14683         }
14684         return mScrollFeedbackProvider;
14685     }
14686 
14687     @RequiresApi(35)
14688     private static final class Api35Impl {
14689         @DoNotInline
setFrameContentVelocity(View view, float velocity)14690         public static void setFrameContentVelocity(View view, float velocity) {
14691             try {
14692                 view.setFrameContentVelocity(velocity);
14693             } catch (LinkageError e) {
14694                 // The setFrameContentVelocity method is unavailable on this device.
14695             }
14696         }
14697     }
14698 }
14699