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_GROUP;
21 import static androidx.core.view.ViewCompat.TYPE_NON_TOUCH;
22 import static androidx.core.view.ViewCompat.TYPE_TOUCH;
23 
24 import android.annotation.SuppressLint;
25 import android.content.Context;
26 import android.content.res.Resources;
27 import android.content.res.TypedArray;
28 import android.database.Observable;
29 import android.graphics.Canvas;
30 import android.graphics.Matrix;
31 import android.graphics.PointF;
32 import android.graphics.Rect;
33 import android.graphics.RectF;
34 import android.graphics.drawable.Drawable;
35 import android.graphics.drawable.StateListDrawable;
36 import android.os.Build;
37 import android.os.Bundle;
38 import android.os.Parcel;
39 import android.os.Parcelable;
40 import android.os.SystemClock;
41 import android.util.AttributeSet;
42 import android.util.Log;
43 import android.util.SparseArray;
44 import android.view.Display;
45 import android.view.FocusFinder;
46 import android.view.InputDevice;
47 import android.view.MotionEvent;
48 import android.view.VelocityTracker;
49 import android.view.View;
50 import android.view.ViewConfiguration;
51 import android.view.ViewGroup;
52 import android.view.ViewParent;
53 import android.view.accessibility.AccessibilityEvent;
54 import android.view.accessibility.AccessibilityManager;
55 import android.view.animation.Interpolator;
56 import android.widget.EdgeEffect;
57 import android.widget.LinearLayout;
58 import android.widget.OverScroller;
59 
60 import androidx.annotation.CallSuper;
61 import androidx.annotation.IntDef;
62 import androidx.annotation.NonNull;
63 import androidx.annotation.Nullable;
64 import androidx.annotation.Px;
65 import androidx.annotation.RestrictTo;
66 import androidx.annotation.VisibleForTesting;
67 import androidx.core.os.TraceCompat;
68 import androidx.core.util.Preconditions;
69 import androidx.core.view.InputDeviceCompat;
70 import androidx.core.view.MotionEventCompat;
71 import androidx.core.view.NestedScrollingChild2;
72 import androidx.core.view.NestedScrollingChildHelper;
73 import androidx.core.view.ScrollingView;
74 import androidx.core.view.ViewCompat;
75 import androidx.core.view.ViewConfigurationCompat;
76 import androidx.core.view.accessibility.AccessibilityEventCompat;
77 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
78 import androidx.core.widget.EdgeEffectCompat;
79 import androidx.customview.view.AbsSavedState;
80 import androidx.recyclerview.R;
81 import androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
82 import androidx.viewpager.widget.ViewPager;
83 
84 import java.lang.annotation.Retention;
85 import java.lang.annotation.RetentionPolicy;
86 import java.lang.ref.WeakReference;
87 import java.lang.reflect.Constructor;
88 import java.lang.reflect.InvocationTargetException;
89 import java.util.ArrayList;
90 import java.util.Collections;
91 import java.util.List;
92 
93 
94 /**
95  * A flexible view for providing a limited window into a large data set.
96  *
97  * <h3>Glossary of terms:</h3>
98  *
99  * <ul>
100  *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
101  *     that represent items in a data set.</li>
102  *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
103  *     <li><em>Index:</em> The index of an attached child view as used in a call to
104  *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
105  *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
106  *     to a <em>position</em> within the adapter.</li>
107  *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
108  *     position may be placed in a cache for later reuse to display the same type of data again
109  *     later. This can drastically improve performance by skipping initial layout inflation
110  *     or construction.</li>
111  *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
112  *     state during layout. Scrap views may be reused without becoming fully detached
113  *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
114  *     by the adapter if the view was considered <em>dirty</em>.</li>
115  *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
116  *     being displayed.</li>
117  * </ul>
118  *
119  * <h4>Positions in RecyclerView:</h4>
120  * <p>
121  * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
122  * {@link LayoutManager} to be able to detect data set changes in batches during a layout
123  * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
124  * It also helps with performance because all view bindings happen at the same time and unnecessary
125  * bindings are avoided.
126  * <p>
127  * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
128  * <ul>
129  *     <li>layout position: Position of an item in the latest layout calculation. This is the
130  *     position from the LayoutManager's perspective.</li>
131  *     <li>adapter position: Position of an item in the adapter. This is the position from
132  *     the Adapter's perspective.</li>
133  * </ul>
134  * <p>
135  * These two positions are the same except the time between dispatching <code>adapter.notify*
136  * </code> events and calculating the updated layout.
137  * <p>
138  * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
139  * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
140  * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
141  * last layout calculation. You can rely on these positions to be consistent with what user is
142  * currently seeing on the screen. For example, if you have a list of items on the screen and user
143  * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
144  * is seeing.
145  * <p>
146  * The other set of position related methods are in the form of
147  * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
148  * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
149  * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
150  * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
151  * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
152  * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
153  * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
154  * <code>null</code> results from these methods.
155  * <p>
156  * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
157  * writing an {@link Adapter}, you probably want to use adapter positions.
158  *
159  * @attr ref androidx.recyclerview.R.styleable#RecyclerView_layoutManager
160  */
161 public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild2 {
162 
163     static final String TAG = "RecyclerView";
164 
165     static final boolean DEBUG = false;
166 
167     static final boolean VERBOSE_TRACING = false;
168 
169     private static final int[]  NESTED_SCROLLING_ATTRS =
170             {16843830 /* android.R.attr.nestedScrollingEnabled */};
171 
172     private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
173 
174     /**
175      * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
176      * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
177      * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
178      * recursively traverses itemView and invalidates display list for each ViewGroup that matches
179      * this criteria.
180      */
181     static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
182             || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
183     /**
184      * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
185      * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
186      * 0 when mode is unspecified.
187      */
188     static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
189 
190     static final boolean POST_UPDATES_ON_ANIMATION = Build.VERSION.SDK_INT >= 16;
191 
192     /**
193      * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
194      * RenderThread but before the next frame begins. We schedule prefetch work in this window.
195      */
196     private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
197 
198     /**
199      * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
200      * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
201      */
202     private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
203 
204     /**
205      * on API 15-, a focused child can still be considered a focused child of RV even after
206      * it's being removed or its focusable flag is set to false. This is because when this focused
207      * child is detached, the reference to this child is not removed in clearFocus. API 16 and above
208      * properly handle this case by calling ensureInputFocusOnFirstFocusable or rootViewRequestFocus
209      * to request focus on a new child, which will clear the focus on the old (detached) child as a
210      * side-effect.
211      */
212     private static final boolean IGNORE_DETACHED_FOCUSED_CHILD = Build.VERSION.SDK_INT <= 15;
213 
214     static final boolean DISPATCH_TEMP_DETACH = false;
215 
216     /** @hide */
217     @RestrictTo(LIBRARY_GROUP)
218     @IntDef({HORIZONTAL, VERTICAL})
219     @Retention(RetentionPolicy.SOURCE)
220     public @interface Orientation {}
221 
222     public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
223     public static final int VERTICAL = LinearLayout.VERTICAL;
224 
225     static final int DEFAULT_ORIENTATION = VERTICAL;
226     public static final int NO_POSITION = -1;
227     public static final long NO_ID = -1;
228     public static final int INVALID_TYPE = -1;
229 
230     /**
231      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
232      * that the RecyclerView should use the standard touch slop for smooth,
233      * continuous scrolling.
234      */
235     public static final int TOUCH_SLOP_DEFAULT = 0;
236 
237     /**
238      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
239      * that the RecyclerView should use the standard touch slop for scrolling
240      * widgets that snap to a page or other coarse-grained barrier.
241      */
242     public static final int TOUCH_SLOP_PAGING = 1;
243 
244     static final int MAX_SCROLL_DURATION = 2000;
245 
246     /**
247      * RecyclerView is calculating a scroll.
248      * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
249      * it. Try to avoid using EditText, focusable views or handle them with care.
250      */
251     static final String TRACE_SCROLL_TAG = "RV Scroll";
252 
253     /**
254      * OnLayout has been called by the View system.
255      * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
256      * update themselves directly. This will cause a full re-layout but when it happens via the
257      * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
258      */
259     private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
260 
261     /**
262      * NotifyDataSetChanged or equal has been called.
263      * If this is taking a long time, try sending granular notify adapter changes instead of just
264      * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
265      * might help.
266      */
267     private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
268 
269     /**
270      * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
271      * If this is taking a long time, you may have dispatched too many Adapter updates causing too
272      * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
273      * methods.
274      */
275     private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
276 
277     /**
278      * RecyclerView is rebinding a View.
279      * If this is taking a lot of time, consider optimizing your layout or make sure you are not
280      * doing extra operations in onBindViewHolder call.
281      */
282     static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
283 
284     /**
285      * RecyclerView is attempting to pre-populate off screen views.
286      */
287     static final String TRACE_PREFETCH_TAG = "RV Prefetch";
288 
289     /**
290      * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
291      * RecyclerView.
292      */
293     static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
294 
295     /**
296      * RecyclerView is creating a new View.
297      * If too many of these present in Systrace:
298      * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
299      * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
300      * > Adapter#onFailedToRecycleView(ViewHolder)})
301      *
302      * - There might be too many item view types.
303      * > Try merging them
304      *
305      * - There might be too many itemChange animations and not enough space in RecyclerPool.
306      * >Try increasing your pool size and item cache size.
307      */
308     static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
309     private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
310             new Class[]{Context.class, AttributeSet.class, int.class, int.class};
311 
312     private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
313 
314     final Recycler mRecycler = new Recycler();
315 
316     private SavedState mPendingSavedState;
317 
318     /**
319      * Handles adapter updates
320      */
321     AdapterHelper mAdapterHelper;
322 
323     /**
324      * Handles abstraction between LayoutManager children and RecyclerView children
325      */
326     ChildHelper mChildHelper;
327 
328     /**
329      * Keeps data about views to be used for animations
330      */
331     final ViewInfoStore mViewInfoStore = new ViewInfoStore();
332 
333     /**
334      * Prior to L, there is no way to query this variable which is why we override the setter and
335      * track it here.
336      */
337     boolean mClipToPadding;
338 
339     /**
340      * Note: this Runnable is only ever posted if:
341      * 1) We've been through first layout
342      * 2) We know we have a fixed size (mHasFixedSize)
343      * 3) We're attached
344      */
345     final Runnable mUpdateChildViewsRunnable = new Runnable() {
346         @Override
347         public void run() {
348             if (!mFirstLayoutComplete || isLayoutRequested()) {
349                 // a layout request will happen, we should not do layout here.
350                 return;
351             }
352             if (!mIsAttached) {
353                 requestLayout();
354                 // if we are not attached yet, mark us as requiring layout and skip
355                 return;
356             }
357             if (mLayoutFrozen) {
358                 mLayoutWasDefered = true;
359                 return; //we'll process updates when ice age ends.
360             }
361             consumePendingUpdateOperations();
362         }
363     };
364 
365     final Rect mTempRect = new Rect();
366     private final Rect mTempRect2 = new Rect();
367     final RectF mTempRectF = new RectF();
368     Adapter mAdapter;
369     @VisibleForTesting LayoutManager mLayout;
370     RecyclerListener mRecyclerListener;
371     final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
372     private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
373             new ArrayList<>();
374     private OnItemTouchListener mActiveOnItemTouchListener;
375     boolean mIsAttached;
376     boolean mHasFixedSize;
377     boolean mEnableFastScroller;
378     @VisibleForTesting boolean mFirstLayoutComplete;
379 
380     /**
381      * The current depth of nested calls to {@link #startInterceptRequestLayout()} (number of
382      * calls to {@link #startInterceptRequestLayout()} - number of calls to
383      * {@link #stopInterceptRequestLayout(boolean)} .  This is used to signal whether we
384      * should defer layout operations caused by layout requests from children of
385      * {@link RecyclerView}.
386      */
387     private int mInterceptRequestLayoutDepth = 0;
388 
389     /**
390      * True if a call to requestLayout was intercepted and prevented from executing like normal and
391      * we plan on continuing with normal execution later.
392      */
393     boolean mLayoutWasDefered;
394 
395     boolean mLayoutFrozen;
396     private boolean mIgnoreMotionEventTillDown;
397 
398     // binary OR of change events that were eaten during a layout or scroll.
399     private int mEatenAccessibilityChangeFlags;
400     boolean mAdapterUpdateDuringMeasure;
401 
402     private final AccessibilityManager mAccessibilityManager;
403     private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
404 
405     /**
406      * True after an event occurs that signals that the entire data set has changed. In that case,
407      * we cannot run any animations since we don't know what happened until layout.
408      *
409      * Attached items are invalid until next layout, at which point layout will animate/replace
410      * items as necessary, building up content from the (effectively) new adapter from scratch.
411      *
412      * Cached items must be discarded when setting this to true, so that the cache may be freely
413      * used by prefetching until the next layout occurs.
414      *
415      * @see #processDataSetCompletelyChanged(boolean)
416      */
417     boolean mDataSetHasChangedAfterLayout = false;
418 
419     /**
420      * True after the data set has completely changed and
421      * {@link LayoutManager#onItemsChanged(RecyclerView)} should be called during the subsequent
422      * measure/layout.
423      *
424      * @see #processDataSetCompletelyChanged(boolean)
425      */
426     boolean mDispatchItemsChangedEvent = false;
427 
428     /**
429      * This variable is incremented during a dispatchLayout and/or scroll.
430      * Some methods should not be called during these periods (e.g. adapter data change).
431      * Doing so will create hard to find bugs so we better check it and throw an exception.
432      *
433      * @see #assertInLayoutOrScroll(String)
434      * @see #assertNotInLayoutOrScroll(String)
435      */
436     private int mLayoutOrScrollCounter = 0;
437 
438     /**
439      * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
440      * (for API compatibility).
441      * <p>
442      * It is a bad practice for a developer to update the data in a scroll callback since it is
443      * potentially called during a layout.
444      */
445     private int mDispatchScrollCounter = 0;
446 
447     @NonNull
448     private EdgeEffectFactory mEdgeEffectFactory = new EdgeEffectFactory();
449     private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
450 
451     ItemAnimator mItemAnimator = new DefaultItemAnimator();
452 
453     private static final int INVALID_POINTER = -1;
454 
455     /**
456      * The RecyclerView is not currently scrolling.
457      * @see #getScrollState()
458      */
459     public static final int SCROLL_STATE_IDLE = 0;
460 
461     /**
462      * The RecyclerView is currently being dragged by outside input such as user touch input.
463      * @see #getScrollState()
464      */
465     public static final int SCROLL_STATE_DRAGGING = 1;
466 
467     /**
468      * The RecyclerView is currently animating to a final position while not under
469      * outside control.
470      * @see #getScrollState()
471      */
472     public static final int SCROLL_STATE_SETTLING = 2;
473 
474     static final long FOREVER_NS = Long.MAX_VALUE;
475 
476     // Touch/scrolling handling
477 
478     private int mScrollState = SCROLL_STATE_IDLE;
479     private int mScrollPointerId = INVALID_POINTER;
480     private VelocityTracker mVelocityTracker;
481     private int mInitialTouchX;
482     private int mInitialTouchY;
483     private int mLastTouchX;
484     private int mLastTouchY;
485     private int mTouchSlop;
486     private OnFlingListener mOnFlingListener;
487     private final int mMinFlingVelocity;
488     private final int mMaxFlingVelocity;
489 
490     // This value is used when handling rotary encoder generic motion events.
491     private float mScaledHorizontalScrollFactor = Float.MIN_VALUE;
492     private float mScaledVerticalScrollFactor = Float.MIN_VALUE;
493 
494     private boolean mPreserveFocusAfterLayout = true;
495 
496     final ViewFlinger mViewFlinger = new ViewFlinger();
497 
498     GapWorker mGapWorker;
499     GapWorker.LayoutPrefetchRegistryImpl mPrefetchRegistry =
500             ALLOW_THREAD_GAP_WORK ? new GapWorker.LayoutPrefetchRegistryImpl() : null;
501 
502     final State mState = new State();
503 
504     private OnScrollListener mScrollListener;
505     private List<OnScrollListener> mScrollListeners;
506 
507     // For use in item animations
508     boolean mItemsAddedOrRemoved = false;
509     boolean mItemsChanged = false;
510     private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
511             new ItemAnimatorRestoreListener();
512     boolean mPostedAnimatorRunner = false;
513     RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
514     private ChildDrawingOrderCallback mChildDrawingOrderCallback;
515 
516     // simple array to keep min and max child position during a layout calculation
517     // preserved not to create a new one in each layout pass
518     private final int[] mMinMaxLayoutPositions = new int[2];
519 
520     private NestedScrollingChildHelper mScrollingChildHelper;
521     private final int[] mScrollOffset = new int[2];
522     private final int[] mScrollConsumed = new int[2];
523     private final int[] mNestedOffsets = new int[2];
524 
525     /**
526      * Reusable int array for use in calls to {@link #scrollStep(int, int, int[])} so that the
527      * method may mutate it to "return" 2 ints.
528      */
529     private final int[] mScrollStepConsumed = new int[2];
530 
531     /**
532      * These are views that had their a11y importance changed during a layout. We defer these events
533      * until the end of the layout because a11y service may make sync calls back to the RV while
534      * the View's state is undefined.
535      */
536     @VisibleForTesting
537     final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList<>();
538 
539     private Runnable mItemAnimatorRunner = new Runnable() {
540         @Override
541         public void run() {
542             if (mItemAnimator != null) {
543                 mItemAnimator.runPendingAnimations();
544             }
545             mPostedAnimatorRunner = false;
546         }
547     };
548 
549     static final Interpolator sQuinticInterpolator = new Interpolator() {
550         @Override
551         public float getInterpolation(float t) {
552             t -= 1.0f;
553             return t * t * t * t * t + 1.0f;
554         }
555     };
556 
557     /**
558      * The callback to convert view info diffs into animations.
559      */
560     private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
561             new ViewInfoStore.ProcessCallback() {
562                 @Override
563                 public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
564                         @Nullable ItemHolderInfo postInfo) {
565                     mRecycler.unscrapView(viewHolder);
566                     animateDisappearance(viewHolder, info, postInfo);
567                 }
568                 @Override
569                 public void processAppeared(ViewHolder viewHolder,
570                         ItemHolderInfo preInfo, ItemHolderInfo info) {
571                     animateAppearance(viewHolder, preInfo, info);
572                 }
573 
574                 @Override
575                 public void processPersistent(ViewHolder viewHolder,
576                         @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
577                     viewHolder.setIsRecyclable(false);
578                     if (mDataSetHasChangedAfterLayout) {
579                         // since it was rebound, use change instead as we'll be mapping them from
580                         // stable ids. If stable ids were false, we would not be running any
581                         // animations
582                         if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo,
583                                 postInfo)) {
584                             postAnimationRunner();
585                         }
586                     } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
587                         postAnimationRunner();
588                     }
589                 }
590                 @Override
591                 public void unused(ViewHolder viewHolder) {
592                     mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
593                 }
594             };
595 
RecyclerView(@onNull Context context)596     public RecyclerView(@NonNull Context context) {
597         this(context, null);
598     }
599 
RecyclerView(@onNull Context context, @Nullable AttributeSet attrs)600     public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
601         this(context, attrs, 0);
602     }
603 
RecyclerView(@onNull Context context, @Nullable AttributeSet attrs, int defStyle)604     public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
605         super(context, attrs, defStyle);
606         if (attrs != null) {
607             TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
608             mClipToPadding = a.getBoolean(0, true);
609             a.recycle();
610         } else {
611             mClipToPadding = true;
612         }
613         setScrollContainer(true);
614         setFocusableInTouchMode(true);
615 
616         final ViewConfiguration vc = ViewConfiguration.get(context);
617         mTouchSlop = vc.getScaledTouchSlop();
618         mScaledHorizontalScrollFactor =
619                 ViewConfigurationCompat.getScaledHorizontalScrollFactor(vc, context);
620         mScaledVerticalScrollFactor =
621                 ViewConfigurationCompat.getScaledVerticalScrollFactor(vc, context);
622         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
623         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
624         setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
625 
626         mItemAnimator.setListener(mItemAnimatorListener);
627         initAdapterManager();
628         initChildrenHelper();
629         initAutofill();
630         // If not explicitly specified this view is important for accessibility.
631         if (ViewCompat.getImportantForAccessibility(this)
632                 == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
633             ViewCompat.setImportantForAccessibility(this,
634                     ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
635         }
636         mAccessibilityManager = (AccessibilityManager) getContext()
637                 .getSystemService(Context.ACCESSIBILITY_SERVICE);
638         setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
639         // Create the layoutManager if specified.
640 
641         boolean nestedScrollingEnabled = true;
642 
643         if (attrs != null) {
644             int defStyleRes = 0;
645             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
646                     defStyle, defStyleRes);
647             String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
648             int descendantFocusability = a.getInt(
649                     R.styleable.RecyclerView_android_descendantFocusability, -1);
650             if (descendantFocusability == -1) {
651                 setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
652             }
653             mEnableFastScroller = a.getBoolean(R.styleable.RecyclerView_fastScrollEnabled, false);
654             if (mEnableFastScroller) {
655                 StateListDrawable verticalThumbDrawable = (StateListDrawable) a
656                         .getDrawable(R.styleable.RecyclerView_fastScrollVerticalThumbDrawable);
657                 Drawable verticalTrackDrawable = a
658                         .getDrawable(R.styleable.RecyclerView_fastScrollVerticalTrackDrawable);
659                 StateListDrawable horizontalThumbDrawable = (StateListDrawable) a
660                         .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalThumbDrawable);
661                 Drawable horizontalTrackDrawable = a
662                         .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalTrackDrawable);
663                 initFastScroller(verticalThumbDrawable, verticalTrackDrawable,
664                         horizontalThumbDrawable, horizontalTrackDrawable);
665             }
666             a.recycle();
667             createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
668 
669             if (Build.VERSION.SDK_INT >= 21) {
670                 a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
671                         defStyle, defStyleRes);
672                 nestedScrollingEnabled = a.getBoolean(0, true);
673                 a.recycle();
674             }
675         } else {
676             setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
677         }
678 
679         // Re-set whether nested scrolling is enabled so that it is set on all API levels
680         setNestedScrollingEnabled(nestedScrollingEnabled);
681     }
682 
683     /**
684      * Label appended to all public exception strings, used to help find which RV in an app is
685      * hitting an exception.
686      */
exceptionLabel()687     String exceptionLabel() {
688         return " " + super.toString()
689                 + ", adapter:" + mAdapter
690                 + ", layout:" + mLayout
691                 + ", context:" + getContext();
692     }
693 
694     /**
695      * If not explicitly specified, this view and its children don't support autofill.
696      * <p>
697      * This is done because autofill's means of uniquely identifying views doesn't work out of the
698      * box with View recycling.
699      */
700     @SuppressLint("InlinedApi")
initAutofill()701     private void initAutofill() {
702         if (ViewCompat.getImportantForAutofill(this) == View.IMPORTANT_FOR_AUTOFILL_AUTO) {
703             ViewCompat.setImportantForAutofill(this,
704                     View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
705         }
706     }
707 
708     /**
709      * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
710      * @return An instance of AccessibilityDelegateCompat used by RecyclerView
711      */
712     @Nullable
getCompatAccessibilityDelegate()713     public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
714         return mAccessibilityDelegate;
715     }
716 
717     /**
718      * Sets the accessibility delegate compatibility implementation used by RecyclerView.
719      * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
720      */
setAccessibilityDelegateCompat( @ullable RecyclerViewAccessibilityDelegate accessibilityDelegate)721     public void setAccessibilityDelegateCompat(
722             @Nullable RecyclerViewAccessibilityDelegate accessibilityDelegate) {
723         mAccessibilityDelegate = accessibilityDelegate;
724         ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
725     }
726 
727     /**
728      * Instantiate and set a LayoutManager, if specified in the attributes.
729      */
createLayoutManager(Context context, String className, AttributeSet attrs, int defStyleAttr, int defStyleRes)730     private void createLayoutManager(Context context, String className, AttributeSet attrs,
731             int defStyleAttr, int defStyleRes) {
732         if (className != null) {
733             className = className.trim();
734             if (!className.isEmpty()) {
735                 className = getFullClassName(context, className);
736                 try {
737                     ClassLoader classLoader;
738                     if (isInEditMode()) {
739                         // Stupid layoutlib cannot handle simple class loaders.
740                         classLoader = this.getClass().getClassLoader();
741                     } else {
742                         classLoader = context.getClassLoader();
743                     }
744                     Class<? extends LayoutManager> layoutManagerClass =
745                             classLoader.loadClass(className).asSubclass(LayoutManager.class);
746                     Constructor<? extends LayoutManager> constructor;
747                     Object[] constructorArgs = null;
748                     try {
749                         constructor = layoutManagerClass
750                                 .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
751                         constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
752                     } catch (NoSuchMethodException e) {
753                         try {
754                             constructor = layoutManagerClass.getConstructor();
755                         } catch (NoSuchMethodException e1) {
756                             e1.initCause(e);
757                             throw new IllegalStateException(attrs.getPositionDescription()
758                                     + ": Error creating LayoutManager " + className, e1);
759                         }
760                     }
761                     constructor.setAccessible(true);
762                     setLayoutManager(constructor.newInstance(constructorArgs));
763                 } catch (ClassNotFoundException e) {
764                     throw new IllegalStateException(attrs.getPositionDescription()
765                             + ": Unable to find LayoutManager " + className, e);
766                 } catch (InvocationTargetException e) {
767                     throw new IllegalStateException(attrs.getPositionDescription()
768                             + ": Could not instantiate the LayoutManager: " + className, e);
769                 } catch (InstantiationException e) {
770                     throw new IllegalStateException(attrs.getPositionDescription()
771                             + ": Could not instantiate the LayoutManager: " + className, e);
772                 } catch (IllegalAccessException e) {
773                     throw new IllegalStateException(attrs.getPositionDescription()
774                             + ": Cannot access non-public constructor " + className, e);
775                 } catch (ClassCastException e) {
776                     throw new IllegalStateException(attrs.getPositionDescription()
777                             + ": Class is not a LayoutManager " + className, e);
778                 }
779             }
780         }
781     }
782 
getFullClassName(Context context, String className)783     private String getFullClassName(Context context, String className) {
784         if (className.charAt(0) == '.') {
785             return context.getPackageName() + className;
786         }
787         if (className.contains(".")) {
788             return className;
789         }
790         return RecyclerView.class.getPackage().getName() + '.' + className;
791     }
792 
initChildrenHelper()793     private void initChildrenHelper() {
794         mChildHelper = new ChildHelper(new ChildHelper.Callback() {
795             @Override
796             public int getChildCount() {
797                 return RecyclerView.this.getChildCount();
798             }
799 
800             @Override
801             public void addView(View child, int index) {
802                 if (VERBOSE_TRACING) {
803                     TraceCompat.beginSection("RV addView");
804                 }
805                 RecyclerView.this.addView(child, index);
806                 if (VERBOSE_TRACING) {
807                     TraceCompat.endSection();
808                 }
809                 dispatchChildAttached(child);
810             }
811 
812             @Override
813             public int indexOfChild(View view) {
814                 return RecyclerView.this.indexOfChild(view);
815             }
816 
817             @Override
818             public void removeViewAt(int index) {
819                 final View child = RecyclerView.this.getChildAt(index);
820                 if (child != null) {
821                     dispatchChildDetached(child);
822 
823                     // Clear any android.view.animation.Animation that may prevent the item from
824                     // detaching when being removed. If a child is re-added before the
825                     // lazy detach occurs, it will receive invalid attach/detach sequencing.
826                     child.clearAnimation();
827                 }
828                 if (VERBOSE_TRACING) {
829                     TraceCompat.beginSection("RV removeViewAt");
830                 }
831                 RecyclerView.this.removeViewAt(index);
832                 if (VERBOSE_TRACING) {
833                     TraceCompat.endSection();
834                 }
835             }
836 
837             @Override
838             public View getChildAt(int offset) {
839                 return RecyclerView.this.getChildAt(offset);
840             }
841 
842             @Override
843             public void removeAllViews() {
844                 final int count = getChildCount();
845                 for (int i = 0; i < count; i++) {
846                     View child = getChildAt(i);
847                     dispatchChildDetached(child);
848 
849                     // Clear any android.view.animation.Animation that may prevent the item from
850                     // detaching when being removed. If a child is re-added before the
851                     // lazy detach occurs, it will receive invalid attach/detach sequencing.
852                     child.clearAnimation();
853                 }
854                 RecyclerView.this.removeAllViews();
855             }
856 
857             @Override
858             public ViewHolder getChildViewHolder(View view) {
859                 return getChildViewHolderInt(view);
860             }
861 
862             @Override
863             public void attachViewToParent(View child, int index,
864                     ViewGroup.LayoutParams layoutParams) {
865                 final ViewHolder vh = getChildViewHolderInt(child);
866                 if (vh != null) {
867                     if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
868                         throw new IllegalArgumentException("Called attach on a child which is not"
869                                 + " detached: " + vh + exceptionLabel());
870                     }
871                     if (DEBUG) {
872                         Log.d(TAG, "reAttach " + vh);
873                     }
874                     vh.clearTmpDetachFlag();
875                 }
876                 RecyclerView.this.attachViewToParent(child, index, layoutParams);
877             }
878 
879             @Override
880             public void detachViewFromParent(int offset) {
881                 final View view = getChildAt(offset);
882                 if (view != null) {
883                     final ViewHolder vh = getChildViewHolderInt(view);
884                     if (vh != null) {
885                         if (vh.isTmpDetached() && !vh.shouldIgnore()) {
886                             throw new IllegalArgumentException("called detach on an already"
887                                     + " detached child " + vh + exceptionLabel());
888                         }
889                         if (DEBUG) {
890                             Log.d(TAG, "tmpDetach " + vh);
891                         }
892                         vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
893                     }
894                 }
895                 RecyclerView.this.detachViewFromParent(offset);
896             }
897 
898             @Override
899             public void onEnteredHiddenState(View child) {
900                 final ViewHolder vh = getChildViewHolderInt(child);
901                 if (vh != null) {
902                     vh.onEnteredHiddenState(RecyclerView.this);
903                 }
904             }
905 
906             @Override
907             public void onLeftHiddenState(View child) {
908                 final ViewHolder vh = getChildViewHolderInt(child);
909                 if (vh != null) {
910                     vh.onLeftHiddenState(RecyclerView.this);
911                 }
912             }
913         });
914     }
915 
initAdapterManager()916     void initAdapterManager() {
917         mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
918             @Override
919             public ViewHolder findViewHolder(int position) {
920                 final ViewHolder vh = findViewHolderForPosition(position, true);
921                 if (vh == null) {
922                     return null;
923                 }
924                 // ensure it is not hidden because for adapter helper, the only thing matter is that
925                 // LM thinks view is a child.
926                 if (mChildHelper.isHidden(vh.itemView)) {
927                     if (DEBUG) {
928                         Log.d(TAG, "assuming view holder cannot be find because it is hidden");
929                     }
930                     return null;
931                 }
932                 return vh;
933             }
934 
935             @Override
936             public void offsetPositionsForRemovingInvisible(int start, int count) {
937                 offsetPositionRecordsForRemove(start, count, true);
938                 mItemsAddedOrRemoved = true;
939                 mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
940             }
941 
942             @Override
943             public void offsetPositionsForRemovingLaidOutOrNewView(
944                     int positionStart, int itemCount) {
945                 offsetPositionRecordsForRemove(positionStart, itemCount, false);
946                 mItemsAddedOrRemoved = true;
947             }
948 
949 
950             @Override
951             public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
952                 viewRangeUpdate(positionStart, itemCount, payload);
953                 mItemsChanged = true;
954             }
955 
956             @Override
957             public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
958                 dispatchUpdate(op);
959             }
960 
961             void dispatchUpdate(AdapterHelper.UpdateOp op) {
962                 switch (op.cmd) {
963                     case AdapterHelper.UpdateOp.ADD:
964                         mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
965                         break;
966                     case AdapterHelper.UpdateOp.REMOVE:
967                         mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
968                         break;
969                     case AdapterHelper.UpdateOp.UPDATE:
970                         mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
971                                 op.payload);
972                         break;
973                     case AdapterHelper.UpdateOp.MOVE:
974                         mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
975                         break;
976                 }
977             }
978 
979             @Override
980             public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
981                 dispatchUpdate(op);
982             }
983 
984             @Override
985             public void offsetPositionsForAdd(int positionStart, int itemCount) {
986                 offsetPositionRecordsForInsert(positionStart, itemCount);
987                 mItemsAddedOrRemoved = true;
988             }
989 
990             @Override
991             public void offsetPositionsForMove(int from, int to) {
992                 offsetPositionRecordsForMove(from, to);
993                 // should we create mItemsMoved ?
994                 mItemsAddedOrRemoved = true;
995             }
996         });
997     }
998 
999     /**
1000      * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
1001      * size is not affected by the adapter contents. RecyclerView can still change its size based
1002      * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
1003      * size of its children or contents of its adapter (except the number of items in the adapter).
1004      * <p>
1005      * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
1006      * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
1007      *
1008      * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
1009      */
setHasFixedSize(boolean hasFixedSize)1010     public void setHasFixedSize(boolean hasFixedSize) {
1011         mHasFixedSize = hasFixedSize;
1012     }
1013 
1014     /**
1015      * @return true if the app has specified that changes in adapter content cannot change
1016      * the size of the RecyclerView itself.
1017      */
hasFixedSize()1018     public boolean hasFixedSize() {
1019         return mHasFixedSize;
1020     }
1021 
1022     @Override
setClipToPadding(boolean clipToPadding)1023     public void setClipToPadding(boolean clipToPadding) {
1024         if (clipToPadding != mClipToPadding) {
1025             invalidateGlows();
1026         }
1027         mClipToPadding = clipToPadding;
1028         super.setClipToPadding(clipToPadding);
1029         if (mFirstLayoutComplete) {
1030             requestLayout();
1031         }
1032     }
1033 
1034     /**
1035      * Returns whether this RecyclerView will clip its children to its padding, and resize (but
1036      * not clip) any EdgeEffect to the padded region, if padding is present.
1037      * <p>
1038      * By default, children are clipped to the padding of their parent
1039      * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
1040      *
1041      * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
1042      *         clip) any EdgeEffect to the padded region, false otherwise.
1043      *
1044      * @attr name android:clipToPadding
1045      */
1046     @Override
getClipToPadding()1047     public boolean getClipToPadding() {
1048         return mClipToPadding;
1049     }
1050 
1051     /**
1052      * Configure the scrolling touch slop for a specific use case.
1053      *
1054      * Set up the RecyclerView's scrolling motion threshold based on common usages.
1055      * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
1056      *
1057      * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
1058      *                     the intended usage of this RecyclerView
1059      */
setScrollingTouchSlop(int slopConstant)1060     public void setScrollingTouchSlop(int slopConstant) {
1061         final ViewConfiguration vc = ViewConfiguration.get(getContext());
1062         switch (slopConstant) {
1063             default:
1064                 Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
1065                         + slopConstant + "; using default value");
1066                 // fall-through
1067             case TOUCH_SLOP_DEFAULT:
1068                 mTouchSlop = vc.getScaledTouchSlop();
1069                 break;
1070 
1071             case TOUCH_SLOP_PAGING:
1072                 mTouchSlop = vc.getScaledPagingTouchSlop();
1073                 break;
1074         }
1075     }
1076 
1077     /**
1078      * Swaps the current adapter with the provided one. It is similar to
1079      * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
1080      * {@link ViewHolder} and does not clear the RecycledViewPool.
1081      * <p>
1082      * Note that it still calls onAdapterChanged callbacks.
1083      *
1084      * @param adapter The new adapter to set, or null to set no adapter.
1085      * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
1086      *                                      Views. If adapters have stable ids and/or you want to
1087      *                                      animate the disappearing views, you may prefer to set
1088      *                                      this to false.
1089      * @see #setAdapter(Adapter)
1090      */
swapAdapter(@ullable Adapter adapter, boolean removeAndRecycleExistingViews)1091     public void swapAdapter(@Nullable Adapter adapter, boolean removeAndRecycleExistingViews) {
1092         // bail out if layout is frozen
1093         setLayoutFrozen(false);
1094         setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
1095         processDataSetCompletelyChanged(true);
1096         requestLayout();
1097     }
1098     /**
1099      * Set a new adapter to provide child views on demand.
1100      * <p>
1101      * When adapter is changed, all existing views are recycled back to the pool. If the pool has
1102      * only one adapter, it will be cleared.
1103      *
1104      * @param adapter The new adapter to set, or null to set no adapter.
1105      * @see #swapAdapter(Adapter, boolean)
1106      */
setAdapter(@ullable Adapter adapter)1107     public void setAdapter(@Nullable Adapter adapter) {
1108         // bail out if layout is frozen
1109         setLayoutFrozen(false);
1110         setAdapterInternal(adapter, false, true);
1111         processDataSetCompletelyChanged(false);
1112         requestLayout();
1113     }
1114 
1115     /**
1116      * Removes and recycles all views - both those currently attached, and those in the Recycler.
1117      */
removeAndRecycleViews()1118     void removeAndRecycleViews() {
1119         // end all running animations
1120         if (mItemAnimator != null) {
1121             mItemAnimator.endAnimations();
1122         }
1123         // Since animations are ended, mLayout.children should be equal to
1124         // recyclerView.children. This may not be true if item animator's end does not work as
1125         // expected. (e.g. not release children instantly). It is safer to use mLayout's child
1126         // count.
1127         if (mLayout != null) {
1128             mLayout.removeAndRecycleAllViews(mRecycler);
1129             mLayout.removeAndRecycleScrapInt(mRecycler);
1130         }
1131         // we should clear it here before adapters are swapped to ensure correct callbacks.
1132         mRecycler.clear();
1133     }
1134 
1135     /**
1136      * Replaces the current adapter with the new one and triggers listeners.
1137      * @param adapter The new adapter
1138      * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
1139      *                               item types with the current adapter (helps us avoid cache
1140      *                               invalidation).
1141      * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
1142      *                               compatibleWithPrevious is false, this parameter is ignored.
1143      */
setAdapterInternal(@ullable Adapter adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews)1144     private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
1145             boolean removeAndRecycleViews) {
1146         if (mAdapter != null) {
1147             mAdapter.unregisterAdapterDataObserver(mObserver);
1148             mAdapter.onDetachedFromRecyclerView(this);
1149         }
1150         if (!compatibleWithPrevious || removeAndRecycleViews) {
1151             removeAndRecycleViews();
1152         }
1153         mAdapterHelper.reset();
1154         final Adapter oldAdapter = mAdapter;
1155         mAdapter = adapter;
1156         if (adapter != null) {
1157             adapter.registerAdapterDataObserver(mObserver);
1158             adapter.onAttachedToRecyclerView(this);
1159         }
1160         if (mLayout != null) {
1161             mLayout.onAdapterChanged(oldAdapter, mAdapter);
1162         }
1163         mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
1164         mState.mStructureChanged = true;
1165     }
1166 
1167     /**
1168      * Retrieves the previously set adapter or null if no adapter is set.
1169      *
1170      * @return The previously set adapter
1171      * @see #setAdapter(Adapter)
1172      */
1173     @Nullable
getAdapter()1174     public Adapter getAdapter() {
1175         return mAdapter;
1176     }
1177 
1178     /**
1179      * Register a listener that will be notified whenever a child view is recycled.
1180      *
1181      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1182      * that a child view is no longer needed. If an application associates expensive
1183      * or heavyweight data with item views, this may be a good place to release
1184      * or free those resources.</p>
1185      *
1186      * @param listener Listener to register, or null to clear
1187      */
setRecyclerListener(@ullable RecyclerListener listener)1188     public void setRecyclerListener(@Nullable RecyclerListener listener) {
1189         mRecyclerListener = listener;
1190     }
1191 
1192     /**
1193      * <p>Return the offset of the RecyclerView's text baseline from the its top
1194      * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
1195      * this method returns -1.</p>
1196      *
1197      * @return the offset of the baseline within the RecyclerView's bounds or -1
1198      *         if baseline alignment is not supported
1199      */
1200     @Override
getBaseline()1201     public int getBaseline() {
1202         if (mLayout != null) {
1203             return mLayout.getBaseline();
1204         } else {
1205             return super.getBaseline();
1206         }
1207     }
1208 
1209     /**
1210      * Register a listener that will be notified whenever a child view is attached to or detached
1211      * from RecyclerView.
1212      *
1213      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1214      * that a child view is no longer needed. If an application associates expensive
1215      * or heavyweight data with item views, this may be a good place to release
1216      * or free those resources.</p>
1217      *
1218      * @param listener Listener to register
1219      */
addOnChildAttachStateChangeListener( @onNull OnChildAttachStateChangeListener listener)1220     public void addOnChildAttachStateChangeListener(
1221             @NonNull OnChildAttachStateChangeListener listener) {
1222         if (mOnChildAttachStateListeners == null) {
1223             mOnChildAttachStateListeners = new ArrayList<>();
1224         }
1225         mOnChildAttachStateListeners.add(listener);
1226     }
1227 
1228     /**
1229      * Removes the provided listener from child attached state listeners list.
1230      *
1231      * @param listener Listener to unregister
1232      */
removeOnChildAttachStateChangeListener( @onNull OnChildAttachStateChangeListener listener)1233     public void removeOnChildAttachStateChangeListener(
1234             @NonNull OnChildAttachStateChangeListener listener) {
1235         if (mOnChildAttachStateListeners == null) {
1236             return;
1237         }
1238         mOnChildAttachStateListeners.remove(listener);
1239     }
1240 
1241     /**
1242      * Removes all listeners that were added via
1243      * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
1244      */
clearOnChildAttachStateChangeListeners()1245     public void clearOnChildAttachStateChangeListeners() {
1246         if (mOnChildAttachStateListeners != null) {
1247             mOnChildAttachStateListeners.clear();
1248         }
1249     }
1250 
1251     /**
1252      * Set the {@link LayoutManager} that this RecyclerView will use.
1253      *
1254      * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
1255      * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
1256      * layout arrangements for child views. These arrangements are controlled by the
1257      * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
1258      *
1259      * <p>Several default strategies are provided for common uses such as lists and grids.</p>
1260      *
1261      * @param layout LayoutManager to use
1262      */
setLayoutManager(@ullable LayoutManager layout)1263     public void setLayoutManager(@Nullable LayoutManager layout) {
1264         if (layout == mLayout) {
1265             return;
1266         }
1267         stopScroll();
1268         // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
1269         // chance that LayoutManagers will re-use views.
1270         if (mLayout != null) {
1271             // end all running animations
1272             if (mItemAnimator != null) {
1273                 mItemAnimator.endAnimations();
1274             }
1275             mLayout.removeAndRecycleAllViews(mRecycler);
1276             mLayout.removeAndRecycleScrapInt(mRecycler);
1277             mRecycler.clear();
1278 
1279             if (mIsAttached) {
1280                 mLayout.dispatchDetachedFromWindow(this, mRecycler);
1281             }
1282             mLayout.setRecyclerView(null);
1283             mLayout = null;
1284         } else {
1285             mRecycler.clear();
1286         }
1287         // this is just a defensive measure for faulty item animators.
1288         mChildHelper.removeAllViewsUnfiltered();
1289         mLayout = layout;
1290         if (layout != null) {
1291             if (layout.mRecyclerView != null) {
1292                 throw new IllegalArgumentException("LayoutManager " + layout
1293                         + " is already attached to a RecyclerView:"
1294                         + layout.mRecyclerView.exceptionLabel());
1295             }
1296             mLayout.setRecyclerView(this);
1297             if (mIsAttached) {
1298                 mLayout.dispatchAttachedToWindow(this);
1299             }
1300         }
1301         mRecycler.updateViewCacheSize();
1302         requestLayout();
1303     }
1304 
1305     /**
1306      * Set a {@link OnFlingListener} for this {@link RecyclerView}.
1307      * <p>
1308      * If the {@link OnFlingListener} is set then it will receive
1309      * calls to {@link #fling(int,int)} and will be able to intercept them.
1310      *
1311      * @param onFlingListener The {@link OnFlingListener} instance.
1312      */
setOnFlingListener(@ullable OnFlingListener onFlingListener)1313     public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
1314         mOnFlingListener = onFlingListener;
1315     }
1316 
1317     /**
1318      * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
1319      *
1320      * @return The {@link OnFlingListener} instance currently set (can be null).
1321      */
1322     @Nullable
getOnFlingListener()1323     public OnFlingListener getOnFlingListener() {
1324         return mOnFlingListener;
1325     }
1326 
1327     @Override
onSaveInstanceState()1328     protected Parcelable onSaveInstanceState() {
1329         SavedState state = new SavedState(super.onSaveInstanceState());
1330         if (mPendingSavedState != null) {
1331             state.copyFrom(mPendingSavedState);
1332         } else if (mLayout != null) {
1333             state.mLayoutState = mLayout.onSaveInstanceState();
1334         } else {
1335             state.mLayoutState = null;
1336         }
1337 
1338         return state;
1339     }
1340 
1341     @Override
onRestoreInstanceState(Parcelable state)1342     protected void onRestoreInstanceState(Parcelable state) {
1343         if (!(state instanceof SavedState)) {
1344             super.onRestoreInstanceState(state);
1345             return;
1346         }
1347 
1348         mPendingSavedState = (SavedState) state;
1349         super.onRestoreInstanceState(mPendingSavedState.getSuperState());
1350         if (mLayout != null && mPendingSavedState.mLayoutState != null) {
1351             mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
1352         }
1353     }
1354 
1355     /**
1356      * Override to prevent freezing of any views created by the adapter.
1357      */
1358     @Override
dispatchSaveInstanceState(SparseArray<Parcelable> container)1359     protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1360         dispatchFreezeSelfOnly(container);
1361     }
1362 
1363     /**
1364      * Override to prevent thawing of any views created by the adapter.
1365      */
1366     @Override
dispatchRestoreInstanceState(SparseArray<Parcelable> container)1367     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1368         dispatchThawSelfOnly(container);
1369     }
1370 
1371     /**
1372      * Adds a view to the animatingViews list.
1373      * mAnimatingViews holds the child views that are currently being kept around
1374      * purely for the purpose of being animated out of view. They are drawn as a regular
1375      * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
1376      * as they are managed separately from the regular child views.
1377      * @param viewHolder The ViewHolder to be removed
1378      */
addAnimatingView(ViewHolder viewHolder)1379     private void addAnimatingView(ViewHolder viewHolder) {
1380         final View view = viewHolder.itemView;
1381         final boolean alreadyParented = view.getParent() == this;
1382         mRecycler.unscrapView(getChildViewHolder(view));
1383         if (viewHolder.isTmpDetached()) {
1384             // re-attach
1385             mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
1386         } else if (!alreadyParented) {
1387             mChildHelper.addView(view, true);
1388         } else {
1389             mChildHelper.hide(view);
1390         }
1391     }
1392 
1393     /**
1394      * Removes a view from the animatingViews list.
1395      * @param view The view to be removed
1396      * @see #addAnimatingView(RecyclerView.ViewHolder)
1397      * @return true if an animating view is removed
1398      */
removeAnimatingView(View view)1399     boolean removeAnimatingView(View view) {
1400         startInterceptRequestLayout();
1401         final boolean removed = mChildHelper.removeViewIfHidden(view);
1402         if (removed) {
1403             final ViewHolder viewHolder = getChildViewHolderInt(view);
1404             mRecycler.unscrapView(viewHolder);
1405             mRecycler.recycleViewHolderInternal(viewHolder);
1406             if (DEBUG) {
1407                 Log.d(TAG, "after removing animated view: " + view + ", " + this);
1408             }
1409         }
1410         // only clear request eaten flag if we removed the view.
1411         stopInterceptRequestLayout(!removed);
1412         return removed;
1413     }
1414 
1415     /**
1416      * Return the {@link LayoutManager} currently responsible for
1417      * layout policy for this RecyclerView.
1418      *
1419      * @return The currently bound LayoutManager
1420      */
1421     @Nullable
getLayoutManager()1422     public LayoutManager getLayoutManager() {
1423         return mLayout;
1424     }
1425 
1426     /**
1427      * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
1428      * if no pool is set for this view a new one will be created. See
1429      * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
1430      *
1431      * @return The pool used to store recycled item views for reuse.
1432      * @see #setRecycledViewPool(RecycledViewPool)
1433      */
1434     @NonNull
getRecycledViewPool()1435     public RecycledViewPool getRecycledViewPool() {
1436         return mRecycler.getRecycledViewPool();
1437     }
1438 
1439     /**
1440      * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
1441      * This can be useful if you have multiple RecyclerViews with adapters that use the same
1442      * view types, for example if you have several data sets with the same kinds of item views
1443      * displayed by a {@link ViewPager ViewPager}.
1444      *
1445      * @param pool Pool to set. If this parameter is null a new pool will be created and used.
1446      */
setRecycledViewPool(@ullable RecycledViewPool pool)1447     public void setRecycledViewPool(@Nullable RecycledViewPool pool) {
1448         mRecycler.setRecycledViewPool(pool);
1449     }
1450 
1451     /**
1452      * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
1453      *
1454      * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
1455      *
1456      * @see ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)
1457      */
setViewCacheExtension(@ullable ViewCacheExtension extension)1458     public void setViewCacheExtension(@Nullable ViewCacheExtension extension) {
1459         mRecycler.setViewCacheExtension(extension);
1460     }
1461 
1462     /**
1463      * Set the number of offscreen views to retain before adding them to the potentially shared
1464      * {@link #getRecycledViewPool() recycled view pool}.
1465      *
1466      * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
1467      * a LayoutManager to reuse those views unmodified without needing to return to the adapter
1468      * to rebind them.</p>
1469      *
1470      * @param size Number of views to cache offscreen before returning them to the general
1471      *             recycled view pool
1472      */
setItemViewCacheSize(int size)1473     public void setItemViewCacheSize(int size) {
1474         mRecycler.setViewCacheSize(size);
1475     }
1476 
1477     /**
1478      * Return the current scrolling state of the RecyclerView.
1479      *
1480      * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
1481      * {@link #SCROLL_STATE_SETTLING}
1482      */
getScrollState()1483     public int getScrollState() {
1484         return mScrollState;
1485     }
1486 
setScrollState(int state)1487     void setScrollState(int state) {
1488         if (state == mScrollState) {
1489             return;
1490         }
1491         if (DEBUG) {
1492             Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
1493                     new Exception());
1494         }
1495         mScrollState = state;
1496         if (state != SCROLL_STATE_SETTLING) {
1497             stopScrollersInternal();
1498         }
1499         dispatchOnScrollStateChanged(state);
1500     }
1501 
1502     /**
1503      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1504      * affect both measurement and drawing of individual item views.
1505      *
1506      * <p>Item decorations are ordered. Decorations placed earlier in the list will
1507      * be run/queried/drawn first for their effects on item views. Padding added to views
1508      * will be nested; a padding added by an earlier decoration will mean further
1509      * item decorations in the list will be asked to draw/pad within the previous decoration's
1510      * given area.</p>
1511      *
1512      * @param decor Decoration to add
1513      * @param index Position in the decoration chain to insert this decoration at. If this value
1514      *              is negative the decoration will be added at the end.
1515      */
addItemDecoration(@onNull ItemDecoration decor, int index)1516     public void addItemDecoration(@NonNull ItemDecoration decor, int index) {
1517         if (mLayout != null) {
1518             mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
1519                     + " layout");
1520         }
1521         if (mItemDecorations.isEmpty()) {
1522             setWillNotDraw(false);
1523         }
1524         if (index < 0) {
1525             mItemDecorations.add(decor);
1526         } else {
1527             mItemDecorations.add(index, decor);
1528         }
1529         markItemDecorInsetsDirty();
1530         requestLayout();
1531     }
1532 
1533     /**
1534      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1535      * affect both measurement and drawing of individual item views.
1536      *
1537      * <p>Item decorations are ordered. Decorations placed earlier in the list will
1538      * be run/queried/drawn first for their effects on item views. Padding added to views
1539      * will be nested; a padding added by an earlier decoration will mean further
1540      * item decorations in the list will be asked to draw/pad within the previous decoration's
1541      * given area.</p>
1542      *
1543      * @param decor Decoration to add
1544      */
addItemDecoration(@onNull ItemDecoration decor)1545     public void addItemDecoration(@NonNull ItemDecoration decor) {
1546         addItemDecoration(decor, -1);
1547     }
1548 
1549     /**
1550      * Returns an {@link ItemDecoration} previously added to this RecyclerView.
1551      *
1552      * @param index The index position of the desired ItemDecoration.
1553      * @return the ItemDecoration at index position
1554      * @throws IndexOutOfBoundsException on invalid index
1555      */
1556     @NonNull
getItemDecorationAt(int index)1557     public ItemDecoration getItemDecorationAt(int index) {
1558         final int size = getItemDecorationCount();
1559         if (index < 0 || index >= size) {
1560             throw new IndexOutOfBoundsException(index + " is an invalid index for size " + size);
1561         }
1562 
1563         return mItemDecorations.get(index);
1564     }
1565 
1566     /**
1567      * Returns the number of {@link ItemDecoration} currently added to this RecyclerView.
1568      *
1569      * @return number of ItemDecorations currently added added to this RecyclerView.
1570      */
getItemDecorationCount()1571     public int getItemDecorationCount() {
1572         return mItemDecorations.size();
1573     }
1574 
1575     /**
1576      * Removes the {@link ItemDecoration} associated with the supplied index position.
1577      *
1578      * @param index The index position of the ItemDecoration to be removed.
1579      */
removeItemDecorationAt(int index)1580     public void removeItemDecorationAt(int index) {
1581         final int size = getItemDecorationCount();
1582         if (index < 0 || index >= size) {
1583             throw new IndexOutOfBoundsException(index + " is an invalid index for size " + size);
1584         }
1585 
1586         removeItemDecoration(getItemDecorationAt(index));
1587     }
1588 
1589     /**
1590      * Remove an {@link ItemDecoration} from this RecyclerView.
1591      *
1592      * <p>The given decoration will no longer impact the measurement and drawing of
1593      * item views.</p>
1594      *
1595      * @param decor Decoration to remove
1596      * @see #addItemDecoration(ItemDecoration)
1597      */
removeItemDecoration(@onNull ItemDecoration decor)1598     public void removeItemDecoration(@NonNull ItemDecoration decor) {
1599         if (mLayout != null) {
1600             mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
1601                     + " layout");
1602         }
1603         mItemDecorations.remove(decor);
1604         if (mItemDecorations.isEmpty()) {
1605             setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
1606         }
1607         markItemDecorInsetsDirty();
1608         requestLayout();
1609     }
1610 
1611     /**
1612      * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
1613      * <p>
1614      * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
1615      * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
1616      * true if childDrawingOrderCallback is not null, false otherwise.
1617      * <p>
1618      * Note that child drawing order may be overridden by View's elevation.
1619      *
1620      * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
1621      *                                  system.
1622      */
setChildDrawingOrderCallback( @ullable ChildDrawingOrderCallback childDrawingOrderCallback)1623     public void setChildDrawingOrderCallback(
1624             @Nullable ChildDrawingOrderCallback childDrawingOrderCallback) {
1625         if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
1626             return;
1627         }
1628         mChildDrawingOrderCallback = childDrawingOrderCallback;
1629         setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
1630     }
1631 
1632     /**
1633      * Set a listener that will be notified of any changes in scroll state or position.
1634      *
1635      * @param listener Listener to set or null to clear
1636      *
1637      * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
1638      *             {@link #removeOnScrollListener(OnScrollListener)}
1639      */
1640     @Deprecated
setOnScrollListener(@ullable OnScrollListener listener)1641     public void setOnScrollListener(@Nullable OnScrollListener listener) {
1642         mScrollListener = listener;
1643     }
1644 
1645     /**
1646      * Add a listener that will be notified of any changes in scroll state or position.
1647      *
1648      * <p>Components that add a listener should take care to remove it when finished.
1649      * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
1650      * to remove all attached listeners.</p>
1651      *
1652      * @param listener listener to set
1653      */
addOnScrollListener(@onNull OnScrollListener listener)1654     public void addOnScrollListener(@NonNull OnScrollListener listener) {
1655         if (mScrollListeners == null) {
1656             mScrollListeners = new ArrayList<>();
1657         }
1658         mScrollListeners.add(listener);
1659     }
1660 
1661     /**
1662      * Remove a listener that was notified of any changes in scroll state or position.
1663      *
1664      * @param listener listener to set or null to clear
1665      */
removeOnScrollListener(@onNull OnScrollListener listener)1666     public void removeOnScrollListener(@NonNull OnScrollListener listener) {
1667         if (mScrollListeners != null) {
1668             mScrollListeners.remove(listener);
1669         }
1670     }
1671 
1672     /**
1673      * Remove all secondary listener that were notified of any changes in scroll state or position.
1674      */
clearOnScrollListeners()1675     public void clearOnScrollListeners() {
1676         if (mScrollListeners != null) {
1677             mScrollListeners.clear();
1678         }
1679     }
1680 
1681     /**
1682      * Convenience method to scroll to a certain position.
1683      *
1684      * RecyclerView does not implement scrolling logic, rather forwards the call to
1685      * {@link RecyclerView.LayoutManager#scrollToPosition(int)}
1686      * @param position Scroll to this adapter position
1687      * @see RecyclerView.LayoutManager#scrollToPosition(int)
1688      */
scrollToPosition(int position)1689     public void scrollToPosition(int position) {
1690         if (mLayoutFrozen) {
1691             return;
1692         }
1693         stopScroll();
1694         if (mLayout == null) {
1695             Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
1696                     + "Call setLayoutManager with a non-null argument.");
1697             return;
1698         }
1699         mLayout.scrollToPosition(position);
1700         awakenScrollBars();
1701     }
1702 
jumpToPositionForSmoothScroller(int position)1703     void jumpToPositionForSmoothScroller(int position) {
1704         if (mLayout == null) {
1705             return;
1706         }
1707         mLayout.scrollToPosition(position);
1708         awakenScrollBars();
1709     }
1710 
1711     /**
1712      * Starts a smooth scroll to an adapter position.
1713      * <p>
1714      * To support smooth scrolling, you must override
1715      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
1716      * {@link SmoothScroller}.
1717      * <p>
1718      * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
1719      * provide a custom smooth scroll logic, override
1720      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
1721      * LayoutManager.
1722      *
1723      * @param position The adapter position to scroll to
1724      * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
1725      */
smoothScrollToPosition(int position)1726     public void smoothScrollToPosition(int position) {
1727         if (mLayoutFrozen) {
1728             return;
1729         }
1730         if (mLayout == null) {
1731             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
1732                     + "Call setLayoutManager with a non-null argument.");
1733             return;
1734         }
1735         mLayout.smoothScrollToPosition(this, mState, position);
1736     }
1737 
1738     @Override
scrollTo(int x, int y)1739     public void scrollTo(int x, int y) {
1740         Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
1741                 + "Use scrollToPosition instead");
1742     }
1743 
1744     @Override
scrollBy(int x, int y)1745     public void scrollBy(int x, int y) {
1746         if (mLayout == null) {
1747             Log.e(TAG, "Cannot scroll without a LayoutManager set. "
1748                     + "Call setLayoutManager with a non-null argument.");
1749             return;
1750         }
1751         if (mLayoutFrozen) {
1752             return;
1753         }
1754         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1755         final boolean canScrollVertical = mLayout.canScrollVertically();
1756         if (canScrollHorizontal || canScrollVertical) {
1757             scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
1758         }
1759     }
1760 
1761     /**
1762      * Scrolls the RV by 'dx' and 'dy' via calls to
1763      * {@link LayoutManager#scrollHorizontallyBy(int, Recycler, State)} and
1764      * {@link LayoutManager#scrollVerticallyBy(int, Recycler, State)}.
1765      *
1766      * Also sets how much of the scroll was actually consumed in 'consumed' parameter (indexes 0 and
1767      * 1 for the x axis and y axis, respectively).
1768      *
1769      * This method should only be called in the context of an existing scroll operation such that
1770      * any other necessary operations (such as a call to {@link #consumePendingUpdateOperations()})
1771      * is already handled.
1772      */
scrollStep(int dx, int dy, @Nullable int[] consumed)1773     private void scrollStep(int dx, int dy, @Nullable int[] consumed) {
1774         startInterceptRequestLayout();
1775         onEnterLayoutOrScroll();
1776 
1777         TraceCompat.beginSection(TRACE_SCROLL_TAG);
1778         fillRemainingScrollValues(mState);
1779 
1780         int consumedX = 0;
1781         int consumedY = 0;
1782         if (dx != 0) {
1783             consumedX = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
1784         }
1785         if (dy != 0) {
1786             consumedY = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
1787         }
1788 
1789         TraceCompat.endSection();
1790         repositionShadowingViews();
1791 
1792         onExitLayoutOrScroll();
1793         stopInterceptRequestLayout(false);
1794 
1795         if (consumed != null) {
1796             consumed[0] = consumedX;
1797             consumed[1] = consumedY;
1798         }
1799     }
1800 
1801     /**
1802      * Helper method reflect data changes to the state.
1803      * <p>
1804      * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
1805      * but data actually changed.
1806      * <p>
1807      * This method consumes all deferred changes to avoid that case.
1808      */
consumePendingUpdateOperations()1809     void consumePendingUpdateOperations() {
1810         if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
1811             TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1812             dispatchLayout();
1813             TraceCompat.endSection();
1814             return;
1815         }
1816         if (!mAdapterHelper.hasPendingUpdates()) {
1817             return;
1818         }
1819 
1820         // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
1821         // of the visible items is affected and if not, just ignore the change.
1822         if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
1823                 .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
1824                         | AdapterHelper.UpdateOp.MOVE)) {
1825             TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
1826             startInterceptRequestLayout();
1827             onEnterLayoutOrScroll();
1828             mAdapterHelper.preProcess();
1829             if (!mLayoutWasDefered) {
1830                 if (hasUpdatedView()) {
1831                     dispatchLayout();
1832                 } else {
1833                     // no need to layout, clean state
1834                     mAdapterHelper.consumePostponedUpdates();
1835                 }
1836             }
1837             stopInterceptRequestLayout(true);
1838             onExitLayoutOrScroll();
1839             TraceCompat.endSection();
1840         } else if (mAdapterHelper.hasPendingUpdates()) {
1841             TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1842             dispatchLayout();
1843             TraceCompat.endSection();
1844         }
1845     }
1846 
1847     /**
1848      * @return True if an existing view holder needs to be updated
1849      */
hasUpdatedView()1850     private boolean hasUpdatedView() {
1851         final int childCount = mChildHelper.getChildCount();
1852         for (int i = 0; i < childCount; i++) {
1853             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
1854             if (holder == null || holder.shouldIgnore()) {
1855                 continue;
1856             }
1857             if (holder.isUpdated()) {
1858                 return true;
1859             }
1860         }
1861         return false;
1862     }
1863 
1864     /**
1865      * Does not perform bounds checking. Used by internal methods that have already validated input.
1866      * <p>
1867      * It also reports any unused scroll request to the related EdgeEffect.
1868      *
1869      * @param x The amount of horizontal scroll request
1870      * @param y The amount of vertical scroll request
1871      * @param ev The originating MotionEvent, or null if not from a touch event.
1872      *
1873      * @return Whether any scroll was consumed in either direction.
1874      */
scrollByInternal(int x, int y, MotionEvent ev)1875     boolean scrollByInternal(int x, int y, MotionEvent ev) {
1876         int unconsumedX = 0, unconsumedY = 0;
1877         int consumedX = 0, consumedY = 0;
1878 
1879         consumePendingUpdateOperations();
1880         if (mAdapter != null) {
1881             scrollStep(x, y, mScrollStepConsumed);
1882             consumedX = mScrollStepConsumed[0];
1883             consumedY = mScrollStepConsumed[1];
1884             unconsumedX = x - consumedX;
1885             unconsumedY = y - consumedY;
1886         }
1887         if (!mItemDecorations.isEmpty()) {
1888             invalidate();
1889         }
1890 
1891         if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset,
1892                 TYPE_TOUCH)) {
1893             // Update the last touch co-ords, taking any scroll offset into account
1894             mLastTouchX -= mScrollOffset[0];
1895             mLastTouchY -= mScrollOffset[1];
1896             if (ev != null) {
1897                 ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
1898             }
1899             mNestedOffsets[0] += mScrollOffset[0];
1900             mNestedOffsets[1] += mScrollOffset[1];
1901         } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
1902             if (ev != null && !MotionEventCompat.isFromSource(ev, InputDevice.SOURCE_MOUSE)) {
1903                 pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
1904             }
1905             considerReleasingGlowsOnScroll(x, y);
1906         }
1907         if (consumedX != 0 || consumedY != 0) {
1908             dispatchOnScrolled(consumedX, consumedY);
1909         }
1910         if (!awakenScrollBars()) {
1911             invalidate();
1912         }
1913         return consumedX != 0 || consumedY != 0;
1914     }
1915 
1916     /**
1917      * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
1918      * range. This value is used to compute the length of the thumb within the scrollbar's track.
1919      * </p>
1920      *
1921      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1922      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
1923      *
1924      * <p>Default implementation returns 0.</p>
1925      *
1926      * <p>If you want to support scroll bars, override
1927      * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
1928      * LayoutManager. </p>
1929      *
1930      * @return The horizontal offset of the scrollbar's thumb
1931      * @see RecyclerView.LayoutManager#computeHorizontalScrollOffset
1932      * (RecyclerView.State)
1933      */
1934     @Override
computeHorizontalScrollOffset()1935     public int computeHorizontalScrollOffset() {
1936         if (mLayout == null) {
1937             return 0;
1938         }
1939         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
1940     }
1941 
1942     /**
1943      * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
1944      * horizontal range. This value is used to compute the length of the thumb within the
1945      * scrollbar's track.</p>
1946      *
1947      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1948      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
1949      *
1950      * <p>Default implementation returns 0.</p>
1951      *
1952      * <p>If you want to support scroll bars, override
1953      * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
1954      * LayoutManager.</p>
1955      *
1956      * @return The horizontal extent of the scrollbar's thumb
1957      * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
1958      */
1959     @Override
computeHorizontalScrollExtent()1960     public int computeHorizontalScrollExtent() {
1961         if (mLayout == null) {
1962             return 0;
1963         }
1964         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
1965     }
1966 
1967     /**
1968      * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
1969      *
1970      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1971      * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
1972      *
1973      * <p>Default implementation returns 0.</p>
1974      *
1975      * <p>If you want to support scroll bars, override
1976      * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
1977      * LayoutManager.</p>
1978      *
1979      * @return The total horizontal range represented by the vertical scrollbar
1980      * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
1981      */
1982     @Override
computeHorizontalScrollRange()1983     public int computeHorizontalScrollRange() {
1984         if (mLayout == null) {
1985             return 0;
1986         }
1987         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
1988     }
1989 
1990     /**
1991      * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
1992      * This value is used to compute the length of the thumb within the scrollbar's track. </p>
1993      *
1994      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1995      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
1996      *
1997      * <p>Default implementation returns 0.</p>
1998      *
1999      * <p>If you want to support scroll bars, override
2000      * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
2001      * LayoutManager.</p>
2002      *
2003      * @return The vertical offset of the scrollbar's thumb
2004      * @see RecyclerView.LayoutManager#computeVerticalScrollOffset
2005      * (RecyclerView.State)
2006      */
2007     @Override
computeVerticalScrollOffset()2008     public int computeVerticalScrollOffset() {
2009         if (mLayout == null) {
2010             return 0;
2011         }
2012         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
2013     }
2014 
2015     /**
2016      * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
2017      * This value is used to compute the length of the thumb within the scrollbar's track.</p>
2018      *
2019      * <p>The range is expressed in arbitrary units that must be the same as the units used by
2020      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
2021      *
2022      * <p>Default implementation returns 0.</p>
2023      *
2024      * <p>If you want to support scroll bars, override
2025      * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
2026      * LayoutManager.</p>
2027      *
2028      * @return The vertical extent of the scrollbar's thumb
2029      * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
2030      */
2031     @Override
computeVerticalScrollExtent()2032     public int computeVerticalScrollExtent() {
2033         if (mLayout == null) {
2034             return 0;
2035         }
2036         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
2037     }
2038 
2039     /**
2040      * <p>Compute the vertical range that the vertical scrollbar represents.</p>
2041      *
2042      * <p>The range is expressed in arbitrary units that must be the same as the units used by
2043      * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
2044      *
2045      * <p>Default implementation returns 0.</p>
2046      *
2047      * <p>If you want to support scroll bars, override
2048      * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
2049      * LayoutManager.</p>
2050      *
2051      * @return The total vertical range represented by the vertical scrollbar
2052      * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
2053      */
2054     @Override
computeVerticalScrollRange()2055     public int computeVerticalScrollRange() {
2056         if (mLayout == null) {
2057             return 0;
2058         }
2059         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
2060     }
2061 
2062     /**
2063      * This method should be called before any code that may trigger a child view to cause a call to
2064      * {@link RecyclerView#requestLayout()}.  Doing so enables {@link RecyclerView} to avoid
2065      * reacting to additional redundant calls to {@link #requestLayout()}.
2066      * <p>
2067      * A call to this method must always be accompanied by a call to
2068      * {@link #stopInterceptRequestLayout(boolean)} that follows the code that may trigger a
2069      * child View to cause a call to {@link RecyclerView#requestLayout()}.
2070      *
2071      * @see #stopInterceptRequestLayout(boolean)
2072      */
startInterceptRequestLayout()2073     void startInterceptRequestLayout() {
2074         mInterceptRequestLayoutDepth++;
2075         if (mInterceptRequestLayoutDepth == 1 && !mLayoutFrozen) {
2076             mLayoutWasDefered = false;
2077         }
2078     }
2079 
2080     /**
2081      * This method should be called after any code that may trigger a child view to cause a call to
2082      * {@link RecyclerView#requestLayout()}.
2083      * <p>
2084      * A call to this method must always be accompanied by a call to
2085      * {@link #startInterceptRequestLayout()} that precedes the code that may trigger a child
2086      * View to cause a call to {@link RecyclerView#requestLayout()}.
2087      *
2088      * @see #startInterceptRequestLayout()
2089      */
stopInterceptRequestLayout(boolean performLayoutChildren)2090     void stopInterceptRequestLayout(boolean performLayoutChildren) {
2091         if (mInterceptRequestLayoutDepth < 1) {
2092             //noinspection PointlessBooleanExpression
2093             if (DEBUG) {
2094                 throw new IllegalStateException("stopInterceptRequestLayout was called more "
2095                         + "times than startInterceptRequestLayout."
2096                         + exceptionLabel());
2097             }
2098             mInterceptRequestLayoutDepth = 1;
2099         }
2100         if (!performLayoutChildren && !mLayoutFrozen) {
2101             // Reset the layout request eaten counter.
2102             // This is necessary since eatRequest calls can be nested in which case the other
2103             // call will override the inner one.
2104             // for instance:
2105             // eat layout for process adapter updates
2106             //   eat layout for dispatchLayout
2107             //     a bunch of req layout calls arrive
2108 
2109             mLayoutWasDefered = false;
2110         }
2111         if (mInterceptRequestLayoutDepth == 1) {
2112             // when layout is frozen we should delay dispatchLayout()
2113             if (performLayoutChildren && mLayoutWasDefered && !mLayoutFrozen
2114                     && mLayout != null && mAdapter != null) {
2115                 dispatchLayout();
2116             }
2117             if (!mLayoutFrozen) {
2118                 mLayoutWasDefered = false;
2119             }
2120         }
2121         mInterceptRequestLayoutDepth--;
2122     }
2123 
2124     /**
2125      * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
2126      * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
2127      * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
2128      * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
2129      * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
2130      * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
2131      * called.
2132      *
2133      * <p>
2134      * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
2135      * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
2136      * RecyclerView, State, int)}.
2137      * <p>
2138      * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
2139      * stop frozen.
2140      * <p>
2141      * Note: Running ItemAnimator is not stopped automatically,  it's caller's
2142      * responsibility to call ItemAnimator.end().
2143      *
2144      * @param frozen   true to freeze layout and scroll, false to re-enable.
2145      */
setLayoutFrozen(boolean frozen)2146     public void setLayoutFrozen(boolean frozen) {
2147         if (frozen != mLayoutFrozen) {
2148             assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
2149             if (!frozen) {
2150                 mLayoutFrozen = false;
2151                 if (mLayoutWasDefered && mLayout != null && mAdapter != null) {
2152                     requestLayout();
2153                 }
2154                 mLayoutWasDefered = false;
2155             } else {
2156                 final long now = SystemClock.uptimeMillis();
2157                 MotionEvent cancelEvent = MotionEvent.obtain(now, now,
2158                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2159                 onTouchEvent(cancelEvent);
2160                 mLayoutFrozen = true;
2161                 mIgnoreMotionEventTillDown = true;
2162                 stopScroll();
2163             }
2164         }
2165     }
2166 
2167     /**
2168      * Returns true if layout and scroll are frozen.
2169      *
2170      * @return true if layout and scroll are frozen
2171      * @see #setLayoutFrozen(boolean)
2172      */
isLayoutFrozen()2173     public boolean isLayoutFrozen() {
2174         return mLayoutFrozen;
2175     }
2176 
2177     /**
2178      * Animate a scroll by the given amount of pixels along either axis.
2179      *
2180      * @param dx Pixels to scroll horizontally
2181      * @param dy Pixels to scroll vertically
2182      */
smoothScrollBy(@x int dx, @Px int dy)2183     public void smoothScrollBy(@Px int dx, @Px int dy) {
2184         smoothScrollBy(dx, dy, null);
2185     }
2186 
2187     /**
2188      * Animate a scroll by the given amount of pixels along either axis.
2189      *
2190      * @param dx Pixels to scroll horizontally
2191      * @param dy Pixels to scroll vertically
2192      * @param interpolator {@link Interpolator} to be used for scrolling. If it is
2193      *                     {@code null}, RecyclerView is going to use the default interpolator.
2194      */
smoothScrollBy(@x int dx, @Px int dy, @Nullable Interpolator interpolator)2195     public void smoothScrollBy(@Px int dx, @Px int dy, @Nullable Interpolator interpolator) {
2196         if (mLayout == null) {
2197             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
2198                     + "Call setLayoutManager with a non-null argument.");
2199             return;
2200         }
2201         if (mLayoutFrozen) {
2202             return;
2203         }
2204         if (!mLayout.canScrollHorizontally()) {
2205             dx = 0;
2206         }
2207         if (!mLayout.canScrollVertically()) {
2208             dy = 0;
2209         }
2210         if (dx != 0 || dy != 0) {
2211             mViewFlinger.smoothScrollBy(dx, dy, interpolator);
2212         }
2213     }
2214 
2215     /**
2216      * Begin a standard fling with an initial velocity along each axis in pixels per second.
2217      * If the velocity given is below the system-defined minimum this method will return false
2218      * and no fling will occur.
2219      *
2220      * @param velocityX Initial horizontal velocity in pixels per second
2221      * @param velocityY Initial vertical velocity in pixels per second
2222      * @return true if the fling was started, false if the velocity was too low to fling or
2223      * LayoutManager does not support scrolling in the axis fling is issued.
2224      *
2225      * @see LayoutManager#canScrollVertically()
2226      * @see LayoutManager#canScrollHorizontally()
2227      */
fling(int velocityX, int velocityY)2228     public boolean fling(int velocityX, int velocityY) {
2229         if (mLayout == null) {
2230             Log.e(TAG, "Cannot fling without a LayoutManager set. "
2231                     + "Call setLayoutManager with a non-null argument.");
2232             return false;
2233         }
2234         if (mLayoutFrozen) {
2235             return false;
2236         }
2237 
2238         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
2239         final boolean canScrollVertical = mLayout.canScrollVertically();
2240 
2241         if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
2242             velocityX = 0;
2243         }
2244         if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
2245             velocityY = 0;
2246         }
2247         if (velocityX == 0 && velocityY == 0) {
2248             // If we don't have any velocity, return false
2249             return false;
2250         }
2251 
2252         if (!dispatchNestedPreFling(velocityX, velocityY)) {
2253             final boolean canScroll = canScrollHorizontal || canScrollVertical;
2254             dispatchNestedFling(velocityX, velocityY, canScroll);
2255 
2256             if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
2257                 return true;
2258             }
2259 
2260             if (canScroll) {
2261                 int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2262                 if (canScrollHorizontal) {
2263                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2264                 }
2265                 if (canScrollVertical) {
2266                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2267                 }
2268                 startNestedScroll(nestedScrollAxis, TYPE_NON_TOUCH);
2269 
2270                 velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
2271                 velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
2272                 mViewFlinger.fling(velocityX, velocityY);
2273                 return true;
2274             }
2275         }
2276         return false;
2277     }
2278 
2279     /**
2280      * Stop any current scroll in progress, such as one started by
2281      * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
2282      */
stopScroll()2283     public void stopScroll() {
2284         setScrollState(SCROLL_STATE_IDLE);
2285         stopScrollersInternal();
2286     }
2287 
2288     /**
2289      * Similar to {@link #stopScroll()} but does not set the state.
2290      */
stopScrollersInternal()2291     private void stopScrollersInternal() {
2292         mViewFlinger.stop();
2293         if (mLayout != null) {
2294             mLayout.stopSmoothScroller();
2295         }
2296     }
2297 
2298     /**
2299      * Returns the minimum velocity to start a fling.
2300      *
2301      * @return The minimum velocity to start a fling
2302      */
getMinFlingVelocity()2303     public int getMinFlingVelocity() {
2304         return mMinFlingVelocity;
2305     }
2306 
2307 
2308     /**
2309      * Returns the maximum fling velocity used by this RecyclerView.
2310      *
2311      * @return The maximum fling velocity used by this RecyclerView.
2312      */
getMaxFlingVelocity()2313     public int getMaxFlingVelocity() {
2314         return mMaxFlingVelocity;
2315     }
2316 
2317     /**
2318      * Apply a pull to relevant overscroll glow effects
2319      */
pullGlows(float x, float overscrollX, float y, float overscrollY)2320     private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
2321         boolean invalidate = false;
2322         if (overscrollX < 0) {
2323             ensureLeftGlow();
2324             EdgeEffectCompat.onPull(mLeftGlow, -overscrollX / getWidth(), 1f - y  / getHeight());
2325             invalidate = true;
2326         } else if (overscrollX > 0) {
2327             ensureRightGlow();
2328             EdgeEffectCompat.onPull(mRightGlow, overscrollX / getWidth(), y / getHeight());
2329             invalidate = true;
2330         }
2331 
2332         if (overscrollY < 0) {
2333             ensureTopGlow();
2334             EdgeEffectCompat.onPull(mTopGlow, -overscrollY / getHeight(), x / getWidth());
2335             invalidate = true;
2336         } else if (overscrollY > 0) {
2337             ensureBottomGlow();
2338             EdgeEffectCompat.onPull(mBottomGlow, overscrollY / getHeight(), 1f - x / getWidth());
2339             invalidate = true;
2340         }
2341 
2342         if (invalidate || overscrollX != 0 || overscrollY != 0) {
2343             ViewCompat.postInvalidateOnAnimation(this);
2344         }
2345     }
2346 
releaseGlows()2347     private void releaseGlows() {
2348         boolean needsInvalidate = false;
2349         if (mLeftGlow != null) {
2350             mLeftGlow.onRelease();
2351             needsInvalidate = mLeftGlow.isFinished();
2352         }
2353         if (mTopGlow != null) {
2354             mTopGlow.onRelease();
2355             needsInvalidate |= mTopGlow.isFinished();
2356         }
2357         if (mRightGlow != null) {
2358             mRightGlow.onRelease();
2359             needsInvalidate |= mRightGlow.isFinished();
2360         }
2361         if (mBottomGlow != null) {
2362             mBottomGlow.onRelease();
2363             needsInvalidate |= mBottomGlow.isFinished();
2364         }
2365         if (needsInvalidate) {
2366             ViewCompat.postInvalidateOnAnimation(this);
2367         }
2368     }
2369 
considerReleasingGlowsOnScroll(int dx, int dy)2370     void considerReleasingGlowsOnScroll(int dx, int dy) {
2371         boolean needsInvalidate = false;
2372         if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
2373             mLeftGlow.onRelease();
2374             needsInvalidate = mLeftGlow.isFinished();
2375         }
2376         if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
2377             mRightGlow.onRelease();
2378             needsInvalidate |= mRightGlow.isFinished();
2379         }
2380         if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
2381             mTopGlow.onRelease();
2382             needsInvalidate |= mTopGlow.isFinished();
2383         }
2384         if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
2385             mBottomGlow.onRelease();
2386             needsInvalidate |= mBottomGlow.isFinished();
2387         }
2388         if (needsInvalidate) {
2389             ViewCompat.postInvalidateOnAnimation(this);
2390         }
2391     }
2392 
absorbGlows(int velocityX, int velocityY)2393     void absorbGlows(int velocityX, int velocityY) {
2394         if (velocityX < 0) {
2395             ensureLeftGlow();
2396             mLeftGlow.onAbsorb(-velocityX);
2397         } else if (velocityX > 0) {
2398             ensureRightGlow();
2399             mRightGlow.onAbsorb(velocityX);
2400         }
2401 
2402         if (velocityY < 0) {
2403             ensureTopGlow();
2404             mTopGlow.onAbsorb(-velocityY);
2405         } else if (velocityY > 0) {
2406             ensureBottomGlow();
2407             mBottomGlow.onAbsorb(velocityY);
2408         }
2409 
2410         if (velocityX != 0 || velocityY != 0) {
2411             ViewCompat.postInvalidateOnAnimation(this);
2412         }
2413     }
2414 
ensureLeftGlow()2415     void ensureLeftGlow() {
2416         if (mLeftGlow != null) {
2417             return;
2418         }
2419         mLeftGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_LEFT);
2420         if (mClipToPadding) {
2421             mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2422                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2423         } else {
2424             mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2425         }
2426     }
2427 
ensureRightGlow()2428     void ensureRightGlow() {
2429         if (mRightGlow != null) {
2430             return;
2431         }
2432         mRightGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_RIGHT);
2433         if (mClipToPadding) {
2434             mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2435                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2436         } else {
2437             mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2438         }
2439     }
2440 
ensureTopGlow()2441     void ensureTopGlow() {
2442         if (mTopGlow != null) {
2443             return;
2444         }
2445         mTopGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_TOP);
2446         if (mClipToPadding) {
2447             mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2448                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2449         } else {
2450             mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2451         }
2452 
2453     }
2454 
ensureBottomGlow()2455     void ensureBottomGlow() {
2456         if (mBottomGlow != null) {
2457             return;
2458         }
2459         mBottomGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_BOTTOM);
2460         if (mClipToPadding) {
2461             mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2462                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2463         } else {
2464             mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2465         }
2466     }
2467 
invalidateGlows()2468     void invalidateGlows() {
2469         mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
2470     }
2471 
2472     /**
2473      * Set a {@link EdgeEffectFactory} for this {@link RecyclerView}.
2474      * <p>
2475      * When a new {@link EdgeEffectFactory} is set, any existing over-scroll effects are cleared
2476      * and new effects are created as needed using
2477      * {@link EdgeEffectFactory#createEdgeEffect(RecyclerView, int)}
2478      *
2479      * @param edgeEffectFactory The {@link EdgeEffectFactory} instance.
2480      */
setEdgeEffectFactory(@onNull EdgeEffectFactory edgeEffectFactory)2481     public void setEdgeEffectFactory(@NonNull EdgeEffectFactory edgeEffectFactory) {
2482         Preconditions.checkNotNull(edgeEffectFactory);
2483         mEdgeEffectFactory = edgeEffectFactory;
2484         invalidateGlows();
2485     }
2486 
2487     /**
2488      * Retrieves the previously set {@link EdgeEffectFactory} or the default factory if nothing
2489      * was set.
2490      *
2491      * @return The previously set {@link EdgeEffectFactory}
2492      * @see #setEdgeEffectFactory(EdgeEffectFactory)
2493      */
2494     @NonNull
getEdgeEffectFactory()2495     public EdgeEffectFactory getEdgeEffectFactory() {
2496         return mEdgeEffectFactory;
2497     }
2498 
2499     /**
2500      * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
2501      * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
2502      * that differs from other ViewGroups.
2503      * <p>
2504      * It first does a focus search within the RecyclerView. If this search finds a View that is in
2505      * the focus direction with respect to the currently focused View, RecyclerView returns that
2506      * child as the next focus target. When it cannot find such child, it calls
2507      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
2508      * in the focus search direction. If LayoutManager adds a View that matches the
2509      * focus search criteria, it will be returned as the focus search result. Otherwise,
2510      * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
2511      * <p>
2512      * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
2513      * is not in the focus direction is still valid focus target which may not be the desired
2514      * behavior if the Adapter has more children in the focus direction. To handle this case,
2515      * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
2516      * focus search in that direction. If there are no Views to gain focus, it will call
2517      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
2518      * focus search with the original (relative) direction. This allows RecyclerView to provide
2519      * better candidates to the focus search while still allowing the view system to take focus from
2520      * the RecyclerView and give it to a more suitable child if such child exists.
2521      *
2522      * @param focused The view that currently has focus
2523      * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
2524      * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
2525      * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
2526      *
2527      * @return A new View that can be the next focus after the focused View
2528      */
2529     @Override
focusSearch(View focused, int direction)2530     public View focusSearch(View focused, int direction) {
2531         View result = mLayout.onInterceptFocusSearch(focused, direction);
2532         if (result != null) {
2533             return result;
2534         }
2535         final boolean canRunFocusFailure = mAdapter != null && mLayout != null
2536                 && !isComputingLayout() && !mLayoutFrozen;
2537 
2538         final FocusFinder ff = FocusFinder.getInstance();
2539         if (canRunFocusFailure
2540                 && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
2541             // convert direction to absolute direction and see if we have a view there and if not
2542             // tell LayoutManager to add if it can.
2543             boolean needsFocusFailureLayout = false;
2544             if (mLayout.canScrollVertically()) {
2545                 final int absDir =
2546                         direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
2547                 final View found = ff.findNextFocus(this, focused, absDir);
2548                 needsFocusFailureLayout = found == null;
2549                 if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2550                     // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2551                     direction = absDir;
2552                 }
2553             }
2554             if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
2555                 boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
2556                 final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
2557                         ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2558                 final View found = ff.findNextFocus(this, focused, absDir);
2559                 needsFocusFailureLayout = found == null;
2560                 if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2561                     // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2562                     direction = absDir;
2563                 }
2564             }
2565             if (needsFocusFailureLayout) {
2566                 consumePendingUpdateOperations();
2567                 final View focusedItemView = findContainingItemView(focused);
2568                 if (focusedItemView == null) {
2569                     // panic, focused view is not a child anymore, cannot call super.
2570                     return null;
2571                 }
2572                 startInterceptRequestLayout();
2573                 mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2574                 stopInterceptRequestLayout(false);
2575             }
2576             result = ff.findNextFocus(this, focused, direction);
2577         } else {
2578             result = ff.findNextFocus(this, focused, direction);
2579             if (result == null && canRunFocusFailure) {
2580                 consumePendingUpdateOperations();
2581                 final View focusedItemView = findContainingItemView(focused);
2582                 if (focusedItemView == null) {
2583                     // panic, focused view is not a child anymore, cannot call super.
2584                     return null;
2585                 }
2586                 startInterceptRequestLayout();
2587                 result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2588                 stopInterceptRequestLayout(false);
2589             }
2590         }
2591         if (result != null && !result.hasFocusable()) {
2592             if (getFocusedChild() == null) {
2593                 // Scrolling to this unfocusable view is not meaningful since there is no currently
2594                 // focused view which RV needs to keep visible.
2595                 return super.focusSearch(focused, direction);
2596             }
2597             // If the next view returned by onFocusSearchFailed in layout manager has no focusable
2598             // views, we still scroll to that view in order to make it visible on the screen.
2599             // If it's focusable, framework already calls RV's requestChildFocus which handles
2600             // bringing this newly focused item onto the screen.
2601             requestChildOnScreen(result, null);
2602             return focused;
2603         }
2604         return isPreferredNextFocus(focused, result, direction)
2605                 ? result : super.focusSearch(focused, direction);
2606     }
2607 
2608     /**
2609      * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
2610      * assign it as the next focus View instead of letting view hierarchy decide.
2611      * A good candidate means a View that is aligned in the focus direction wrt the focused View
2612      * and is not the RecyclerView itself.
2613      * When this method returns false, RecyclerView will let the parent make the decision so the
2614      * same View may still get the focus as a result of that search.
2615      */
isPreferredNextFocus(View focused, View next, int direction)2616     private boolean isPreferredNextFocus(View focused, View next, int direction) {
2617         if (next == null || next == this) {
2618             return false;
2619         }
2620         // panic, result view is not a child anymore, maybe workaround b/37864393
2621         if (findContainingItemView(next) == null) {
2622             return false;
2623         }
2624         if (focused == null) {
2625             return true;
2626         }
2627         // panic, focused view is not a child anymore, maybe workaround b/37864393
2628         if (findContainingItemView(focused) == null) {
2629             return true;
2630         }
2631 
2632         mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2633         mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
2634         offsetDescendantRectToMyCoords(focused, mTempRect);
2635         offsetDescendantRectToMyCoords(next, mTempRect2);
2636         final int rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL ? -1 : 1;
2637         int rightness = 0;
2638         if ((mTempRect.left < mTempRect2.left
2639                 || mTempRect.right <= mTempRect2.left)
2640                 && mTempRect.right < mTempRect2.right) {
2641             rightness = 1;
2642         } else if ((mTempRect.right > mTempRect2.right
2643                 || mTempRect.left >= mTempRect2.right)
2644                 && mTempRect.left > mTempRect2.left) {
2645             rightness = -1;
2646         }
2647         int downness = 0;
2648         if ((mTempRect.top < mTempRect2.top
2649                 || mTempRect.bottom <= mTempRect2.top)
2650                 && mTempRect.bottom < mTempRect2.bottom) {
2651             downness = 1;
2652         } else if ((mTempRect.bottom > mTempRect2.bottom
2653                 || mTempRect.top >= mTempRect2.bottom)
2654                 && mTempRect.top > mTempRect2.top) {
2655             downness = -1;
2656         }
2657         switch (direction) {
2658             case View.FOCUS_LEFT:
2659                 return rightness < 0;
2660             case View.FOCUS_RIGHT:
2661                 return rightness > 0;
2662             case View.FOCUS_UP:
2663                 return downness < 0;
2664             case View.FOCUS_DOWN:
2665                 return downness > 0;
2666             case View.FOCUS_FORWARD:
2667                 return downness > 0 || (downness == 0 && rightness * rtl >= 0);
2668             case View.FOCUS_BACKWARD:
2669                 return downness < 0 || (downness == 0 && rightness * rtl <= 0);
2670         }
2671         throw new IllegalArgumentException("Invalid direction: " + direction + exceptionLabel());
2672     }
2673 
2674     @Override
requestChildFocus(View child, View focused)2675     public void requestChildFocus(View child, View focused) {
2676         if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
2677             requestChildOnScreen(child, focused);
2678         }
2679         super.requestChildFocus(child, focused);
2680     }
2681 
2682     /**
2683      * Requests that the given child of the RecyclerView be positioned onto the screen. This method
2684      * can be called for both unfocusable and focusable child views. For unfocusable child views,
2685      * the {@param focused} parameter passed is null, whereas for a focusable child, this parameter
2686      * indicates the actual descendant view within this child view that holds the focus.
2687      * @param child The child view of this RecyclerView that wants to come onto the screen.
2688      * @param focused The descendant view that actually has the focus if child is focusable, null
2689      *                otherwise.
2690      */
requestChildOnScreen(@onNull View child, @Nullable View focused)2691     private void requestChildOnScreen(@NonNull View child, @Nullable View focused) {
2692         View rectView = (focused != null) ? focused : child;
2693         mTempRect.set(0, 0, rectView.getWidth(), rectView.getHeight());
2694 
2695         // get item decor offsets w/o refreshing. If they are invalid, there will be another
2696         // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
2697         // View in viewport.
2698         final ViewGroup.LayoutParams focusedLayoutParams = rectView.getLayoutParams();
2699         if (focusedLayoutParams instanceof LayoutParams) {
2700             // if focused child has item decors, use them. Otherwise, ignore.
2701             final LayoutParams lp = (LayoutParams) focusedLayoutParams;
2702             if (!lp.mInsetsDirty) {
2703                 final Rect insets = lp.mDecorInsets;
2704                 mTempRect.left -= insets.left;
2705                 mTempRect.right += insets.right;
2706                 mTempRect.top -= insets.top;
2707                 mTempRect.bottom += insets.bottom;
2708             }
2709         }
2710 
2711         if (focused != null) {
2712             offsetDescendantRectToMyCoords(focused, mTempRect);
2713             offsetRectIntoDescendantCoords(child, mTempRect);
2714         }
2715         mLayout.requestChildRectangleOnScreen(this, child, mTempRect, !mFirstLayoutComplete,
2716                 (focused == null));
2717     }
2718 
2719     @Override
requestChildRectangleOnScreen(View child, Rect rect, boolean immediate)2720     public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
2721         return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
2722     }
2723 
2724     @Override
addFocusables(ArrayList<View> views, int direction, int focusableMode)2725     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
2726         if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
2727             super.addFocusables(views, direction, focusableMode);
2728         }
2729     }
2730 
2731     @Override
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)2732     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
2733         if (isComputingLayout()) {
2734             // if we are in the middle of a layout calculation, don't let any child take focus.
2735             // RV will handle it after layout calculation is finished.
2736             return false;
2737         }
2738         return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
2739     }
2740 
2741     @Override
onAttachedToWindow()2742     protected void onAttachedToWindow() {
2743         super.onAttachedToWindow();
2744         mLayoutOrScrollCounter = 0;
2745         mIsAttached = true;
2746         mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
2747         if (mLayout != null) {
2748             mLayout.dispatchAttachedToWindow(this);
2749         }
2750         mPostedAnimatorRunner = false;
2751 
2752         if (ALLOW_THREAD_GAP_WORK) {
2753             // Register with gap worker
2754             mGapWorker = GapWorker.sGapWorker.get();
2755             if (mGapWorker == null) {
2756                 mGapWorker = new GapWorker();
2757 
2758                 // break 60 fps assumption if data from display appears valid
2759                 // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
2760                 Display display = ViewCompat.getDisplay(this);
2761                 float refreshRate = 60.0f;
2762                 if (!isInEditMode() && display != null) {
2763                     float displayRefreshRate = display.getRefreshRate();
2764                     if (displayRefreshRate >= 30.0f) {
2765                         refreshRate = displayRefreshRate;
2766                     }
2767                 }
2768                 mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
2769                 GapWorker.sGapWorker.set(mGapWorker);
2770             }
2771             mGapWorker.add(this);
2772         }
2773     }
2774 
2775     @Override
onDetachedFromWindow()2776     protected void onDetachedFromWindow() {
2777         super.onDetachedFromWindow();
2778         if (mItemAnimator != null) {
2779             mItemAnimator.endAnimations();
2780         }
2781         stopScroll();
2782         mIsAttached = false;
2783         if (mLayout != null) {
2784             mLayout.dispatchDetachedFromWindow(this, mRecycler);
2785         }
2786         mPendingAccessibilityImportanceChange.clear();
2787         removeCallbacks(mItemAnimatorRunner);
2788         mViewInfoStore.onDetach();
2789 
2790         if (ALLOW_THREAD_GAP_WORK && mGapWorker != null) {
2791             // Unregister with gap worker
2792             mGapWorker.remove(this);
2793             mGapWorker = null;
2794         }
2795     }
2796 
2797     /**
2798      * Returns true if RecyclerView is attached to window.
2799      */
2800     @Override
isAttachedToWindow()2801     public boolean isAttachedToWindow() {
2802         return mIsAttached;
2803     }
2804 
2805     /**
2806      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2807      * {@link IllegalStateException} if it <b>is not</b>.
2808      *
2809      * @param message The message for the exception. Can be null.
2810      * @see #assertNotInLayoutOrScroll(String)
2811      */
assertInLayoutOrScroll(String message)2812     void assertInLayoutOrScroll(String message) {
2813         if (!isComputingLayout()) {
2814             if (message == null) {
2815                 throw new IllegalStateException("Cannot call this method unless RecyclerView is "
2816                         + "computing a layout or scrolling" + exceptionLabel());
2817             }
2818             throw new IllegalStateException(message + exceptionLabel());
2819 
2820         }
2821     }
2822 
2823     /**
2824      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2825      * {@link IllegalStateException} if it <b>is</b>.
2826      *
2827      * @param message The message for the exception. Can be null.
2828      * @see #assertInLayoutOrScroll(String)
2829      */
assertNotInLayoutOrScroll(String message)2830     void assertNotInLayoutOrScroll(String message) {
2831         if (isComputingLayout()) {
2832             if (message == null) {
2833                 throw new IllegalStateException("Cannot call this method while RecyclerView is "
2834                         + "computing a layout or scrolling" + exceptionLabel());
2835             }
2836             throw new IllegalStateException(message);
2837         }
2838         if (mDispatchScrollCounter > 0) {
2839             Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might"
2840                             + "be run during a measure & layout pass where you cannot change the"
2841                             + "RecyclerView data. Any method call that might change the structure"
2842                             + "of the RecyclerView or the adapter contents should be postponed to"
2843                             + "the next frame.",
2844                     new IllegalStateException("" + exceptionLabel()));
2845         }
2846     }
2847 
2848     /**
2849      * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
2850      * to child views or this view's standard scrolling behavior.
2851      *
2852      * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
2853      * returns true from
2854      * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
2855      * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
2856      * for each incoming MotionEvent until the end of the gesture.</p>
2857      *
2858      * @param listener Listener to add
2859      * @see SimpleOnItemTouchListener
2860      */
addOnItemTouchListener(@onNull OnItemTouchListener listener)2861     public void addOnItemTouchListener(@NonNull OnItemTouchListener listener) {
2862         mOnItemTouchListeners.add(listener);
2863     }
2864 
2865     /**
2866      * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
2867      *
2868      * @param listener Listener to remove
2869      */
removeOnItemTouchListener(@onNull OnItemTouchListener listener)2870     public void removeOnItemTouchListener(@NonNull OnItemTouchListener listener) {
2871         mOnItemTouchListeners.remove(listener);
2872         if (mActiveOnItemTouchListener == listener) {
2873             mActiveOnItemTouchListener = null;
2874         }
2875     }
2876 
dispatchOnItemTouchIntercept(MotionEvent e)2877     private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
2878         final int action = e.getAction();
2879         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
2880             mActiveOnItemTouchListener = null;
2881         }
2882 
2883         final int listenerCount = mOnItemTouchListeners.size();
2884         for (int i = 0; i < listenerCount; i++) {
2885             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2886             if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
2887                 mActiveOnItemTouchListener = listener;
2888                 return true;
2889             }
2890         }
2891         return false;
2892     }
2893 
dispatchOnItemTouch(MotionEvent e)2894     private boolean dispatchOnItemTouch(MotionEvent e) {
2895         final int action = e.getAction();
2896         if (mActiveOnItemTouchListener != null) {
2897             if (action == MotionEvent.ACTION_DOWN) {
2898                 // Stale state from a previous gesture, we're starting a new one. Clear it.
2899                 mActiveOnItemTouchListener = null;
2900             } else {
2901                 mActiveOnItemTouchListener.onTouchEvent(this, e);
2902                 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
2903                     // Clean up for the next gesture.
2904                     mActiveOnItemTouchListener = null;
2905                 }
2906                 return true;
2907             }
2908         }
2909 
2910         // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
2911         // as called from onInterceptTouchEvent; skip it.
2912         if (action != MotionEvent.ACTION_DOWN) {
2913             final int listenerCount = mOnItemTouchListeners.size();
2914             for (int i = 0; i < listenerCount; i++) {
2915                 final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2916                 if (listener.onInterceptTouchEvent(this, e)) {
2917                     mActiveOnItemTouchListener = listener;
2918                     return true;
2919                 }
2920             }
2921         }
2922         return false;
2923     }
2924 
2925     @Override
onInterceptTouchEvent(MotionEvent e)2926     public boolean onInterceptTouchEvent(MotionEvent e) {
2927         if (mLayoutFrozen) {
2928             // When layout is frozen,  RV does not intercept the motion event.
2929             // A child view e.g. a button may still get the click.
2930             return false;
2931         }
2932         if (dispatchOnItemTouchIntercept(e)) {
2933             cancelTouch();
2934             return true;
2935         }
2936 
2937         if (mLayout == null) {
2938             return false;
2939         }
2940 
2941         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2942         final boolean canScrollVertically = mLayout.canScrollVertically();
2943 
2944         if (mVelocityTracker == null) {
2945             mVelocityTracker = VelocityTracker.obtain();
2946         }
2947         mVelocityTracker.addMovement(e);
2948 
2949         final int action = e.getActionMasked();
2950         final int actionIndex = e.getActionIndex();
2951 
2952         switch (action) {
2953             case MotionEvent.ACTION_DOWN:
2954                 if (mIgnoreMotionEventTillDown) {
2955                     mIgnoreMotionEventTillDown = false;
2956                 }
2957                 mScrollPointerId = e.getPointerId(0);
2958                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2959                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2960 
2961                 if (mScrollState == SCROLL_STATE_SETTLING) {
2962                     getParent().requestDisallowInterceptTouchEvent(true);
2963                     setScrollState(SCROLL_STATE_DRAGGING);
2964                 }
2965 
2966                 // Clear the nested offsets
2967                 mNestedOffsets[0] = mNestedOffsets[1] = 0;
2968 
2969                 int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2970                 if (canScrollHorizontally) {
2971                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2972                 }
2973                 if (canScrollVertically) {
2974                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2975                 }
2976                 startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
2977                 break;
2978 
2979             case MotionEvent.ACTION_POINTER_DOWN:
2980                 mScrollPointerId = e.getPointerId(actionIndex);
2981                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2982                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2983                 break;
2984 
2985             case MotionEvent.ACTION_MOVE: {
2986                 final int index = e.findPointerIndex(mScrollPointerId);
2987                 if (index < 0) {
2988                     Log.e(TAG, "Error processing scroll; pointer index for id "
2989                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2990                     return false;
2991                 }
2992 
2993                 final int x = (int) (e.getX(index) + 0.5f);
2994                 final int y = (int) (e.getY(index) + 0.5f);
2995                 if (mScrollState != SCROLL_STATE_DRAGGING) {
2996                     final int dx = x - mInitialTouchX;
2997                     final int dy = y - mInitialTouchY;
2998                     boolean startScroll = false;
2999                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
3000                         mLastTouchX = x;
3001                         startScroll = true;
3002                     }
3003                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
3004                         mLastTouchY = y;
3005                         startScroll = true;
3006                     }
3007                     if (startScroll) {
3008                         setScrollState(SCROLL_STATE_DRAGGING);
3009                     }
3010                 }
3011             } break;
3012 
3013             case MotionEvent.ACTION_POINTER_UP: {
3014                 onPointerUp(e);
3015             } break;
3016 
3017             case MotionEvent.ACTION_UP: {
3018                 mVelocityTracker.clear();
3019                 stopNestedScroll(TYPE_TOUCH);
3020             } break;
3021 
3022             case MotionEvent.ACTION_CANCEL: {
3023                 cancelTouch();
3024             }
3025         }
3026         return mScrollState == SCROLL_STATE_DRAGGING;
3027     }
3028 
3029     @Override
requestDisallowInterceptTouchEvent(boolean disallowIntercept)3030     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
3031         final int listenerCount = mOnItemTouchListeners.size();
3032         for (int i = 0; i < listenerCount; i++) {
3033             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
3034             listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
3035         }
3036         super.requestDisallowInterceptTouchEvent(disallowIntercept);
3037     }
3038 
3039     @Override
onTouchEvent(MotionEvent e)3040     public boolean onTouchEvent(MotionEvent e) {
3041         if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
3042             return false;
3043         }
3044         if (dispatchOnItemTouch(e)) {
3045             cancelTouch();
3046             return true;
3047         }
3048 
3049         if (mLayout == null) {
3050             return false;
3051         }
3052 
3053         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
3054         final boolean canScrollVertically = mLayout.canScrollVertically();
3055 
3056         if (mVelocityTracker == null) {
3057             mVelocityTracker = VelocityTracker.obtain();
3058         }
3059         boolean eventAddedToVelocityTracker = false;
3060 
3061         final MotionEvent vtev = MotionEvent.obtain(e);
3062         final int action = e.getActionMasked();
3063         final int actionIndex = e.getActionIndex();
3064 
3065         if (action == MotionEvent.ACTION_DOWN) {
3066             mNestedOffsets[0] = mNestedOffsets[1] = 0;
3067         }
3068         vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
3069 
3070         switch (action) {
3071             case MotionEvent.ACTION_DOWN: {
3072                 mScrollPointerId = e.getPointerId(0);
3073                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
3074                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
3075 
3076                 int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
3077                 if (canScrollHorizontally) {
3078                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
3079                 }
3080                 if (canScrollVertically) {
3081                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
3082                 }
3083                 startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
3084             } break;
3085 
3086             case MotionEvent.ACTION_POINTER_DOWN: {
3087                 mScrollPointerId = e.getPointerId(actionIndex);
3088                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
3089                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
3090             } break;
3091 
3092             case MotionEvent.ACTION_MOVE: {
3093                 final int index = e.findPointerIndex(mScrollPointerId);
3094                 if (index < 0) {
3095                     Log.e(TAG, "Error processing scroll; pointer index for id "
3096                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
3097                     return false;
3098                 }
3099 
3100                 final int x = (int) (e.getX(index) + 0.5f);
3101                 final int y = (int) (e.getY(index) + 0.5f);
3102                 int dx = mLastTouchX - x;
3103                 int dy = mLastTouchY - y;
3104 
3105                 if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset, TYPE_TOUCH)) {
3106                     dx -= mScrollConsumed[0];
3107                     dy -= mScrollConsumed[1];
3108                     vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
3109                     // Updated the nested offsets
3110                     mNestedOffsets[0] += mScrollOffset[0];
3111                     mNestedOffsets[1] += mScrollOffset[1];
3112                 }
3113 
3114                 if (mScrollState != SCROLL_STATE_DRAGGING) {
3115                     boolean startScroll = false;
3116                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
3117                         if (dx > 0) {
3118                             dx -= mTouchSlop;
3119                         } else {
3120                             dx += mTouchSlop;
3121                         }
3122                         startScroll = true;
3123                     }
3124                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
3125                         if (dy > 0) {
3126                             dy -= mTouchSlop;
3127                         } else {
3128                             dy += mTouchSlop;
3129                         }
3130                         startScroll = true;
3131                     }
3132                     if (startScroll) {
3133                         setScrollState(SCROLL_STATE_DRAGGING);
3134                     }
3135                 }
3136 
3137                 if (mScrollState == SCROLL_STATE_DRAGGING) {
3138                     mLastTouchX = x - mScrollOffset[0];
3139                     mLastTouchY = y - mScrollOffset[1];
3140 
3141                     if (scrollByInternal(
3142                             canScrollHorizontally ? dx : 0,
3143                             canScrollVertically ? dy : 0,
3144                             vtev)) {
3145                         getParent().requestDisallowInterceptTouchEvent(true);
3146                     }
3147                     if (mGapWorker != null && (dx != 0 || dy != 0)) {
3148                         mGapWorker.postFromTraversal(this, dx, dy);
3149                     }
3150                 }
3151             } break;
3152 
3153             case MotionEvent.ACTION_POINTER_UP: {
3154                 onPointerUp(e);
3155             } break;
3156 
3157             case MotionEvent.ACTION_UP: {
3158                 mVelocityTracker.addMovement(vtev);
3159                 eventAddedToVelocityTracker = true;
3160                 mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
3161                 final float xvel = canScrollHorizontally
3162                         ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
3163                 final float yvel = canScrollVertically
3164                         ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
3165                 if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
3166                     setScrollState(SCROLL_STATE_IDLE);
3167                 }
3168                 resetTouch();
3169             } break;
3170 
3171             case MotionEvent.ACTION_CANCEL: {
3172                 cancelTouch();
3173             } break;
3174         }
3175 
3176         if (!eventAddedToVelocityTracker) {
3177             mVelocityTracker.addMovement(vtev);
3178         }
3179         vtev.recycle();
3180 
3181         return true;
3182     }
3183 
resetTouch()3184     private void resetTouch() {
3185         if (mVelocityTracker != null) {
3186             mVelocityTracker.clear();
3187         }
3188         stopNestedScroll(TYPE_TOUCH);
3189         releaseGlows();
3190     }
3191 
cancelTouch()3192     private void cancelTouch() {
3193         resetTouch();
3194         setScrollState(SCROLL_STATE_IDLE);
3195     }
3196 
onPointerUp(MotionEvent e)3197     private void onPointerUp(MotionEvent e) {
3198         final int actionIndex = e.getActionIndex();
3199         if (e.getPointerId(actionIndex) == mScrollPointerId) {
3200             // Pick a new pointer to pick up the slack.
3201             final int newIndex = actionIndex == 0 ? 1 : 0;
3202             mScrollPointerId = e.getPointerId(newIndex);
3203             mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
3204             mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
3205         }
3206     }
3207 
3208     @Override
onGenericMotionEvent(MotionEvent event)3209     public boolean onGenericMotionEvent(MotionEvent event) {
3210         if (mLayout == null) {
3211             return false;
3212         }
3213         if (mLayoutFrozen) {
3214             return false;
3215         }
3216         if (event.getAction() == MotionEventCompat.ACTION_SCROLL) {
3217             final float vScroll, hScroll;
3218             if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
3219                 if (mLayout.canScrollVertically()) {
3220                     // Inverse the sign of the vertical scroll to align the scroll orientation
3221                     // with AbsListView.
3222                     vScroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
3223                 } else {
3224                     vScroll = 0f;
3225                 }
3226                 if (mLayout.canScrollHorizontally()) {
3227                     hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
3228                 } else {
3229                     hScroll = 0f;
3230                 }
3231             } else if ((event.getSource() & InputDeviceCompat.SOURCE_ROTARY_ENCODER) != 0) {
3232                 final float axisScroll = event.getAxisValue(MotionEventCompat.AXIS_SCROLL);
3233                 if (mLayout.canScrollVertically()) {
3234                     // Invert the sign of the vertical scroll to align the scroll orientation
3235                     // with AbsListView.
3236                     vScroll = -axisScroll;
3237                     hScroll = 0f;
3238                 } else if (mLayout.canScrollHorizontally()) {
3239                     vScroll = 0f;
3240                     hScroll = axisScroll;
3241                 } else {
3242                     vScroll = 0f;
3243                     hScroll = 0f;
3244                 }
3245             } else {
3246                 vScroll = 0f;
3247                 hScroll = 0f;
3248             }
3249 
3250             if (vScroll != 0 || hScroll != 0) {
3251                 scrollByInternal((int) (hScroll * mScaledHorizontalScrollFactor),
3252                         (int) (vScroll * mScaledVerticalScrollFactor), event);
3253             }
3254         }
3255         return false;
3256     }
3257 
3258     @Override
onMeasure(int widthSpec, int heightSpec)3259     protected void onMeasure(int widthSpec, int heightSpec) {
3260         if (mLayout == null) {
3261             defaultOnMeasure(widthSpec, heightSpec);
3262             return;
3263         }
3264         if (mLayout.isAutoMeasureEnabled()) {
3265             final int widthMode = MeasureSpec.getMode(widthSpec);
3266             final int heightMode = MeasureSpec.getMode(heightSpec);
3267 
3268             /**
3269              * This specific call should be considered deprecated and replaced with
3270              * {@link #defaultOnMeasure(int, int)}. It can't actually be replaced as it could
3271              * break existing third party code but all documentation directs developers to not
3272              * override {@link LayoutManager#onMeasure(int, int)} when
3273              * {@link LayoutManager#isAutoMeasureEnabled()} returns true.
3274              */
3275             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3276 
3277             final boolean measureSpecModeIsExactly =
3278                     widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
3279             if (measureSpecModeIsExactly || mAdapter == null) {
3280                 return;
3281             }
3282 
3283             if (mState.mLayoutStep == State.STEP_START) {
3284                 dispatchLayoutStep1();
3285             }
3286             // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
3287             // consistency
3288             mLayout.setMeasureSpecs(widthSpec, heightSpec);
3289             mState.mIsMeasuring = true;
3290             dispatchLayoutStep2();
3291 
3292             // now we can get the width and height from the children.
3293             mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
3294 
3295             // if RecyclerView has non-exact width and height and if there is at least one child
3296             // which also has non-exact width & height, we have to re-measure.
3297             if (mLayout.shouldMeasureTwice()) {
3298                 mLayout.setMeasureSpecs(
3299                         MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
3300                         MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
3301                 mState.mIsMeasuring = true;
3302                 dispatchLayoutStep2();
3303                 // now we can get the width and height from the children.
3304                 mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
3305             }
3306         } else {
3307             if (mHasFixedSize) {
3308                 mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3309                 return;
3310             }
3311             // custom onMeasure
3312             if (mAdapterUpdateDuringMeasure) {
3313                 startInterceptRequestLayout();
3314                 onEnterLayoutOrScroll();
3315                 processAdapterUpdatesAndSetAnimationFlags();
3316                 onExitLayoutOrScroll();
3317 
3318                 if (mState.mRunPredictiveAnimations) {
3319                     mState.mInPreLayout = true;
3320                 } else {
3321                     // consume remaining updates to provide a consistent state with the layout pass.
3322                     mAdapterHelper.consumeUpdatesInOnePass();
3323                     mState.mInPreLayout = false;
3324                 }
3325                 mAdapterUpdateDuringMeasure = false;
3326                 stopInterceptRequestLayout(false);
3327             } else if (mState.mRunPredictiveAnimations) {
3328                 // If mAdapterUpdateDuringMeasure is false and mRunPredictiveAnimations is true:
3329                 // this means there is already an onMeasure() call performed to handle the pending
3330                 // adapter change, two onMeasure() calls can happen if RV is a child of LinearLayout
3331                 // with layout_width=MATCH_PARENT. RV cannot call LM.onMeasure() second time
3332                 // because getViewForPosition() will crash when LM uses a child to measure.
3333                 setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
3334                 return;
3335             }
3336 
3337             if (mAdapter != null) {
3338                 mState.mItemCount = mAdapter.getItemCount();
3339             } else {
3340                 mState.mItemCount = 0;
3341             }
3342             startInterceptRequestLayout();
3343             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3344             stopInterceptRequestLayout(false);
3345             mState.mInPreLayout = false; // clear
3346         }
3347     }
3348 
3349     /**
3350      * An implementation of {@link View#onMeasure(int, int)} to fall back to in various scenarios
3351      * where this RecyclerView is otherwise lacking better information.
3352      */
defaultOnMeasure(int widthSpec, int heightSpec)3353     void defaultOnMeasure(int widthSpec, int heightSpec) {
3354         // calling LayoutManager here is not pretty but that API is already public and it is better
3355         // than creating another method since this is internal.
3356         final int width = LayoutManager.chooseSize(widthSpec,
3357                 getPaddingLeft() + getPaddingRight(),
3358                 ViewCompat.getMinimumWidth(this));
3359         final int height = LayoutManager.chooseSize(heightSpec,
3360                 getPaddingTop() + getPaddingBottom(),
3361                 ViewCompat.getMinimumHeight(this));
3362 
3363         setMeasuredDimension(width, height);
3364     }
3365 
3366     @Override
onSizeChanged(int w, int h, int oldw, int oldh)3367     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
3368         super.onSizeChanged(w, h, oldw, oldh);
3369         if (w != oldw || h != oldh) {
3370             invalidateGlows();
3371             // layout's w/h are updated during measure/layout steps.
3372         }
3373     }
3374 
3375     /**
3376      * Sets the {@link ItemAnimator} that will handle animations involving changes
3377      * to the items in this RecyclerView. By default, RecyclerView instantiates and
3378      * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
3379      * enabled for the RecyclerView depends on the ItemAnimator and whether
3380      * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
3381      * supports item animations}.
3382      *
3383      * @param animator The ItemAnimator being set. If null, no animations will occur
3384      * when changes occur to the items in this RecyclerView.
3385      */
setItemAnimator(@ullable ItemAnimator animator)3386     public void setItemAnimator(@Nullable ItemAnimator animator) {
3387         if (mItemAnimator != null) {
3388             mItemAnimator.endAnimations();
3389             mItemAnimator.setListener(null);
3390         }
3391         mItemAnimator = animator;
3392         if (mItemAnimator != null) {
3393             mItemAnimator.setListener(mItemAnimatorListener);
3394         }
3395     }
3396 
onEnterLayoutOrScroll()3397     void onEnterLayoutOrScroll() {
3398         mLayoutOrScrollCounter++;
3399     }
3400 
onExitLayoutOrScroll()3401     void onExitLayoutOrScroll() {
3402         onExitLayoutOrScroll(true);
3403     }
3404 
onExitLayoutOrScroll(boolean enableChangeEvents)3405     void onExitLayoutOrScroll(boolean enableChangeEvents) {
3406         mLayoutOrScrollCounter--;
3407         if (mLayoutOrScrollCounter < 1) {
3408             if (DEBUG && mLayoutOrScrollCounter < 0) {
3409                 throw new IllegalStateException("layout or scroll counter cannot go below zero."
3410                         + "Some calls are not matching" + exceptionLabel());
3411             }
3412             mLayoutOrScrollCounter = 0;
3413             if (enableChangeEvents) {
3414                 dispatchContentChangedIfNecessary();
3415                 dispatchPendingImportantForAccessibilityChanges();
3416             }
3417         }
3418     }
3419 
isAccessibilityEnabled()3420     boolean isAccessibilityEnabled() {
3421         return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
3422     }
3423 
dispatchContentChangedIfNecessary()3424     private void dispatchContentChangedIfNecessary() {
3425         final int flags = mEatenAccessibilityChangeFlags;
3426         mEatenAccessibilityChangeFlags = 0;
3427         if (flags != 0 && isAccessibilityEnabled()) {
3428             final AccessibilityEvent event = AccessibilityEvent.obtain();
3429             event.setEventType(AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
3430             AccessibilityEventCompat.setContentChangeTypes(event, flags);
3431             sendAccessibilityEventUnchecked(event);
3432         }
3433     }
3434 
3435     /**
3436      * Returns whether RecyclerView is currently computing a layout.
3437      * <p>
3438      * If this method returns true, it means that RecyclerView is in a lockdown state and any
3439      * attempt to update adapter contents will result in an exception because adapter contents
3440      * cannot be changed while RecyclerView is trying to compute the layout.
3441      * <p>
3442      * It is very unlikely that your code will be running during this state as it is
3443      * called by the framework when a layout traversal happens or RecyclerView starts to scroll
3444      * in response to system events (touch, accessibility etc).
3445      * <p>
3446      * This case may happen if you have some custom logic to change adapter contents in
3447      * response to a View callback (e.g. focus change callback) which might be triggered during a
3448      * layout calculation. In these cases, you should just postpone the change using a Handler or a
3449      * similar mechanism.
3450      *
3451      * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
3452      *         otherwise
3453      */
isComputingLayout()3454     public boolean isComputingLayout() {
3455         return mLayoutOrScrollCounter > 0;
3456     }
3457 
3458     /**
3459      * Returns true if an accessibility event should not be dispatched now. This happens when an
3460      * accessibility request arrives while RecyclerView does not have a stable state which is very
3461      * hard to handle for a LayoutManager. Instead, this method records necessary information about
3462      * the event and dispatches a window change event after the critical section is finished.
3463      *
3464      * @return True if the accessibility event should be postponed.
3465      */
shouldDeferAccessibilityEvent(AccessibilityEvent event)3466     boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
3467         if (isComputingLayout()) {
3468             int type = 0;
3469             if (event != null) {
3470                 type = AccessibilityEventCompat.getContentChangeTypes(event);
3471             }
3472             if (type == 0) {
3473                 type = AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED;
3474             }
3475             mEatenAccessibilityChangeFlags |= type;
3476             return true;
3477         }
3478         return false;
3479     }
3480 
3481     @Override
sendAccessibilityEventUnchecked(AccessibilityEvent event)3482     public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
3483         if (shouldDeferAccessibilityEvent(event)) {
3484             return;
3485         }
3486         super.sendAccessibilityEventUnchecked(event);
3487     }
3488 
3489     /**
3490      * Gets the current ItemAnimator for this RecyclerView. A null return value
3491      * indicates that there is no animator and that item changes will happen without
3492      * any animations. By default, RecyclerView instantiates and
3493      * uses an instance of {@link DefaultItemAnimator}.
3494      *
3495      * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
3496      * when changes occur to the items in this RecyclerView.
3497      */
3498     @Nullable
getItemAnimator()3499     public ItemAnimator getItemAnimator() {
3500         return mItemAnimator;
3501     }
3502 
3503     /**
3504      * Post a runnable to the next frame to run pending item animations. Only the first such
3505      * request will be posted, governed by the mPostedAnimatorRunner flag.
3506      */
postAnimationRunner()3507     void postAnimationRunner() {
3508         if (!mPostedAnimatorRunner && mIsAttached) {
3509             ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
3510             mPostedAnimatorRunner = true;
3511         }
3512     }
3513 
predictiveItemAnimationsEnabled()3514     private boolean predictiveItemAnimationsEnabled() {
3515         return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
3516     }
3517 
3518     /**
3519      * Consumes adapter updates and calculates which type of animations we want to run.
3520      * Called in onMeasure and dispatchLayout.
3521      * <p>
3522      * This method may process only the pre-layout state of updates or all of them.
3523      */
processAdapterUpdatesAndSetAnimationFlags()3524     private void processAdapterUpdatesAndSetAnimationFlags() {
3525         if (mDataSetHasChangedAfterLayout) {
3526             // Processing these items have no value since data set changed unexpectedly.
3527             // Instead, we just reset it.
3528             mAdapterHelper.reset();
3529             if (mDispatchItemsChangedEvent) {
3530                 mLayout.onItemsChanged(this);
3531             }
3532         }
3533         // simple animations are a subset of advanced animations (which will cause a
3534         // pre-layout step)
3535         // If layout supports predictive animations, pre-process to decide if we want to run them
3536         if (predictiveItemAnimationsEnabled()) {
3537             mAdapterHelper.preProcess();
3538         } else {
3539             mAdapterHelper.consumeUpdatesInOnePass();
3540         }
3541         boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
3542         mState.mRunSimpleAnimations = mFirstLayoutComplete
3543                 && mItemAnimator != null
3544                 && (mDataSetHasChangedAfterLayout
3545                 || animationTypeSupported
3546                 || mLayout.mRequestedSimpleAnimations)
3547                 && (!mDataSetHasChangedAfterLayout
3548                 || mAdapter.hasStableIds());
3549         mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
3550                 && animationTypeSupported
3551                 && !mDataSetHasChangedAfterLayout
3552                 && predictiveItemAnimationsEnabled();
3553     }
3554 
3555     /**
3556      * Wrapper around layoutChildren() that handles animating changes caused by layout.
3557      * Animations work on the assumption that there are five different kinds of items
3558      * in play:
3559      * PERSISTENT: items are visible before and after layout
3560      * REMOVED: items were visible before layout and were removed by the app
3561      * ADDED: items did not exist before layout and were added by the app
3562      * DISAPPEARING: items exist in the data set before/after, but changed from
3563      * visible to non-visible in the process of layout (they were moved off
3564      * screen as a side-effect of other changes)
3565      * APPEARING: items exist in the data set before/after, but changed from
3566      * non-visible to visible in the process of layout (they were moved on
3567      * screen as a side-effect of other changes)
3568      * The overall approach figures out what items exist before/after layout and
3569      * infers one of the five above states for each of the items. Then the animations
3570      * are set up accordingly:
3571      * PERSISTENT views are animated via
3572      * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3573      * DISAPPEARING views are animated via
3574      * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3575      * APPEARING views are animated via
3576      * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3577      * and changed views are animated via
3578      * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
3579      */
dispatchLayout()3580     void dispatchLayout() {
3581         if (mAdapter == null) {
3582             Log.e(TAG, "No adapter attached; skipping layout");
3583             // leave the state in START
3584             return;
3585         }
3586         if (mLayout == null) {
3587             Log.e(TAG, "No layout manager attached; skipping layout");
3588             // leave the state in START
3589             return;
3590         }
3591         mState.mIsMeasuring = false;
3592         if (mState.mLayoutStep == State.STEP_START) {
3593             dispatchLayoutStep1();
3594             mLayout.setExactMeasureSpecsFrom(this);
3595             dispatchLayoutStep2();
3596         } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
3597                 || mLayout.getHeight() != getHeight()) {
3598             // First 2 steps are done in onMeasure but looks like we have to run again due to
3599             // changed size.
3600             mLayout.setExactMeasureSpecsFrom(this);
3601             dispatchLayoutStep2();
3602         } else {
3603             // always make sure we sync them (to ensure mode is exact)
3604             mLayout.setExactMeasureSpecsFrom(this);
3605         }
3606         dispatchLayoutStep3();
3607     }
3608 
saveFocusInfo()3609     private void saveFocusInfo() {
3610         View child = null;
3611         if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
3612             child = getFocusedChild();
3613         }
3614 
3615         final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
3616         if (focusedVh == null) {
3617             resetFocusInfo();
3618         } else {
3619             mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
3620             // mFocusedItemPosition should hold the current adapter position of the previously
3621             // focused item. If the item is removed, we store the previous adapter position of the
3622             // removed item.
3623             mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION
3624                     : (focusedVh.isRemoved() ? focusedVh.mOldPosition
3625                             : focusedVh.getAdapterPosition());
3626             mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
3627         }
3628     }
3629 
resetFocusInfo()3630     private void resetFocusInfo() {
3631         mState.mFocusedItemId = NO_ID;
3632         mState.mFocusedItemPosition = NO_POSITION;
3633         mState.mFocusedSubChildId = View.NO_ID;
3634     }
3635 
3636     /**
3637      * Finds the best view candidate to request focus on using mFocusedItemPosition index of the
3638      * previously focused item. It first traverses the adapter forward to find a focusable candidate
3639      * and if no such candidate is found, it reverses the focus search direction for the items
3640      * before the mFocusedItemPosition'th index;
3641      * @return The best candidate to request focus on, or null if no such candidate exists. Null
3642      * indicates all the existing adapter items are unfocusable.
3643      */
3644     @Nullable
findNextViewToFocus()3645     private View findNextViewToFocus() {
3646         int startFocusSearchIndex = mState.mFocusedItemPosition != -1 ? mState.mFocusedItemPosition
3647                 : 0;
3648         ViewHolder nextFocus;
3649         final int itemCount = mState.getItemCount();
3650         for (int i = startFocusSearchIndex; i < itemCount; i++) {
3651             nextFocus = findViewHolderForAdapterPosition(i);
3652             if (nextFocus == null) {
3653                 break;
3654             }
3655             if (nextFocus.itemView.hasFocusable()) {
3656                 return nextFocus.itemView;
3657             }
3658         }
3659         final int limit = Math.min(itemCount, startFocusSearchIndex);
3660         for (int i = limit - 1; i >= 0; i--) {
3661             nextFocus = findViewHolderForAdapterPosition(i);
3662             if (nextFocus == null) {
3663                 return null;
3664             }
3665             if (nextFocus.itemView.hasFocusable()) {
3666                 return nextFocus.itemView;
3667             }
3668         }
3669         return null;
3670     }
3671 
recoverFocusFromState()3672     private void recoverFocusFromState() {
3673         if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()
3674                 || getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
3675                 || (getDescendantFocusability() == FOCUS_BEFORE_DESCENDANTS && isFocused())) {
3676             // No-op if either of these cases happens:
3677             // 1. RV has no focus, or 2. RV blocks focus to its children, or 3. RV takes focus
3678             // before its children and is focused (i.e. it already stole the focus away from its
3679             // descendants).
3680             return;
3681         }
3682         // only recover focus if RV itself has the focus or the focused view is hidden
3683         if (!isFocused()) {
3684             final View focusedChild = getFocusedChild();
3685             if (IGNORE_DETACHED_FOCUSED_CHILD
3686                     && (focusedChild.getParent() == null || !focusedChild.hasFocus())) {
3687                 // Special handling of API 15-. A focused child can be invalid because mFocus is not
3688                 // cleared when the child is detached (mParent = null),
3689                 // This happens because clearFocus on API 15- does not invalidate mFocus of its
3690                 // parent when this child is detached.
3691                 // For API 16+, this is not an issue because requestFocus takes care of clearing the
3692                 // prior detached focused child. For API 15- the problem happens in 2 cases because
3693                 // clearChild does not call clearChildFocus on RV: 1. setFocusable(false) is called
3694                 // for the current focused item which calls clearChild or 2. when the prior focused
3695                 // child is removed, removeDetachedView called in layout step 3 which calls
3696                 // clearChild. We should ignore this invalid focused child in all our calculations
3697                 // for the next view to receive focus, and apply the focus recovery logic instead.
3698                 if (mChildHelper.getChildCount() == 0) {
3699                     // No children left. Request focus on the RV itself since one of its children
3700                     // was holding focus previously.
3701                     requestFocus();
3702                     return;
3703                 }
3704             } else if (!mChildHelper.isHidden(focusedChild)) {
3705                 // If the currently focused child is hidden, apply the focus recovery logic.
3706                 // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
3707                 return;
3708             }
3709         }
3710         ViewHolder focusTarget = null;
3711         // RV first attempts to locate the previously focused item to request focus on using
3712         // mFocusedItemId. If such an item no longer exists, it then makes a best-effort attempt to
3713         // find the next best candidate to request focus on based on mFocusedItemPosition.
3714         if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
3715             focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
3716         }
3717         View viewToFocus = null;
3718         if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView)
3719                 || !focusTarget.itemView.hasFocusable()) {
3720             if (mChildHelper.getChildCount() > 0) {
3721                 // At this point, RV has focus and either of these conditions are true:
3722                 // 1. There's no previously focused item either because RV received focused before
3723                 // layout, or the previously focused item was removed, or RV doesn't have stable IDs
3724                 // 2. Previous focus child is hidden, or 3. Previous focused child is no longer
3725                 // focusable. In either of these cases, we make sure that RV still passes down the
3726                 // focus to one of its focusable children using a best-effort algorithm.
3727                 viewToFocus = findNextViewToFocus();
3728             }
3729         } else {
3730             // looks like the focused item has been replaced with another view that represents the
3731             // same item in the adapter. Request focus on that.
3732             viewToFocus = focusTarget.itemView;
3733         }
3734 
3735         if (viewToFocus != null) {
3736             if (mState.mFocusedSubChildId != NO_ID) {
3737                 View child = viewToFocus.findViewById(mState.mFocusedSubChildId);
3738                 if (child != null && child.isFocusable()) {
3739                     viewToFocus = child;
3740                 }
3741             }
3742             viewToFocus.requestFocus();
3743         }
3744     }
3745 
getDeepestFocusedViewWithId(View view)3746     private int getDeepestFocusedViewWithId(View view) {
3747         int lastKnownId = view.getId();
3748         while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
3749             view = ((ViewGroup) view).getFocusedChild();
3750             final int id = view.getId();
3751             if (id != View.NO_ID) {
3752                 lastKnownId = view.getId();
3753             }
3754         }
3755         return lastKnownId;
3756     }
3757 
fillRemainingScrollValues(State state)3758     final void fillRemainingScrollValues(State state) {
3759         if (getScrollState() == SCROLL_STATE_SETTLING) {
3760             final OverScroller scroller = mViewFlinger.mScroller;
3761             state.mRemainingScrollHorizontal = scroller.getFinalX() - scroller.getCurrX();
3762             state.mRemainingScrollVertical = scroller.getFinalY() - scroller.getCurrY();
3763         } else {
3764             state.mRemainingScrollHorizontal = 0;
3765             state.mRemainingScrollVertical = 0;
3766         }
3767     }
3768 
3769     /**
3770      * The first step of a layout where we;
3771      * - process adapter updates
3772      * - decide which animation should run
3773      * - save information about current views
3774      * - If necessary, run predictive layout and save its information
3775      */
dispatchLayoutStep1()3776     private void dispatchLayoutStep1() {
3777         mState.assertLayoutStep(State.STEP_START);
3778         fillRemainingScrollValues(mState);
3779         mState.mIsMeasuring = false;
3780         startInterceptRequestLayout();
3781         mViewInfoStore.clear();
3782         onEnterLayoutOrScroll();
3783         processAdapterUpdatesAndSetAnimationFlags();
3784         saveFocusInfo();
3785         mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
3786         mItemsAddedOrRemoved = mItemsChanged = false;
3787         mState.mInPreLayout = mState.mRunPredictiveAnimations;
3788         mState.mItemCount = mAdapter.getItemCount();
3789         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3790 
3791         if (mState.mRunSimpleAnimations) {
3792             // Step 0: Find out where all non-removed items are, pre-layout
3793             int count = mChildHelper.getChildCount();
3794             for (int i = 0; i < count; ++i) {
3795                 final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3796                 if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
3797                     continue;
3798                 }
3799                 final ItemHolderInfo animationInfo = mItemAnimator
3800                         .recordPreLayoutInformation(mState, holder,
3801                                 ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
3802                                 holder.getUnmodifiedPayloads());
3803                 mViewInfoStore.addToPreLayout(holder, animationInfo);
3804                 if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
3805                         && !holder.shouldIgnore() && !holder.isInvalid()) {
3806                     long key = getChangedHolderKey(holder);
3807                     // This is NOT the only place where a ViewHolder is added to old change holders
3808                     // list. There is another case where:
3809                     //    * A VH is currently hidden but not deleted
3810                     //    * The hidden item is changed in the adapter
3811                     //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
3812                     // When this case is detected, RV will un-hide that view and add to the old
3813                     // change holders list.
3814                     mViewInfoStore.addToOldChangeHolders(key, holder);
3815                 }
3816             }
3817         }
3818         if (mState.mRunPredictiveAnimations) {
3819             // Step 1: run prelayout: This will use the old positions of items. The layout manager
3820             // is expected to layout everything, even removed items (though not to add removed
3821             // items back to the container). This gives the pre-layout position of APPEARING views
3822             // which come into existence as part of the real layout.
3823 
3824             // Save old positions so that LayoutManager can run its mapping logic.
3825             saveOldPositions();
3826             final boolean didStructureChange = mState.mStructureChanged;
3827             mState.mStructureChanged = false;
3828             // temporarily disable flag because we are asking for previous layout
3829             mLayout.onLayoutChildren(mRecycler, mState);
3830             mState.mStructureChanged = didStructureChange;
3831 
3832             for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
3833                 final View child = mChildHelper.getChildAt(i);
3834                 final ViewHolder viewHolder = getChildViewHolderInt(child);
3835                 if (viewHolder.shouldIgnore()) {
3836                     continue;
3837                 }
3838                 if (!mViewInfoStore.isInPreLayout(viewHolder)) {
3839                     int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
3840                     boolean wasHidden = viewHolder
3841                             .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3842                     if (!wasHidden) {
3843                         flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
3844                     }
3845                     final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
3846                             mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
3847                     if (wasHidden) {
3848                         recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
3849                     } else {
3850                         mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
3851                     }
3852                 }
3853             }
3854             // we don't process disappearing list because they may re-appear in post layout pass.
3855             clearOldPositions();
3856         } else {
3857             clearOldPositions();
3858         }
3859         onExitLayoutOrScroll();
3860         stopInterceptRequestLayout(false);
3861         mState.mLayoutStep = State.STEP_LAYOUT;
3862     }
3863 
3864     /**
3865      * The second layout step where we do the actual layout of the views for the final state.
3866      * This step might be run multiple times if necessary (e.g. measure).
3867      */
dispatchLayoutStep2()3868     private void dispatchLayoutStep2() {
3869         startInterceptRequestLayout();
3870         onEnterLayoutOrScroll();
3871         mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
3872         mAdapterHelper.consumeUpdatesInOnePass();
3873         mState.mItemCount = mAdapter.getItemCount();
3874         mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
3875 
3876         // Step 2: Run layout
3877         mState.mInPreLayout = false;
3878         mLayout.onLayoutChildren(mRecycler, mState);
3879 
3880         mState.mStructureChanged = false;
3881         mPendingSavedState = null;
3882 
3883         // onLayoutChildren may have caused client code to disable item animations; re-check
3884         mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
3885         mState.mLayoutStep = State.STEP_ANIMATIONS;
3886         onExitLayoutOrScroll();
3887         stopInterceptRequestLayout(false);
3888     }
3889 
3890     /**
3891      * The final step of the layout where we save the information about views for animations,
3892      * trigger animations and do any necessary cleanup.
3893      */
dispatchLayoutStep3()3894     private void dispatchLayoutStep3() {
3895         mState.assertLayoutStep(State.STEP_ANIMATIONS);
3896         startInterceptRequestLayout();
3897         onEnterLayoutOrScroll();
3898         mState.mLayoutStep = State.STEP_START;
3899         if (mState.mRunSimpleAnimations) {
3900             // Step 3: Find out where things are now, and process change animations.
3901             // traverse list in reverse because we may call animateChange in the loop which may
3902             // remove the target view holder.
3903             for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
3904                 ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3905                 if (holder.shouldIgnore()) {
3906                     continue;
3907                 }
3908                 long key = getChangedHolderKey(holder);
3909                 final ItemHolderInfo animationInfo = mItemAnimator
3910                         .recordPostLayoutInformation(mState, holder);
3911                 ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
3912                 if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
3913                     // run a change animation
3914 
3915                     // If an Item is CHANGED but the updated version is disappearing, it creates
3916                     // a conflicting case.
3917                     // Since a view that is marked as disappearing is likely to be going out of
3918                     // bounds, we run a change animation. Both views will be cleaned automatically
3919                     // once their animations finish.
3920                     // On the other hand, if it is the same view holder instance, we run a
3921                     // disappearing animation instead because we are not going to rebind the updated
3922                     // VH unless it is enforced by the layout manager.
3923                     final boolean oldDisappearing = mViewInfoStore.isDisappearing(
3924                             oldChangeViewHolder);
3925                     final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
3926                     if (oldDisappearing && oldChangeViewHolder == holder) {
3927                         // run disappear animation instead of change
3928                         mViewInfoStore.addToPostLayout(holder, animationInfo);
3929                     } else {
3930                         final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
3931                                 oldChangeViewHolder);
3932                         // we add and remove so that any post info is merged.
3933                         mViewInfoStore.addToPostLayout(holder, animationInfo);
3934                         ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
3935                         if (preInfo == null) {
3936                             handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
3937                         } else {
3938                             animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
3939                                     oldDisappearing, newDisappearing);
3940                         }
3941                     }
3942                 } else {
3943                     mViewInfoStore.addToPostLayout(holder, animationInfo);
3944                 }
3945             }
3946 
3947             // Step 4: Process view info lists and trigger animations
3948             mViewInfoStore.process(mViewInfoProcessCallback);
3949         }
3950 
3951         mLayout.removeAndRecycleScrapInt(mRecycler);
3952         mState.mPreviousLayoutItemCount = mState.mItemCount;
3953         mDataSetHasChangedAfterLayout = false;
3954         mDispatchItemsChangedEvent = false;
3955         mState.mRunSimpleAnimations = false;
3956 
3957         mState.mRunPredictiveAnimations = false;
3958         mLayout.mRequestedSimpleAnimations = false;
3959         if (mRecycler.mChangedScrap != null) {
3960             mRecycler.mChangedScrap.clear();
3961         }
3962         if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
3963             // Initial prefetch has expanded cache, so reset until next prefetch.
3964             // This prevents initial prefetches from expanding the cache permanently.
3965             mLayout.mPrefetchMaxCountObserved = 0;
3966             mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
3967             mRecycler.updateViewCacheSize();
3968         }
3969 
3970         mLayout.onLayoutCompleted(mState);
3971         onExitLayoutOrScroll();
3972         stopInterceptRequestLayout(false);
3973         mViewInfoStore.clear();
3974         if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
3975             dispatchOnScrolled(0, 0);
3976         }
3977         recoverFocusFromState();
3978         resetFocusInfo();
3979     }
3980 
3981     /**
3982      * This handles the case where there is an unexpected VH missing in the pre-layout map.
3983      * <p>
3984      * We might be able to detect the error in the application which will help the developer to
3985      * resolve the issue.
3986      * <p>
3987      * If it is not an expected error, we at least print an error to notify the developer and ignore
3988      * the animation.
3989      *
3990      * https://code.google.com/p/android/issues/detail?id=193958
3991      *
3992      * @param key The change key
3993      * @param holder Current ViewHolder
3994      * @param oldChangeViewHolder Changed ViewHolder
3995      */
handleMissingPreInfoForChangeError(long key, ViewHolder holder, ViewHolder oldChangeViewHolder)3996     private void handleMissingPreInfoForChangeError(long key,
3997             ViewHolder holder, ViewHolder oldChangeViewHolder) {
3998         // check if two VH have the same key, if so, print that as an error
3999         final int childCount = mChildHelper.getChildCount();
4000         for (int i = 0; i < childCount; i++) {
4001             View view = mChildHelper.getChildAt(i);
4002             ViewHolder other = getChildViewHolderInt(view);
4003             if (other == holder) {
4004                 continue;
4005             }
4006             final long otherKey = getChangedHolderKey(other);
4007             if (otherKey == key) {
4008                 if (mAdapter != null && mAdapter.hasStableIds()) {
4009                     throw new IllegalStateException("Two different ViewHolders have the same stable"
4010                             + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
4011                             + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
4012                             + exceptionLabel());
4013                 } else {
4014                     throw new IllegalStateException("Two different ViewHolders have the same change"
4015                             + " ID. This might happen due to inconsistent Adapter update events or"
4016                             + " if the LayoutManager lays out the same View multiple times."
4017                             + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
4018                             + exceptionLabel());
4019                 }
4020             }
4021         }
4022         // Very unlikely to happen but if it does, notify the developer.
4023         Log.e(TAG, "Problem while matching changed view holders with the new"
4024                 + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
4025                 + " cannot be found but it is necessary for " + holder + exceptionLabel());
4026     }
4027 
4028     /**
4029      * Records the animation information for a view holder that was bounced from hidden list. It
4030      * also clears the bounce back flag.
4031      */
recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder, ItemHolderInfo animationInfo)4032     void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
4033             ItemHolderInfo animationInfo) {
4034         // looks like this view bounced back from hidden list!
4035         viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
4036         if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
4037                 && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
4038             long key = getChangedHolderKey(viewHolder);
4039             mViewInfoStore.addToOldChangeHolders(key, viewHolder);
4040         }
4041         mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
4042     }
4043 
findMinMaxChildLayoutPositions(int[] into)4044     private void findMinMaxChildLayoutPositions(int[] into) {
4045         final int count = mChildHelper.getChildCount();
4046         if (count == 0) {
4047             into[0] = NO_POSITION;
4048             into[1] = NO_POSITION;
4049             return;
4050         }
4051         int minPositionPreLayout = Integer.MAX_VALUE;
4052         int maxPositionPreLayout = Integer.MIN_VALUE;
4053         for (int i = 0; i < count; ++i) {
4054             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
4055             if (holder.shouldIgnore()) {
4056                 continue;
4057             }
4058             final int pos = holder.getLayoutPosition();
4059             if (pos < minPositionPreLayout) {
4060                 minPositionPreLayout = pos;
4061             }
4062             if (pos > maxPositionPreLayout) {
4063                 maxPositionPreLayout = pos;
4064             }
4065         }
4066         into[0] = minPositionPreLayout;
4067         into[1] = maxPositionPreLayout;
4068     }
4069 
didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout)4070     private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
4071         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
4072         return mMinMaxLayoutPositions[0] != minPositionPreLayout
4073                 || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
4074     }
4075 
4076     @Override
removeDetachedView(View child, boolean animate)4077     protected void removeDetachedView(View child, boolean animate) {
4078         ViewHolder vh = getChildViewHolderInt(child);
4079         if (vh != null) {
4080             if (vh.isTmpDetached()) {
4081                 vh.clearTmpDetachFlag();
4082             } else if (!vh.shouldIgnore()) {
4083                 throw new IllegalArgumentException("Called removeDetachedView with a view which"
4084                         + " is not flagged as tmp detached." + vh + exceptionLabel());
4085             }
4086         }
4087 
4088         // Clear any android.view.animation.Animation that may prevent the item from
4089         // detaching when being removed. If a child is re-added before the
4090         // lazy detach occurs, it will receive invalid attach/detach sequencing.
4091         child.clearAnimation();
4092 
4093         dispatchChildDetached(child);
4094         super.removeDetachedView(child, animate);
4095     }
4096 
4097     /**
4098      * Returns a unique key to be used while handling change animations.
4099      * It might be child's position or stable id depending on the adapter type.
4100      */
getChangedHolderKey(ViewHolder holder)4101     long getChangedHolderKey(ViewHolder holder) {
4102         return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
4103     }
4104 
animateAppearance(@onNull ViewHolder itemHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)4105     void animateAppearance(@NonNull ViewHolder itemHolder,
4106             @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
4107         itemHolder.setIsRecyclable(false);
4108         if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
4109             postAnimationRunner();
4110         }
4111     }
4112 
animateDisappearance(@onNull ViewHolder holder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo)4113     void animateDisappearance(@NonNull ViewHolder holder,
4114             @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
4115         addAnimatingView(holder);
4116         holder.setIsRecyclable(false);
4117         if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
4118             postAnimationRunner();
4119         }
4120     }
4121 
animateChange(@onNull ViewHolder oldHolder, @NonNull ViewHolder newHolder, @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo, boolean oldHolderDisappearing, boolean newHolderDisappearing)4122     private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
4123             @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
4124             boolean oldHolderDisappearing, boolean newHolderDisappearing) {
4125         oldHolder.setIsRecyclable(false);
4126         if (oldHolderDisappearing) {
4127             addAnimatingView(oldHolder);
4128         }
4129         if (oldHolder != newHolder) {
4130             if (newHolderDisappearing) {
4131                 addAnimatingView(newHolder);
4132             }
4133             oldHolder.mShadowedHolder = newHolder;
4134             // old holder should disappear after animation ends
4135             addAnimatingView(oldHolder);
4136             mRecycler.unscrapView(oldHolder);
4137             newHolder.setIsRecyclable(false);
4138             newHolder.mShadowingHolder = oldHolder;
4139         }
4140         if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
4141             postAnimationRunner();
4142         }
4143     }
4144 
4145     @Override
onLayout(boolean changed, int l, int t, int r, int b)4146     protected void onLayout(boolean changed, int l, int t, int r, int b) {
4147         TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
4148         dispatchLayout();
4149         TraceCompat.endSection();
4150         mFirstLayoutComplete = true;
4151     }
4152 
4153     @Override
requestLayout()4154     public void requestLayout() {
4155         if (mInterceptRequestLayoutDepth == 0 && !mLayoutFrozen) {
4156             super.requestLayout();
4157         } else {
4158             mLayoutWasDefered = true;
4159         }
4160     }
4161 
markItemDecorInsetsDirty()4162     void markItemDecorInsetsDirty() {
4163         final int childCount = mChildHelper.getUnfilteredChildCount();
4164         for (int i = 0; i < childCount; i++) {
4165             final View child = mChildHelper.getUnfilteredChildAt(i);
4166             ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
4167         }
4168         mRecycler.markItemDecorInsetsDirty();
4169     }
4170 
4171     @Override
draw(Canvas c)4172     public void draw(Canvas c) {
4173         super.draw(c);
4174 
4175         final int count = mItemDecorations.size();
4176         for (int i = 0; i < count; i++) {
4177             mItemDecorations.get(i).onDrawOver(c, this, mState);
4178         }
4179         // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
4180         // need find children closest to edges. Not sure if it is worth the effort.
4181         boolean needsInvalidate = false;
4182         if (mLeftGlow != null && !mLeftGlow.isFinished()) {
4183             final int restore = c.save();
4184             final int padding = mClipToPadding ? getPaddingBottom() : 0;
4185             c.rotate(270);
4186             c.translate(-getHeight() + padding, 0);
4187             needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
4188             c.restoreToCount(restore);
4189         }
4190         if (mTopGlow != null && !mTopGlow.isFinished()) {
4191             final int restore = c.save();
4192             if (mClipToPadding) {
4193                 c.translate(getPaddingLeft(), getPaddingTop());
4194             }
4195             needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
4196             c.restoreToCount(restore);
4197         }
4198         if (mRightGlow != null && !mRightGlow.isFinished()) {
4199             final int restore = c.save();
4200             final int width = getWidth();
4201             final int padding = mClipToPadding ? getPaddingTop() : 0;
4202             c.rotate(90);
4203             c.translate(-padding, -width);
4204             needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
4205             c.restoreToCount(restore);
4206         }
4207         if (mBottomGlow != null && !mBottomGlow.isFinished()) {
4208             final int restore = c.save();
4209             c.rotate(180);
4210             if (mClipToPadding) {
4211                 c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
4212             } else {
4213                 c.translate(-getWidth(), -getHeight());
4214             }
4215             needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
4216             c.restoreToCount(restore);
4217         }
4218 
4219         // If some views are animating, ItemDecorators are likely to move/change with them.
4220         // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
4221         // display lists are not invalidated.
4222         if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
4223                 && mItemAnimator.isRunning()) {
4224             needsInvalidate = true;
4225         }
4226 
4227         if (needsInvalidate) {
4228             ViewCompat.postInvalidateOnAnimation(this);
4229         }
4230     }
4231 
4232     @Override
onDraw(Canvas c)4233     public void onDraw(Canvas c) {
4234         super.onDraw(c);
4235 
4236         final int count = mItemDecorations.size();
4237         for (int i = 0; i < count; i++) {
4238             mItemDecorations.get(i).onDraw(c, this, mState);
4239         }
4240     }
4241 
4242     @Override
checkLayoutParams(ViewGroup.LayoutParams p)4243     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
4244         return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
4245     }
4246 
4247     @Override
generateDefaultLayoutParams()4248     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
4249         if (mLayout == null) {
4250             throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
4251         }
4252         return mLayout.generateDefaultLayoutParams();
4253     }
4254 
4255     @Override
generateLayoutParams(AttributeSet attrs)4256     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
4257         if (mLayout == null) {
4258             throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
4259         }
4260         return mLayout.generateLayoutParams(getContext(), attrs);
4261     }
4262 
4263     @Override
generateLayoutParams(ViewGroup.LayoutParams p)4264     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
4265         if (mLayout == null) {
4266             throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
4267         }
4268         return mLayout.generateLayoutParams(p);
4269     }
4270 
4271     /**
4272      * Returns true if RecyclerView is currently running some animations.
4273      * <p>
4274      * If you want to be notified when animations are finished, use
4275      * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
4276      *
4277      * @return True if there are some item animations currently running or waiting to be started.
4278      */
isAnimating()4279     public boolean isAnimating() {
4280         return mItemAnimator != null && mItemAnimator.isRunning();
4281     }
4282 
saveOldPositions()4283     void saveOldPositions() {
4284         final int childCount = mChildHelper.getUnfilteredChildCount();
4285         for (int i = 0; i < childCount; i++) {
4286             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4287             if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
4288                 throw new IllegalStateException("view holder cannot have position -1 unless it"
4289                         + " is removed" + exceptionLabel());
4290             }
4291             if (!holder.shouldIgnore()) {
4292                 holder.saveOldPosition();
4293             }
4294         }
4295     }
4296 
clearOldPositions()4297     void clearOldPositions() {
4298         final int childCount = mChildHelper.getUnfilteredChildCount();
4299         for (int i = 0; i < childCount; i++) {
4300             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4301             if (!holder.shouldIgnore()) {
4302                 holder.clearOldPosition();
4303             }
4304         }
4305         mRecycler.clearOldPositions();
4306     }
4307 
offsetPositionRecordsForMove(int from, int to)4308     void offsetPositionRecordsForMove(int from, int to) {
4309         final int childCount = mChildHelper.getUnfilteredChildCount();
4310         final int start, end, inBetweenOffset;
4311         if (from < to) {
4312             start = from;
4313             end = to;
4314             inBetweenOffset = -1;
4315         } else {
4316             start = to;
4317             end = from;
4318             inBetweenOffset = 1;
4319         }
4320 
4321         for (int i = 0; i < childCount; i++) {
4322             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4323             if (holder == null || holder.mPosition < start || holder.mPosition > end) {
4324                 continue;
4325             }
4326             if (DEBUG) {
4327                 Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
4328                         + holder);
4329             }
4330             if (holder.mPosition == from) {
4331                 holder.offsetPosition(to - from, false);
4332             } else {
4333                 holder.offsetPosition(inBetweenOffset, false);
4334             }
4335 
4336             mState.mStructureChanged = true;
4337         }
4338         mRecycler.offsetPositionRecordsForMove(from, to);
4339         requestLayout();
4340     }
4341 
offsetPositionRecordsForInsert(int positionStart, int itemCount)4342     void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
4343         final int childCount = mChildHelper.getUnfilteredChildCount();
4344         for (int i = 0; i < childCount; i++) {
4345             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4346             if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
4347                 if (DEBUG) {
4348                     Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
4349                             + holder + " now at position " + (holder.mPosition + itemCount));
4350                 }
4351                 holder.offsetPosition(itemCount, false);
4352                 mState.mStructureChanged = true;
4353             }
4354         }
4355         mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
4356         requestLayout();
4357     }
4358 
offsetPositionRecordsForRemove(int positionStart, int itemCount, boolean applyToPreLayout)4359     void offsetPositionRecordsForRemove(int positionStart, int itemCount,
4360             boolean applyToPreLayout) {
4361         final int positionEnd = positionStart + itemCount;
4362         final int childCount = mChildHelper.getUnfilteredChildCount();
4363         for (int i = 0; i < childCount; i++) {
4364             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4365             if (holder != null && !holder.shouldIgnore()) {
4366                 if (holder.mPosition >= positionEnd) {
4367                     if (DEBUG) {
4368                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4369                                 + " holder " + holder + " now at position "
4370                                 + (holder.mPosition - itemCount));
4371                     }
4372                     holder.offsetPosition(-itemCount, applyToPreLayout);
4373                     mState.mStructureChanged = true;
4374                 } else if (holder.mPosition >= positionStart) {
4375                     if (DEBUG) {
4376                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4377                                 + " holder " + holder + " now REMOVED");
4378                     }
4379                     holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
4380                             applyToPreLayout);
4381                     mState.mStructureChanged = true;
4382                 }
4383             }
4384         }
4385         mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
4386         requestLayout();
4387     }
4388 
4389     /**
4390      * Rebind existing views for the given range, or create as needed.
4391      *
4392      * @param positionStart Adapter position to start at
4393      * @param itemCount Number of views that must explicitly be rebound
4394      */
viewRangeUpdate(int positionStart, int itemCount, Object payload)4395     void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
4396         final int childCount = mChildHelper.getUnfilteredChildCount();
4397         final int positionEnd = positionStart + itemCount;
4398 
4399         for (int i = 0; i < childCount; i++) {
4400             final View child = mChildHelper.getUnfilteredChildAt(i);
4401             final ViewHolder holder = getChildViewHolderInt(child);
4402             if (holder == null || holder.shouldIgnore()) {
4403                 continue;
4404             }
4405             if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
4406                 // We re-bind these view holders after pre-processing is complete so that
4407                 // ViewHolders have their final positions assigned.
4408                 holder.addFlags(ViewHolder.FLAG_UPDATE);
4409                 holder.addChangePayload(payload);
4410                 // lp cannot be null since we get ViewHolder from it.
4411                 ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
4412             }
4413         }
4414         mRecycler.viewRangeUpdate(positionStart, itemCount);
4415     }
4416 
canReuseUpdatedViewHolder(ViewHolder viewHolder)4417     boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
4418         return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
4419                 viewHolder.getUnmodifiedPayloads());
4420     }
4421 
4422     /**
4423      * Processes the fact that, as far as we can tell, the data set has completely changed.
4424      *
4425      * <ul>
4426      *   <li>Once layout occurs, all attached items should be discarded or animated.
4427      *   <li>Attached items are labeled as invalid.
4428      *   <li>Because items may still be prefetched between a "data set completely changed"
4429      *       event and a layout event, all cached items are discarded.
4430      * </ul>
4431      *
4432      * @param dispatchItemsChanged Whether to call
4433      * {@link LayoutManager#onItemsChanged(RecyclerView)} during measure/layout.
4434      */
processDataSetCompletelyChanged(boolean dispatchItemsChanged)4435     void processDataSetCompletelyChanged(boolean dispatchItemsChanged) {
4436         mDispatchItemsChangedEvent |= dispatchItemsChanged;
4437         mDataSetHasChangedAfterLayout = true;
4438         markKnownViewsInvalid();
4439     }
4440 
4441     /**
4442      * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
4443      * data change event.
4444      */
markKnownViewsInvalid()4445     void markKnownViewsInvalid() {
4446         final int childCount = mChildHelper.getUnfilteredChildCount();
4447         for (int i = 0; i < childCount; i++) {
4448             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4449             if (holder != null && !holder.shouldIgnore()) {
4450                 holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
4451             }
4452         }
4453         markItemDecorInsetsDirty();
4454         mRecycler.markKnownViewsInvalid();
4455     }
4456 
4457     /**
4458      * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
4459      * will trigger a {@link #requestLayout()} call.
4460      */
invalidateItemDecorations()4461     public void invalidateItemDecorations() {
4462         if (mItemDecorations.size() == 0) {
4463             return;
4464         }
4465         if (mLayout != null) {
4466             mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
4467                     + " or layout");
4468         }
4469         markItemDecorInsetsDirty();
4470         requestLayout();
4471     }
4472 
4473     /**
4474      * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
4475      * focus even if the View representing the Item is replaced during a layout calculation.
4476      * <p>
4477      * By default, this value is {@code true}.
4478      *
4479      * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
4480      * focus.
4481      *
4482      * @see #setPreserveFocusAfterLayout(boolean)
4483      */
getPreserveFocusAfterLayout()4484     public boolean getPreserveFocusAfterLayout() {
4485         return mPreserveFocusAfterLayout;
4486     }
4487 
4488     /**
4489      * Set whether the RecyclerView should try to keep the same Item focused after a layout
4490      * calculation or not.
4491      * <p>
4492      * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
4493      * views may lose focus during a layout calculation as their state changes or they are replaced
4494      * with another view due to type change or animation. In these cases, RecyclerView can request
4495      * focus on the new view automatically.
4496      *
4497      * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
4498      *                                 layout calculations. Defaults to true.
4499      *
4500      * @see #getPreserveFocusAfterLayout()
4501      */
setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout)4502     public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
4503         mPreserveFocusAfterLayout = preserveFocusAfterLayout;
4504     }
4505 
4506     /**
4507      * Retrieve the {@link ViewHolder} for the given child view.
4508      *
4509      * @param child Child of this RecyclerView to query for its ViewHolder
4510      * @return The child view's ViewHolder
4511      */
getChildViewHolder(@onNull View child)4512     public ViewHolder getChildViewHolder(@NonNull View child) {
4513         final ViewParent parent = child.getParent();
4514         if (parent != null && parent != this) {
4515             throw new IllegalArgumentException("View " + child + " is not a direct child of "
4516                     + this);
4517         }
4518         return getChildViewHolderInt(child);
4519     }
4520 
4521     /**
4522      * Traverses the ancestors of the given view and returns the item view that contains it and
4523      * also a direct child of the RecyclerView. This returned view can be used to get the
4524      * ViewHolder by calling {@link #getChildViewHolder(View)}.
4525      *
4526      * @param view The view that is a descendant of the RecyclerView.
4527      *
4528      * @return The direct child of the RecyclerView which contains the given view or null if the
4529      * provided view is not a descendant of this RecyclerView.
4530      *
4531      * @see #getChildViewHolder(View)
4532      * @see #findContainingViewHolder(View)
4533      */
4534     @Nullable
findContainingItemView(@onNull View view)4535     public View findContainingItemView(@NonNull View view) {
4536         ViewParent parent = view.getParent();
4537         while (parent != null && parent != this && parent instanceof View) {
4538             view = (View) parent;
4539             parent = view.getParent();
4540         }
4541         return parent == this ? view : null;
4542     }
4543 
4544     /**
4545      * Returns the ViewHolder that contains the given view.
4546      *
4547      * @param view The view that is a descendant of the RecyclerView.
4548      *
4549      * @return The ViewHolder that contains the given view or null if the provided view is not a
4550      * descendant of this RecyclerView.
4551      */
4552     @Nullable
findContainingViewHolder(@onNull View view)4553     public ViewHolder findContainingViewHolder(@NonNull View view) {
4554         View itemView = findContainingItemView(view);
4555         return itemView == null ? null : getChildViewHolder(itemView);
4556     }
4557 
4558 
getChildViewHolderInt(View child)4559     static ViewHolder getChildViewHolderInt(View child) {
4560         if (child == null) {
4561             return null;
4562         }
4563         return ((LayoutParams) child.getLayoutParams()).mViewHolder;
4564     }
4565 
4566     /**
4567      * @deprecated use {@link #getChildAdapterPosition(View)} or
4568      * {@link #getChildLayoutPosition(View)}.
4569      */
4570     @Deprecated
getChildPosition(@onNull View child)4571     public int getChildPosition(@NonNull View child) {
4572         return getChildAdapterPosition(child);
4573     }
4574 
4575     /**
4576      * Return the adapter position that the given child view corresponds to.
4577      *
4578      * @param child Child View to query
4579      * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
4580      */
getChildAdapterPosition(@onNull View child)4581     public int getChildAdapterPosition(@NonNull View child) {
4582         final ViewHolder holder = getChildViewHolderInt(child);
4583         return holder != null ? holder.getAdapterPosition() : NO_POSITION;
4584     }
4585 
4586     /**
4587      * Return the adapter position of the given child view as of the latest completed layout pass.
4588      * <p>
4589      * This position may not be equal to Item's adapter position if there are pending changes
4590      * in the adapter which have not been reflected to the layout yet.
4591      *
4592      * @param child Child View to query
4593      * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
4594      * the View is representing a removed item.
4595      */
getChildLayoutPosition(@onNull View child)4596     public int getChildLayoutPosition(@NonNull View child) {
4597         final ViewHolder holder = getChildViewHolderInt(child);
4598         return holder != null ? holder.getLayoutPosition() : NO_POSITION;
4599     }
4600 
4601     /**
4602      * Return the stable item id that the given child view corresponds to.
4603      *
4604      * @param child Child View to query
4605      * @return Item id corresponding to the given view or {@link #NO_ID}
4606      */
getChildItemId(@onNull View child)4607     public long getChildItemId(@NonNull View child) {
4608         if (mAdapter == null || !mAdapter.hasStableIds()) {
4609             return NO_ID;
4610         }
4611         final ViewHolder holder = getChildViewHolderInt(child);
4612         return holder != null ? holder.getItemId() : NO_ID;
4613     }
4614 
4615     /**
4616      * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
4617      * {@link #findViewHolderForAdapterPosition(int)}
4618      */
4619     @Deprecated
4620     @Nullable
findViewHolderForPosition(int position)4621     public ViewHolder findViewHolderForPosition(int position) {
4622         return findViewHolderForPosition(position, false);
4623     }
4624 
4625     /**
4626      * Return the ViewHolder for the item in the given position of the data set as of the latest
4627      * layout pass.
4628      * <p>
4629      * This method checks only the children of RecyclerView. If the item at the given
4630      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4631      * <p>
4632      * Note that when Adapter contents change, ViewHolder positions are not updated until the
4633      * next layout calculation. If there are pending adapter updates, the return value of this
4634      * method may not match your adapter contents. You can use
4635      * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
4636      * <p>
4637      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4638      * with the same layout position representing the same Item. In this case, the updated
4639      * ViewHolder will be returned.
4640      *
4641      * @param position The position of the item in the data set of the adapter
4642      * @return The ViewHolder at <code>position</code> or null if there is no such item
4643      */
4644     @Nullable
findViewHolderForLayoutPosition(int position)4645     public ViewHolder findViewHolderForLayoutPosition(int position) {
4646         return findViewHolderForPosition(position, false);
4647     }
4648 
4649     /**
4650      * Return the ViewHolder for the item in the given position of the data set. Unlike
4651      * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
4652      * adapter changes that may not be reflected to the layout yet. On the other hand, if
4653      * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
4654      * calculated yet, this method will return <code>null</code> since the new positions of views
4655      * are unknown until the layout is calculated.
4656      * <p>
4657      * This method checks only the children of RecyclerView. If the item at the given
4658      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4659      * <p>
4660      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4661      * representing the same Item. In this case, the updated ViewHolder will be returned.
4662      *
4663      * @param position The position of the item in the data set of the adapter
4664      * @return The ViewHolder at <code>position</code> or null if there is no such item
4665      */
4666     @Nullable
findViewHolderForAdapterPosition(int position)4667     public ViewHolder findViewHolderForAdapterPosition(int position) {
4668         if (mDataSetHasChangedAfterLayout) {
4669             return null;
4670         }
4671         final int childCount = mChildHelper.getUnfilteredChildCount();
4672         // hidden VHs are not preferred but if that is the only one we find, we rather return it
4673         ViewHolder hidden = null;
4674         for (int i = 0; i < childCount; i++) {
4675             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4676             if (holder != null && !holder.isRemoved()
4677                     && getAdapterPositionFor(holder) == position) {
4678                 if (mChildHelper.isHidden(holder.itemView)) {
4679                     hidden = holder;
4680                 } else {
4681                     return holder;
4682                 }
4683             }
4684         }
4685         return hidden;
4686     }
4687 
4688     @Nullable
findViewHolderForPosition(int position, boolean checkNewPosition)4689     ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
4690         final int childCount = mChildHelper.getUnfilteredChildCount();
4691         ViewHolder hidden = null;
4692         for (int i = 0; i < childCount; i++) {
4693             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4694             if (holder != null && !holder.isRemoved()) {
4695                 if (checkNewPosition) {
4696                     if (holder.mPosition != position) {
4697                         continue;
4698                     }
4699                 } else if (holder.getLayoutPosition() != position) {
4700                     continue;
4701                 }
4702                 if (mChildHelper.isHidden(holder.itemView)) {
4703                     hidden = holder;
4704                 } else {
4705                     return holder;
4706                 }
4707             }
4708         }
4709         // This method should not query cached views. It creates a problem during adapter updates
4710         // when we are dealing with already laid out views. Also, for the public method, it is more
4711         // reasonable to return null if position is not laid out.
4712         return hidden;
4713     }
4714 
4715     /**
4716      * Return the ViewHolder for the item with the given id. The RecyclerView must
4717      * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
4718      * return a non-null value.
4719      * <p>
4720      * This method checks only the children of RecyclerView. If the item with the given
4721      * <code>id</code> is not laid out, it <em>will not</em> create a new one.
4722      *
4723      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
4724      * same id. In this case, the updated ViewHolder will be returned.
4725      *
4726      * @param id The id for the requested item
4727      * @return The ViewHolder with the given <code>id</code> or null if there is no such item
4728      */
findViewHolderForItemId(long id)4729     public ViewHolder findViewHolderForItemId(long id) {
4730         if (mAdapter == null || !mAdapter.hasStableIds()) {
4731             return null;
4732         }
4733         final int childCount = mChildHelper.getUnfilteredChildCount();
4734         ViewHolder hidden = null;
4735         for (int i = 0; i < childCount; i++) {
4736             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4737             if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
4738                 if (mChildHelper.isHidden(holder.itemView)) {
4739                     hidden = holder;
4740                 } else {
4741                     return holder;
4742                 }
4743             }
4744         }
4745         return hidden;
4746     }
4747 
4748     /**
4749      * Find the topmost view under the given point.
4750      *
4751      * @param x Horizontal position in pixels to search
4752      * @param y Vertical position in pixels to search
4753      * @return The child view under (x, y) or null if no matching child is found
4754      */
4755     @Nullable
findChildViewUnder(float x, float y)4756     public View findChildViewUnder(float x, float y) {
4757         final int count = mChildHelper.getChildCount();
4758         for (int i = count - 1; i >= 0; i--) {
4759             final View child = mChildHelper.getChildAt(i);
4760             final float translationX = child.getTranslationX();
4761             final float translationY = child.getTranslationY();
4762             if (x >= child.getLeft() + translationX
4763                     && x <= child.getRight() + translationX
4764                     && y >= child.getTop() + translationY
4765                     && y <= child.getBottom() + translationY) {
4766                 return child;
4767             }
4768         }
4769         return null;
4770     }
4771 
4772     @Override
drawChild(Canvas canvas, View child, long drawingTime)4773     public boolean drawChild(Canvas canvas, View child, long drawingTime) {
4774         return super.drawChild(canvas, child, drawingTime);
4775     }
4776 
4777     /**
4778      * Offset the bounds of all child views by <code>dy</code> pixels.
4779      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4780      *
4781      * @param dy Vertical pixel offset to apply to the bounds of all child views
4782      */
offsetChildrenVertical(@x int dy)4783     public void offsetChildrenVertical(@Px int dy) {
4784         final int childCount = mChildHelper.getChildCount();
4785         for (int i = 0; i < childCount; i++) {
4786             mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
4787         }
4788     }
4789 
4790     /**
4791      * Called when an item view is attached to this RecyclerView.
4792      *
4793      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4794      * of child views as they become attached. This will be called before a
4795      * {@link LayoutManager} measures or lays out the view and is a good time to perform these
4796      * changes.</p>
4797      *
4798      * @param child Child view that is now attached to this RecyclerView and its associated window
4799      */
onChildAttachedToWindow(@onNull View child)4800     public void onChildAttachedToWindow(@NonNull View child) {
4801     }
4802 
4803     /**
4804      * Called when an item view is detached from this RecyclerView.
4805      *
4806      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4807      * of child views as they become detached. This will be called as a
4808      * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
4809      *
4810      * @param child Child view that is now detached from this RecyclerView and its associated window
4811      */
onChildDetachedFromWindow(@onNull View child)4812     public void onChildDetachedFromWindow(@NonNull View child) {
4813     }
4814 
4815     /**
4816      * Offset the bounds of all child views by <code>dx</code> pixels.
4817      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4818      *
4819      * @param dx Horizontal pixel offset to apply to the bounds of all child views
4820      */
offsetChildrenHorizontal(@x int dx)4821     public void offsetChildrenHorizontal(@Px int dx) {
4822         final int childCount = mChildHelper.getChildCount();
4823         for (int i = 0; i < childCount; i++) {
4824             mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
4825         }
4826     }
4827 
4828     /**
4829      * Returns the bounds of the view including its decoration and margins.
4830      *
4831      * @param view The view element to check
4832      * @param outBounds A rect that will receive the bounds of the element including its
4833      *                  decoration and margins.
4834      */
getDecoratedBoundsWithMargins(@onNull View view, @NonNull Rect outBounds)4835     public void getDecoratedBoundsWithMargins(@NonNull View view, @NonNull Rect outBounds) {
4836         getDecoratedBoundsWithMarginsInt(view, outBounds);
4837     }
4838 
getDecoratedBoundsWithMarginsInt(View view, Rect outBounds)4839     static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
4840         final LayoutParams lp = (LayoutParams) view.getLayoutParams();
4841         final Rect insets = lp.mDecorInsets;
4842         outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
4843                 view.getTop() - insets.top - lp.topMargin,
4844                 view.getRight() + insets.right + lp.rightMargin,
4845                 view.getBottom() + insets.bottom + lp.bottomMargin);
4846     }
4847 
getItemDecorInsetsForChild(View child)4848     Rect getItemDecorInsetsForChild(View child) {
4849         final LayoutParams lp = (LayoutParams) child.getLayoutParams();
4850         if (!lp.mInsetsDirty) {
4851             return lp.mDecorInsets;
4852         }
4853 
4854         if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
4855             // changed/invalid items should not be updated until they are rebound.
4856             return lp.mDecorInsets;
4857         }
4858         final Rect insets = lp.mDecorInsets;
4859         insets.set(0, 0, 0, 0);
4860         final int decorCount = mItemDecorations.size();
4861         for (int i = 0; i < decorCount; i++) {
4862             mTempRect.set(0, 0, 0, 0);
4863             mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
4864             insets.left += mTempRect.left;
4865             insets.top += mTempRect.top;
4866             insets.right += mTempRect.right;
4867             insets.bottom += mTempRect.bottom;
4868         }
4869         lp.mInsetsDirty = false;
4870         return insets;
4871     }
4872 
4873     /**
4874      * Called when the scroll position of this RecyclerView changes. Subclasses should use
4875      * this method to respond to scrolling within the adapter's data set instead of an explicit
4876      * listener.
4877      *
4878      * <p>This method will always be invoked before listeners. If a subclass needs to perform
4879      * any additional upkeep or bookkeeping after scrolling but before listeners run,
4880      * this is a good place to do so.</p>
4881      *
4882      * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
4883      * the distance scrolled in either direction within the adapter's data set instead of absolute
4884      * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
4885      * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
4886      * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
4887      * do not correspond to the data set scroll position. However, some subclasses may choose
4888      * to use these fields as special offsets.</p>
4889      *
4890      * @param dx horizontal distance scrolled in pixels
4891      * @param dy vertical distance scrolled in pixels
4892      */
onScrolled(@x int dx, @Px int dy)4893     public void onScrolled(@Px int dx, @Px int dy) {
4894         // Do nothing
4895     }
4896 
dispatchOnScrolled(int hresult, int vresult)4897     void dispatchOnScrolled(int hresult, int vresult) {
4898         mDispatchScrollCounter++;
4899         // Pass the current scrollX/scrollY values; no actual change in these properties occurred
4900         // but some general-purpose code may choose to respond to changes this way.
4901         final int scrollX = getScrollX();
4902         final int scrollY = getScrollY();
4903         onScrollChanged(scrollX, scrollY, scrollX, scrollY);
4904 
4905         // Pass the real deltas to onScrolled, the RecyclerView-specific method.
4906         onScrolled(hresult, vresult);
4907 
4908         // Invoke listeners last. Subclassed view methods always handle the event first.
4909         // All internal state is consistent by the time listeners are invoked.
4910         if (mScrollListener != null) {
4911             mScrollListener.onScrolled(this, hresult, vresult);
4912         }
4913         if (mScrollListeners != null) {
4914             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4915                 mScrollListeners.get(i).onScrolled(this, hresult, vresult);
4916             }
4917         }
4918         mDispatchScrollCounter--;
4919     }
4920 
4921     /**
4922      * Called when the scroll state of this RecyclerView changes. Subclasses should use this
4923      * method to respond to state changes instead of an explicit listener.
4924      *
4925      * <p>This method will always be invoked before listeners, but after the LayoutManager
4926      * responds to the scroll state change.</p>
4927      *
4928      * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
4929      *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
4930      */
onScrollStateChanged(int state)4931     public void onScrollStateChanged(int state) {
4932         // Do nothing
4933     }
4934 
dispatchOnScrollStateChanged(int state)4935     void dispatchOnScrollStateChanged(int state) {
4936         // Let the LayoutManager go first; this allows it to bring any properties into
4937         // a consistent state before the RecyclerView subclass responds.
4938         if (mLayout != null) {
4939             mLayout.onScrollStateChanged(state);
4940         }
4941 
4942         // Let the RecyclerView subclass handle this event next; any LayoutManager property
4943         // changes will be reflected by this time.
4944         onScrollStateChanged(state);
4945 
4946         // Listeners go last. All other internal state is consistent by this point.
4947         if (mScrollListener != null) {
4948             mScrollListener.onScrollStateChanged(this, state);
4949         }
4950         if (mScrollListeners != null) {
4951             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4952                 mScrollListeners.get(i).onScrollStateChanged(this, state);
4953             }
4954         }
4955     }
4956 
4957     /**
4958      * Returns whether there are pending adapter updates which are not yet applied to the layout.
4959      * <p>
4960      * If this method returns <code>true</code>, it means that what user is currently seeing may not
4961      * reflect them adapter contents (depending on what has changed).
4962      * You may use this information to defer or cancel some operations.
4963      * <p>
4964      * This method returns true if RecyclerView has not yet calculated the first layout after it is
4965      * attached to the Window or the Adapter has been replaced.
4966      *
4967      * @return True if there are some adapter updates which are not yet reflected to layout or false
4968      * if layout is up to date.
4969      */
hasPendingAdapterUpdates()4970     public boolean hasPendingAdapterUpdates() {
4971         return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
4972                 || mAdapterHelper.hasPendingUpdates();
4973     }
4974 
4975     class ViewFlinger implements Runnable {
4976         private int mLastFlingX;
4977         private int mLastFlingY;
4978         private OverScroller mScroller;
4979         Interpolator mInterpolator = sQuinticInterpolator;
4980 
4981         // When set to true, postOnAnimation callbacks are delayed until the run method completes
4982         private boolean mEatRunOnAnimationRequest = false;
4983 
4984         // Tracks if postAnimationCallback should be re-attached when it is done
4985         private boolean mReSchedulePostAnimationCallback = false;
4986 
ViewFlinger()4987         ViewFlinger() {
4988             mScroller = new OverScroller(getContext(), sQuinticInterpolator);
4989         }
4990 
4991         @Override
run()4992         public void run() {
4993             if (mLayout == null) {
4994                 stop();
4995                 return; // no layout, cannot scroll.
4996             }
4997             disableRunOnAnimationRequests();
4998             consumePendingUpdateOperations();
4999             // keep a local reference so that if it is changed during onAnimation method, it won't
5000             // cause unexpected behaviors
5001             final OverScroller scroller = mScroller;
5002             final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
5003             if (scroller.computeScrollOffset()) {
5004                 final int[] scrollConsumed = mScrollConsumed;
5005                 final int x = scroller.getCurrX();
5006                 final int y = scroller.getCurrY();
5007                 int dx = x - mLastFlingX;
5008                 int dy = y - mLastFlingY;
5009                 int hresult = 0;
5010                 int vresult = 0;
5011                 mLastFlingX = x;
5012                 mLastFlingY = y;
5013                 int overscrollX = 0, overscrollY = 0;
5014 
5015                 if (dispatchNestedPreScroll(dx, dy, scrollConsumed, null, TYPE_NON_TOUCH)) {
5016                     dx -= scrollConsumed[0];
5017                     dy -= scrollConsumed[1];
5018                 }
5019 
5020                 if (mAdapter != null) {
5021                     scrollStep(dx, dy, mScrollStepConsumed);
5022                     hresult = mScrollStepConsumed[0];
5023                     vresult = mScrollStepConsumed[1];
5024                     overscrollX = dx - hresult;
5025                     overscrollY = dy - vresult;
5026 
5027                     if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
5028                             && smoothScroller.isRunning()) {
5029                         final int adapterSize = mState.getItemCount();
5030                         if (adapterSize == 0) {
5031                             smoothScroller.stop();
5032                         } else if (smoothScroller.getTargetPosition() >= adapterSize) {
5033                             smoothScroller.setTargetPosition(adapterSize - 1);
5034                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
5035                         } else {
5036                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
5037                         }
5038                     }
5039                 }
5040                 if (!mItemDecorations.isEmpty()) {
5041                     invalidate();
5042                 }
5043                 if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
5044                     considerReleasingGlowsOnScroll(dx, dy);
5045                 }
5046 
5047                 if (!dispatchNestedScroll(hresult, vresult, overscrollX, overscrollY, null,
5048                         TYPE_NON_TOUCH)
5049                         && (overscrollX != 0 || overscrollY != 0)) {
5050                     final int vel = (int) scroller.getCurrVelocity();
5051 
5052                     int velX = 0;
5053                     if (overscrollX != x) {
5054                         velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
5055                     }
5056 
5057                     int velY = 0;
5058                     if (overscrollY != y) {
5059                         velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
5060                     }
5061 
5062                     if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
5063                         absorbGlows(velX, velY);
5064                     }
5065                     if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
5066                             && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
5067                         scroller.abortAnimation();
5068                     }
5069                 }
5070                 if (hresult != 0 || vresult != 0) {
5071                     dispatchOnScrolled(hresult, vresult);
5072                 }
5073 
5074                 if (!awakenScrollBars()) {
5075                     invalidate();
5076                 }
5077 
5078                 final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
5079                         && vresult == dy;
5080                 final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
5081                         && hresult == dx;
5082                 final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
5083                         || fullyConsumedVertical;
5084 
5085                 if (scroller.isFinished() || (!fullyConsumedAny
5086                         && !hasNestedScrollingParent(TYPE_NON_TOUCH))) {
5087                     // setting state to idle will stop this.
5088                     setScrollState(SCROLL_STATE_IDLE);
5089                     if (ALLOW_THREAD_GAP_WORK) {
5090                         mPrefetchRegistry.clearPrefetchPositions();
5091                     }
5092                     stopNestedScroll(TYPE_NON_TOUCH);
5093                 } else {
5094                     postOnAnimation();
5095                     if (mGapWorker != null) {
5096                         mGapWorker.postFromTraversal(RecyclerView.this, dx, dy);
5097                     }
5098                 }
5099             }
5100             // call this after the onAnimation is complete not to have inconsistent callbacks etc.
5101             if (smoothScroller != null) {
5102                 if (smoothScroller.isPendingInitialRun()) {
5103                     smoothScroller.onAnimation(0, 0);
5104                 }
5105                 if (!mReSchedulePostAnimationCallback) {
5106                     smoothScroller.stop(); //stop if it does not trigger any scroll
5107                 }
5108             }
5109             enableRunOnAnimationRequests();
5110         }
5111 
disableRunOnAnimationRequests()5112         private void disableRunOnAnimationRequests() {
5113             mReSchedulePostAnimationCallback = false;
5114             mEatRunOnAnimationRequest = true;
5115         }
5116 
enableRunOnAnimationRequests()5117         private void enableRunOnAnimationRequests() {
5118             mEatRunOnAnimationRequest = false;
5119             if (mReSchedulePostAnimationCallback) {
5120                 postOnAnimation();
5121             }
5122         }
5123 
postOnAnimation()5124         void postOnAnimation() {
5125             if (mEatRunOnAnimationRequest) {
5126                 mReSchedulePostAnimationCallback = true;
5127             } else {
5128                 removeCallbacks(this);
5129                 ViewCompat.postOnAnimation(RecyclerView.this, this);
5130             }
5131         }
5132 
fling(int velocityX, int velocityY)5133         public void fling(int velocityX, int velocityY) {
5134             setScrollState(SCROLL_STATE_SETTLING);
5135             mLastFlingX = mLastFlingY = 0;
5136             mScroller.fling(0, 0, velocityX, velocityY,
5137                     Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
5138             postOnAnimation();
5139         }
5140 
smoothScrollBy(int dx, int dy)5141         public void smoothScrollBy(int dx, int dy) {
5142             smoothScrollBy(dx, dy, 0, 0);
5143         }
5144 
smoothScrollBy(int dx, int dy, int vx, int vy)5145         public void smoothScrollBy(int dx, int dy, int vx, int vy) {
5146             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
5147         }
5148 
distanceInfluenceForSnapDuration(float f)5149         private float distanceInfluenceForSnapDuration(float f) {
5150             f -= 0.5f; // center the values about 0.
5151             f *= 0.3f * (float) Math.PI / 2.0f;
5152             return (float) Math.sin(f);
5153         }
5154 
computeScrollDuration(int dx, int dy, int vx, int vy)5155         private int computeScrollDuration(int dx, int dy, int vx, int vy) {
5156             final int absDx = Math.abs(dx);
5157             final int absDy = Math.abs(dy);
5158             final boolean horizontal = absDx > absDy;
5159             final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
5160             final int delta = (int) Math.sqrt(dx * dx + dy * dy);
5161             final int containerSize = horizontal ? getWidth() : getHeight();
5162             final int halfContainerSize = containerSize / 2;
5163             final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
5164             final float distance = halfContainerSize + halfContainerSize
5165                     * distanceInfluenceForSnapDuration(distanceRatio);
5166 
5167             final int duration;
5168             if (velocity > 0) {
5169                 duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
5170             } else {
5171                 float absDelta = (float) (horizontal ? absDx : absDy);
5172                 duration = (int) (((absDelta / containerSize) + 1) * 300);
5173             }
5174             return Math.min(duration, MAX_SCROLL_DURATION);
5175         }
5176 
smoothScrollBy(int dx, int dy, int duration)5177         public void smoothScrollBy(int dx, int dy, int duration) {
5178             smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
5179         }
5180 
smoothScrollBy(int dx, int dy, Interpolator interpolator)5181         public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
5182             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, 0, 0),
5183                     interpolator == null ? sQuinticInterpolator : interpolator);
5184         }
5185 
smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator)5186         public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
5187             if (mInterpolator != interpolator) {
5188                 mInterpolator = interpolator;
5189                 mScroller = new OverScroller(getContext(), interpolator);
5190             }
5191             setScrollState(SCROLL_STATE_SETTLING);
5192             mLastFlingX = mLastFlingY = 0;
5193             mScroller.startScroll(0, 0, dx, dy, duration);
5194             if (Build.VERSION.SDK_INT < 23) {
5195                 // b/64931938 before API 23, startScroll() does not reset getCurX()/getCurY()
5196                 // to start values, which causes fillRemainingScrollValues() put in obsolete values
5197                 // for LayoutManager.onLayoutChildren().
5198                 mScroller.computeScrollOffset();
5199             }
5200             postOnAnimation();
5201         }
5202 
stop()5203         public void stop() {
5204             removeCallbacks(this);
5205             mScroller.abortAnimation();
5206         }
5207 
5208     }
5209 
repositionShadowingViews()5210     void repositionShadowingViews() {
5211         // Fix up shadow views used by change animations
5212         int count = mChildHelper.getChildCount();
5213         for (int i = 0; i < count; i++) {
5214             View view = mChildHelper.getChildAt(i);
5215             ViewHolder holder = getChildViewHolder(view);
5216             if (holder != null && holder.mShadowingHolder != null) {
5217                 View shadowingView = holder.mShadowingHolder.itemView;
5218                 int left = view.getLeft();
5219                 int top = view.getTop();
5220                 if (left != shadowingView.getLeft() ||  top != shadowingView.getTop()) {
5221                     shadowingView.layout(left, top,
5222                             left + shadowingView.getWidth(),
5223                             top + shadowingView.getHeight());
5224                 }
5225             }
5226         }
5227     }
5228 
5229     private class RecyclerViewDataObserver extends AdapterDataObserver {
RecyclerViewDataObserver()5230         RecyclerViewDataObserver() {
5231         }
5232 
5233         @Override
onChanged()5234         public void onChanged() {
5235             assertNotInLayoutOrScroll(null);
5236             mState.mStructureChanged = true;
5237 
5238             processDataSetCompletelyChanged(true);
5239             if (!mAdapterHelper.hasPendingUpdates()) {
5240                 requestLayout();
5241             }
5242         }
5243 
5244         @Override
onItemRangeChanged(int positionStart, int itemCount, Object payload)5245         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
5246             assertNotInLayoutOrScroll(null);
5247             if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
5248                 triggerUpdateProcessor();
5249             }
5250         }
5251 
5252         @Override
onItemRangeInserted(int positionStart, int itemCount)5253         public void onItemRangeInserted(int positionStart, int itemCount) {
5254             assertNotInLayoutOrScroll(null);
5255             if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
5256                 triggerUpdateProcessor();
5257             }
5258         }
5259 
5260         @Override
onItemRangeRemoved(int positionStart, int itemCount)5261         public void onItemRangeRemoved(int positionStart, int itemCount) {
5262             assertNotInLayoutOrScroll(null);
5263             if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
5264                 triggerUpdateProcessor();
5265             }
5266         }
5267 
5268         @Override
onItemRangeMoved(int fromPosition, int toPosition, int itemCount)5269         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
5270             assertNotInLayoutOrScroll(null);
5271             if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
5272                 triggerUpdateProcessor();
5273             }
5274         }
5275 
triggerUpdateProcessor()5276         void triggerUpdateProcessor() {
5277             if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
5278                 ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
5279             } else {
5280                 mAdapterUpdateDuringMeasure = true;
5281                 requestLayout();
5282             }
5283         }
5284     }
5285 
5286     /**
5287      * EdgeEffectFactory lets you customize the over-scroll edge effect for RecyclerViews.
5288      *
5289      * @see RecyclerView#setEdgeEffectFactory(EdgeEffectFactory)
5290      */
5291     public static class EdgeEffectFactory {
5292 
5293         @Retention(RetentionPolicy.SOURCE)
5294         @IntDef({DIRECTION_LEFT, DIRECTION_TOP, DIRECTION_RIGHT, DIRECTION_BOTTOM})
5295         public @interface EdgeDirection {}
5296 
5297         /**
5298          * Direction constant for the left edge
5299          */
5300         public static final int DIRECTION_LEFT = 0;
5301 
5302         /**
5303          * Direction constant for the top edge
5304          */
5305         public static final int DIRECTION_TOP = 1;
5306 
5307         /**
5308          * Direction constant for the right edge
5309          */
5310         public static final int DIRECTION_RIGHT = 2;
5311 
5312         /**
5313          * Direction constant for the bottom edge
5314          */
5315         public static final int DIRECTION_BOTTOM = 3;
5316 
5317         /**
5318          * Create a new EdgeEffect for the provided direction.
5319          */
createEdgeEffect(@onNull RecyclerView view, @EdgeDirection int direction)5320         protected @NonNull EdgeEffect createEdgeEffect(@NonNull RecyclerView view,
5321                 @EdgeDirection int direction) {
5322             return new EdgeEffect(view.getContext());
5323         }
5324     }
5325 
5326     /**
5327      * RecycledViewPool lets you share Views between multiple RecyclerViews.
5328      * <p>
5329      * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
5330      * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
5331      * <p>
5332      * RecyclerView automatically creates a pool for itself if you don't provide one.
5333      */
5334     public static class RecycledViewPool {
5335         private static final int DEFAULT_MAX_SCRAP = 5;
5336 
5337         /**
5338          * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
5339          *
5340          * Note that this tracks running averages of create/bind time across all RecyclerViews
5341          * (and, indirectly, Adapters) that use this pool.
5342          *
5343          * 1) This enables us to track average create and bind times across multiple adapters. Even
5344          * though create (and especially bind) may behave differently for different Adapter
5345          * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
5346          *
5347          * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
5348          * false for all other views of its type for the same deadline. This prevents items
5349          * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
5350          */
5351         static class ScrapData {
5352             final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
5353             int mMaxScrap = DEFAULT_MAX_SCRAP;
5354             long mCreateRunningAverageNs = 0;
5355             long mBindRunningAverageNs = 0;
5356         }
5357         SparseArray<ScrapData> mScrap = new SparseArray<>();
5358 
5359         private int mAttachCount = 0;
5360 
5361         /**
5362          * Discard all ViewHolders.
5363          */
clear()5364         public void clear() {
5365             for (int i = 0; i < mScrap.size(); i++) {
5366                 ScrapData data = mScrap.valueAt(i);
5367                 data.mScrapHeap.clear();
5368             }
5369         }
5370 
5371         /**
5372          * Sets the maximum number of ViewHolders to hold in the pool before discarding.
5373          *
5374          * @param viewType ViewHolder Type
5375          * @param max Maximum number
5376          */
setMaxRecycledViews(int viewType, int max)5377         public void setMaxRecycledViews(int viewType, int max) {
5378             ScrapData scrapData = getScrapDataForType(viewType);
5379             scrapData.mMaxScrap = max;
5380             final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
5381             while (scrapHeap.size() > max) {
5382                 scrapHeap.remove(scrapHeap.size() - 1);
5383             }
5384         }
5385 
5386         /**
5387          * Returns the current number of Views held by the RecycledViewPool of the given view type.
5388          */
getRecycledViewCount(int viewType)5389         public int getRecycledViewCount(int viewType) {
5390             return getScrapDataForType(viewType).mScrapHeap.size();
5391         }
5392 
5393         /**
5394          * Acquire a ViewHolder of the specified type from the pool, or {@code null} if none are
5395          * present.
5396          *
5397          * @param viewType ViewHolder type.
5398          * @return ViewHolder of the specified type acquired from the pool, or {@code null} if none
5399          * are present.
5400          */
5401         @Nullable
getRecycledView(int viewType)5402         public ViewHolder getRecycledView(int viewType) {
5403             final ScrapData scrapData = mScrap.get(viewType);
5404             if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
5405                 final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
5406                 return scrapHeap.remove(scrapHeap.size() - 1);
5407             }
5408             return null;
5409         }
5410 
5411         /**
5412          * Total number of ViewHolders held by the pool.
5413          *
5414          * @return Number of ViewHolders held by the pool.
5415          */
size()5416         int size() {
5417             int count = 0;
5418             for (int i = 0; i < mScrap.size(); i++) {
5419                 ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
5420                 if (viewHolders != null) {
5421                     count += viewHolders.size();
5422                 }
5423             }
5424             return count;
5425         }
5426 
5427         /**
5428          * Add a scrap ViewHolder to the pool.
5429          * <p>
5430          * If the pool is already full for that ViewHolder's type, it will be immediately discarded.
5431          *
5432          * @param scrap ViewHolder to be added to the pool.
5433          */
putRecycledView(ViewHolder scrap)5434         public void putRecycledView(ViewHolder scrap) {
5435             final int viewType = scrap.getItemViewType();
5436             final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
5437             if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
5438                 return;
5439             }
5440             if (DEBUG && scrapHeap.contains(scrap)) {
5441                 throw new IllegalArgumentException("this scrap item already exists");
5442             }
5443             scrap.resetInternal();
5444             scrapHeap.add(scrap);
5445         }
5446 
runningAverage(long oldAverage, long newValue)5447         long runningAverage(long oldAverage, long newValue) {
5448             if (oldAverage == 0) {
5449                 return newValue;
5450             }
5451             return (oldAverage / 4 * 3) + (newValue / 4);
5452         }
5453 
factorInCreateTime(int viewType, long createTimeNs)5454         void factorInCreateTime(int viewType, long createTimeNs) {
5455             ScrapData scrapData = getScrapDataForType(viewType);
5456             scrapData.mCreateRunningAverageNs = runningAverage(
5457                     scrapData.mCreateRunningAverageNs, createTimeNs);
5458         }
5459 
factorInBindTime(int viewType, long bindTimeNs)5460         void factorInBindTime(int viewType, long bindTimeNs) {
5461             ScrapData scrapData = getScrapDataForType(viewType);
5462             scrapData.mBindRunningAverageNs = runningAverage(
5463                     scrapData.mBindRunningAverageNs, bindTimeNs);
5464         }
5465 
willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs)5466         boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5467             long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
5468             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5469         }
5470 
willBindInTime(int viewType, long approxCurrentNs, long deadlineNs)5471         boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5472             long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
5473             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5474         }
5475 
attach()5476         void attach() {
5477             mAttachCount++;
5478         }
5479 
detach()5480         void detach() {
5481             mAttachCount--;
5482         }
5483 
5484 
5485         /**
5486          * Detaches the old adapter and attaches the new one.
5487          * <p>
5488          * RecycledViewPool will clear its cache if it has only one adapter attached and the new
5489          * adapter uses a different ViewHolder than the oldAdapter.
5490          *
5491          * @param oldAdapter The previous adapter instance. Will be detached.
5492          * @param newAdapter The new adapter instance. Will be attached.
5493          * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
5494          *                               ViewHolder and view types.
5495          */
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, boolean compatibleWithPrevious)5496         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
5497                 boolean compatibleWithPrevious) {
5498             if (oldAdapter != null) {
5499                 detach();
5500             }
5501             if (!compatibleWithPrevious && mAttachCount == 0) {
5502                 clear();
5503             }
5504             if (newAdapter != null) {
5505                 attach();
5506             }
5507         }
5508 
getScrapDataForType(int viewType)5509         private ScrapData getScrapDataForType(int viewType) {
5510             ScrapData scrapData = mScrap.get(viewType);
5511             if (scrapData == null) {
5512                 scrapData = new ScrapData();
5513                 mScrap.put(viewType, scrapData);
5514             }
5515             return scrapData;
5516         }
5517     }
5518 
5519     /**
5520      * Utility method for finding an internal RecyclerView, if present
5521      */
5522     @Nullable
findNestedRecyclerView(@onNull View view)5523     static RecyclerView findNestedRecyclerView(@NonNull View view) {
5524         if (!(view instanceof ViewGroup)) {
5525             return null;
5526         }
5527         if (view instanceof RecyclerView) {
5528             return (RecyclerView) view;
5529         }
5530         final ViewGroup parent = (ViewGroup) view;
5531         final int count = parent.getChildCount();
5532         for (int i = 0; i < count; i++) {
5533             final View child = parent.getChildAt(i);
5534             final RecyclerView descendant = findNestedRecyclerView(child);
5535             if (descendant != null) {
5536                 return descendant;
5537             }
5538         }
5539         return null;
5540     }
5541 
5542     /**
5543      * Utility method for clearing holder's internal RecyclerView, if present
5544      */
clearNestedRecyclerViewIfNotNested(@onNull ViewHolder holder)5545     static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
5546         if (holder.mNestedRecyclerView != null) {
5547             View item = holder.mNestedRecyclerView.get();
5548             while (item != null) {
5549                 if (item == holder.itemView) {
5550                     return; // match found, don't need to clear
5551                 }
5552 
5553                 ViewParent parent = item.getParent();
5554                 if (parent instanceof View) {
5555                     item = (View) parent;
5556                 } else {
5557                     item = null;
5558                 }
5559             }
5560             holder.mNestedRecyclerView = null; // not nested
5561         }
5562     }
5563 
5564     /**
5565      * Time base for deadline-aware work scheduling. Overridable for testing.
5566      *
5567      * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
5568      * isn't relevant.
5569      */
getNanoTime()5570     long getNanoTime() {
5571         if (ALLOW_THREAD_GAP_WORK) {
5572             return System.nanoTime();
5573         } else {
5574             return 0;
5575         }
5576     }
5577 
5578     /**
5579      * A Recycler is responsible for managing scrapped or detached item views for reuse.
5580      *
5581      * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
5582      * that has been marked for removal or reuse.</p>
5583      *
5584      * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
5585      * an adapter's data set representing the data at a given position or item ID.
5586      * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
5587      * If not, the view can be quickly reused by the LayoutManager with no further work.
5588      * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
5589      * may be repositioned by a LayoutManager without remeasurement.</p>
5590      */
5591     public final class Recycler {
5592         final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
5593         ArrayList<ViewHolder> mChangedScrap = null;
5594 
5595         final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
5596 
5597         private final List<ViewHolder>
5598                 mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
5599 
5600         private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
5601         int mViewCacheMax = DEFAULT_CACHE_SIZE;
5602 
5603         RecycledViewPool mRecyclerPool;
5604 
5605         private ViewCacheExtension mViewCacheExtension;
5606 
5607         static final int DEFAULT_CACHE_SIZE = 2;
5608 
5609         /**
5610          * Clear scrap views out of this recycler. Detached views contained within a
5611          * recycled view pool will remain.
5612          */
clear()5613         public void clear() {
5614             mAttachedScrap.clear();
5615             recycleAndClearCachedViews();
5616         }
5617 
5618         /**
5619          * Set the maximum number of detached, valid views we should retain for later use.
5620          *
5621          * @param viewCount Number of views to keep before sending views to the shared pool
5622          */
setViewCacheSize(int viewCount)5623         public void setViewCacheSize(int viewCount) {
5624             mRequestedCacheMax = viewCount;
5625             updateViewCacheSize();
5626         }
5627 
updateViewCacheSize()5628         void updateViewCacheSize() {
5629             int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
5630             mViewCacheMax = mRequestedCacheMax + extraCache;
5631 
5632             // first, try the views that can be recycled
5633             for (int i = mCachedViews.size() - 1;
5634                     i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
5635                 recycleCachedViewAt(i);
5636             }
5637         }
5638 
5639         /**
5640          * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
5641          *
5642          * @return List of ViewHolders in the scrap list.
5643          */
5644         @NonNull
getScrapList()5645         public List<ViewHolder> getScrapList() {
5646             return mUnmodifiableAttachedScrap;
5647         }
5648 
5649         /**
5650          * Helper method for getViewForPosition.
5651          * <p>
5652          * Checks whether a given view holder can be used for the provided position.
5653          *
5654          * @param holder ViewHolder
5655          * @return true if ViewHolder matches the provided position, false otherwise
5656          */
validateViewHolderForOffsetPosition(ViewHolder holder)5657         boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
5658             // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
5659             // if it is not removed, verify the type and id.
5660             if (holder.isRemoved()) {
5661                 if (DEBUG && !mState.isPreLayout()) {
5662                     throw new IllegalStateException("should not receive a removed view unless it"
5663                             + " is pre layout" + exceptionLabel());
5664                 }
5665                 return mState.isPreLayout();
5666             }
5667             if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
5668                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
5669                         + "adapter position" + holder + exceptionLabel());
5670             }
5671             if (!mState.isPreLayout()) {
5672                 // don't check type if it is pre-layout.
5673                 final int type = mAdapter.getItemViewType(holder.mPosition);
5674                 if (type != holder.getItemViewType()) {
5675                     return false;
5676                 }
5677             }
5678             if (mAdapter.hasStableIds()) {
5679                 return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
5680             }
5681             return true;
5682         }
5683 
5684         /**
5685          * Attempts to bind view, and account for relevant timing information. If
5686          * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
5687          *
5688          * @param holder Holder to be bound.
5689          * @param offsetPosition Position of item to be bound.
5690          * @param position Pre-layout position of item to be bound.
5691          * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5692          *                   complete. If FOREVER_NS is passed, this method will not fail to
5693          *                   bind the holder.
5694          * @return
5695          */
tryBindViewHolderByDeadline(@onNull ViewHolder holder, int offsetPosition, int position, long deadlineNs)5696         private boolean tryBindViewHolderByDeadline(@NonNull ViewHolder holder, int offsetPosition,
5697                 int position, long deadlineNs) {
5698             holder.mOwnerRecyclerView = RecyclerView.this;
5699             final int viewType = holder.getItemViewType();
5700             long startBindNs = getNanoTime();
5701             if (deadlineNs != FOREVER_NS
5702                     && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
5703                 // abort - we have a deadline we can't meet
5704                 return false;
5705             }
5706             mAdapter.bindViewHolder(holder, offsetPosition);
5707             long endBindNs = getNanoTime();
5708             mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
5709             attachAccessibilityDelegateOnBind(holder);
5710             if (mState.isPreLayout()) {
5711                 holder.mPreLayoutPosition = position;
5712             }
5713             return true;
5714         }
5715 
5716         /**
5717          * Binds the given View to the position. The View can be a View previously retrieved via
5718          * {@link #getViewForPosition(int)} or created by
5719          * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
5720          * <p>
5721          * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
5722          * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
5723          * wants to handle its own recycling logic.
5724          * <p>
5725          * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
5726          * you don't need to call this method unless you want to bind this View to another position.
5727          *
5728          * @param view The view to update.
5729          * @param position The position of the item to bind to this View.
5730          */
bindViewToPosition(@onNull View view, int position)5731         public void bindViewToPosition(@NonNull View view, int position) {
5732             ViewHolder holder = getChildViewHolderInt(view);
5733             if (holder == null) {
5734                 throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
5735                         + " pass arbitrary views to this method, they should be created by the "
5736                         + "Adapter" + exceptionLabel());
5737             }
5738             final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5739             if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5740                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5741                         + "position " + position + "(offset:" + offsetPosition + ")."
5742                         + "state:" + mState.getItemCount() + exceptionLabel());
5743             }
5744             tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
5745 
5746             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5747             final LayoutParams rvLayoutParams;
5748             if (lp == null) {
5749                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5750                 holder.itemView.setLayoutParams(rvLayoutParams);
5751             } else if (!checkLayoutParams(lp)) {
5752                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5753                 holder.itemView.setLayoutParams(rvLayoutParams);
5754             } else {
5755                 rvLayoutParams = (LayoutParams) lp;
5756             }
5757 
5758             rvLayoutParams.mInsetsDirty = true;
5759             rvLayoutParams.mViewHolder = holder;
5760             rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
5761         }
5762 
5763         /**
5764          * RecyclerView provides artificial position range (item count) in pre-layout state and
5765          * automatically maps these positions to {@link Adapter} positions when
5766          * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
5767          * <p>
5768          * Usually, LayoutManager does not need to worry about this. However, in some cases, your
5769          * LayoutManager may need to call some custom component with item positions in which
5770          * case you need the actual adapter position instead of the pre layout position. You
5771          * can use this method to convert a pre-layout position to adapter (post layout) position.
5772          * <p>
5773          * Note that if the provided position belongs to a deleted ViewHolder, this method will
5774          * return -1.
5775          * <p>
5776          * Calling this method in post-layout state returns the same value back.
5777          *
5778          * @param position The pre-layout position to convert. Must be greater or equal to 0 and
5779          *                 less than {@link State#getItemCount()}.
5780          */
convertPreLayoutPositionToPostLayout(int position)5781         public int convertPreLayoutPositionToPostLayout(int position) {
5782             if (position < 0 || position >= mState.getItemCount()) {
5783                 throw new IndexOutOfBoundsException("invalid position " + position + ". State "
5784                         + "item count is " + mState.getItemCount() + exceptionLabel());
5785             }
5786             if (!mState.isPreLayout()) {
5787                 return position;
5788             }
5789             return mAdapterHelper.findPositionOffset(position);
5790         }
5791 
5792         /**
5793          * Obtain a view initialized for the given position.
5794          *
5795          * This method should be used by {@link LayoutManager} implementations to obtain
5796          * views to represent data from an {@link Adapter}.
5797          * <p>
5798          * The Recycler may reuse a scrap or detached view from a shared pool if one is
5799          * available for the correct view type. If the adapter has not indicated that the
5800          * data at the given position has changed, the Recycler will attempt to hand back
5801          * a scrap view that was previously initialized for that data without rebinding.
5802          *
5803          * @param position Position to obtain a view for
5804          * @return A view representing the data at <code>position</code> from <code>adapter</code>
5805          */
5806         @NonNull
getViewForPosition(int position)5807         public View getViewForPosition(int position) {
5808             return getViewForPosition(position, false);
5809         }
5810 
getViewForPosition(int position, boolean dryRun)5811         View getViewForPosition(int position, boolean dryRun) {
5812             return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
5813         }
5814 
5815         /**
5816          * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
5817          * cache, the RecycledViewPool, or creating it directly.
5818          * <p>
5819          * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
5820          * rather than constructing or binding a ViewHolder if it doesn't think it has time.
5821          * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
5822          * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
5823          * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
5824          *
5825          * @param position Position of ViewHolder to be returned.
5826          * @param dryRun True if the ViewHolder should not be removed from scrap/cache/
5827          * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5828          *                   complete. If FOREVER_NS is passed, this method will not fail to
5829          *                   create/bind the holder if needed.
5830          *
5831          * @return ViewHolder for requested position
5832          */
5833         @Nullable
tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs)5834         ViewHolder tryGetViewHolderForPositionByDeadline(int position,
5835                 boolean dryRun, long deadlineNs) {
5836             if (position < 0 || position >= mState.getItemCount()) {
5837                 throw new IndexOutOfBoundsException("Invalid item position " + position
5838                         + "(" + position + "). Item count:" + mState.getItemCount()
5839                         + exceptionLabel());
5840             }
5841             boolean fromScrapOrHiddenOrCache = false;
5842             ViewHolder holder = null;
5843             // 0) If there is a changed scrap, try to find from there
5844             if (mState.isPreLayout()) {
5845                 holder = getChangedScrapViewForPosition(position);
5846                 fromScrapOrHiddenOrCache = holder != null;
5847             }
5848             // 1) Find by position from scrap/hidden list/cache
5849             if (holder == null) {
5850                 holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
5851                 if (holder != null) {
5852                     if (!validateViewHolderForOffsetPosition(holder)) {
5853                         // recycle holder (and unscrap if relevant) since it can't be used
5854                         if (!dryRun) {
5855                             // we would like to recycle this but need to make sure it is not used by
5856                             // animation logic etc.
5857                             holder.addFlags(ViewHolder.FLAG_INVALID);
5858                             if (holder.isScrap()) {
5859                                 removeDetachedView(holder.itemView, false);
5860                                 holder.unScrap();
5861                             } else if (holder.wasReturnedFromScrap()) {
5862                                 holder.clearReturnedFromScrapFlag();
5863                             }
5864                             recycleViewHolderInternal(holder);
5865                         }
5866                         holder = null;
5867                     } else {
5868                         fromScrapOrHiddenOrCache = true;
5869                     }
5870                 }
5871             }
5872             if (holder == null) {
5873                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5874                 if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5875                     throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5876                             + "position " + position + "(offset:" + offsetPosition + ")."
5877                             + "state:" + mState.getItemCount() + exceptionLabel());
5878                 }
5879 
5880                 final int type = mAdapter.getItemViewType(offsetPosition);
5881                 // 2) Find from scrap/cache via stable ids, if exists
5882                 if (mAdapter.hasStableIds()) {
5883                     holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
5884                             type, dryRun);
5885                     if (holder != null) {
5886                         // update position
5887                         holder.mPosition = offsetPosition;
5888                         fromScrapOrHiddenOrCache = true;
5889                     }
5890                 }
5891                 if (holder == null && mViewCacheExtension != null) {
5892                     // We are NOT sending the offsetPosition because LayoutManager does not
5893                     // know it.
5894                     final View view = mViewCacheExtension
5895                             .getViewForPositionAndType(this, position, type);
5896                     if (view != null) {
5897                         holder = getChildViewHolder(view);
5898                         if (holder == null) {
5899                             throw new IllegalArgumentException("getViewForPositionAndType returned"
5900                                     + " a view which does not have a ViewHolder"
5901                                     + exceptionLabel());
5902                         } else if (holder.shouldIgnore()) {
5903                             throw new IllegalArgumentException("getViewForPositionAndType returned"
5904                                     + " a view that is ignored. You must call stopIgnoring before"
5905                                     + " returning this view." + exceptionLabel());
5906                         }
5907                     }
5908                 }
5909                 if (holder == null) { // fallback to pool
5910                     if (DEBUG) {
5911                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
5912                                 + position + ") fetching from shared pool");
5913                     }
5914                     holder = getRecycledViewPool().getRecycledView(type);
5915                     if (holder != null) {
5916                         holder.resetInternal();
5917                         if (FORCE_INVALIDATE_DISPLAY_LIST) {
5918                             invalidateDisplayListInt(holder);
5919                         }
5920                     }
5921                 }
5922                 if (holder == null) {
5923                     long start = getNanoTime();
5924                     if (deadlineNs != FOREVER_NS
5925                             && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
5926                         // abort - we have a deadline we can't meet
5927                         return null;
5928                     }
5929                     holder = mAdapter.createViewHolder(RecyclerView.this, type);
5930                     if (ALLOW_THREAD_GAP_WORK) {
5931                         // only bother finding nested RV if prefetching
5932                         RecyclerView innerView = findNestedRecyclerView(holder.itemView);
5933                         if (innerView != null) {
5934                             holder.mNestedRecyclerView = new WeakReference<>(innerView);
5935                         }
5936                     }
5937 
5938                     long end = getNanoTime();
5939                     mRecyclerPool.factorInCreateTime(type, end - start);
5940                     if (DEBUG) {
5941                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
5942                     }
5943                 }
5944             }
5945 
5946             // This is very ugly but the only place we can grab this information
5947             // before the View is rebound and returned to the LayoutManager for post layout ops.
5948             // We don't need this in pre-layout since the VH is not updated by the LM.
5949             if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
5950                     .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
5951                 holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5952                 if (mState.mRunSimpleAnimations) {
5953                     int changeFlags = ItemAnimator
5954                             .buildAdapterChangeFlagsForAnimations(holder);
5955                     changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
5956                     final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
5957                             holder, changeFlags, holder.getUnmodifiedPayloads());
5958                     recordAnimationInfoIfBouncedHiddenView(holder, info);
5959                 }
5960             }
5961 
5962             boolean bound = false;
5963             if (mState.isPreLayout() && holder.isBound()) {
5964                 // do not update unless we absolutely have to.
5965                 holder.mPreLayoutPosition = position;
5966             } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
5967                 if (DEBUG && holder.isRemoved()) {
5968                     throw new IllegalStateException("Removed holder should be bound and it should"
5969                             + " come here only in pre-layout. Holder: " + holder
5970                             + exceptionLabel());
5971                 }
5972                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5973                 bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
5974             }
5975 
5976             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5977             final LayoutParams rvLayoutParams;
5978             if (lp == null) {
5979                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5980                 holder.itemView.setLayoutParams(rvLayoutParams);
5981             } else if (!checkLayoutParams(lp)) {
5982                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5983                 holder.itemView.setLayoutParams(rvLayoutParams);
5984             } else {
5985                 rvLayoutParams = (LayoutParams) lp;
5986             }
5987             rvLayoutParams.mViewHolder = holder;
5988             rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
5989             return holder;
5990         }
5991 
attachAccessibilityDelegateOnBind(ViewHolder holder)5992         private void attachAccessibilityDelegateOnBind(ViewHolder holder) {
5993             if (isAccessibilityEnabled()) {
5994                 final View itemView = holder.itemView;
5995                 if (ViewCompat.getImportantForAccessibility(itemView)
5996                         == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
5997                     ViewCompat.setImportantForAccessibility(itemView,
5998                             ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
5999                 }
6000                 if (!ViewCompat.hasAccessibilityDelegate(itemView)) {
6001                     holder.addFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
6002                     ViewCompat.setAccessibilityDelegate(itemView,
6003                             mAccessibilityDelegate.getItemDelegate());
6004                 }
6005             }
6006         }
6007 
invalidateDisplayListInt(ViewHolder holder)6008         private void invalidateDisplayListInt(ViewHolder holder) {
6009             if (holder.itemView instanceof ViewGroup) {
6010                 invalidateDisplayListInt((ViewGroup) holder.itemView, false);
6011             }
6012         }
6013 
invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis)6014         private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
6015             for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
6016                 final View view = viewGroup.getChildAt(i);
6017                 if (view instanceof ViewGroup) {
6018                     invalidateDisplayListInt((ViewGroup) view, true);
6019                 }
6020             }
6021             if (!invalidateThis) {
6022                 return;
6023             }
6024             // we need to force it to become invisible
6025             if (viewGroup.getVisibility() == View.INVISIBLE) {
6026                 viewGroup.setVisibility(View.VISIBLE);
6027                 viewGroup.setVisibility(View.INVISIBLE);
6028             } else {
6029                 final int visibility = viewGroup.getVisibility();
6030                 viewGroup.setVisibility(View.INVISIBLE);
6031                 viewGroup.setVisibility(visibility);
6032             }
6033         }
6034 
6035         /**
6036          * Recycle a detached view. The specified view will be added to a pool of views
6037          * for later rebinding and reuse.
6038          *
6039          * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
6040          * View is scrapped, it will be removed from scrap list.</p>
6041          *
6042          * @param view Removed view for recycling
6043          * @see LayoutManager#removeAndRecycleView(View, Recycler)
6044          */
recycleView(@onNull View view)6045         public void recycleView(@NonNull View view) {
6046             // This public recycle method tries to make view recycle-able since layout manager
6047             // intended to recycle this view (e.g. even if it is in scrap or change cache)
6048             ViewHolder holder = getChildViewHolderInt(view);
6049             if (holder.isTmpDetached()) {
6050                 removeDetachedView(view, false);
6051             }
6052             if (holder.isScrap()) {
6053                 holder.unScrap();
6054             } else if (holder.wasReturnedFromScrap()) {
6055                 holder.clearReturnedFromScrapFlag();
6056             }
6057             recycleViewHolderInternal(holder);
6058         }
6059 
6060         /**
6061          * Internally, use this method instead of {@link #recycleView(android.view.View)} to
6062          * catch potential bugs.
6063          * @param view
6064          */
recycleViewInternal(View view)6065         void recycleViewInternal(View view) {
6066             recycleViewHolderInternal(getChildViewHolderInt(view));
6067         }
6068 
recycleAndClearCachedViews()6069         void recycleAndClearCachedViews() {
6070             final int count = mCachedViews.size();
6071             for (int i = count - 1; i >= 0; i--) {
6072                 recycleCachedViewAt(i);
6073             }
6074             mCachedViews.clear();
6075             if (ALLOW_THREAD_GAP_WORK) {
6076                 mPrefetchRegistry.clearPrefetchPositions();
6077             }
6078         }
6079 
6080         /**
6081          * Recycles a cached view and removes the view from the list. Views are added to cache
6082          * if and only if they are recyclable, so this method does not check it again.
6083          * <p>
6084          * A small exception to this rule is when the view does not have an animator reference
6085          * but transient state is true (due to animations created outside ItemAnimator). In that
6086          * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
6087          * still recyclable since Adapter wants to do so.
6088          *
6089          * @param cachedViewIndex The index of the view in cached views list
6090          */
recycleCachedViewAt(int cachedViewIndex)6091         void recycleCachedViewAt(int cachedViewIndex) {
6092             if (DEBUG) {
6093                 Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
6094             }
6095             ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
6096             if (DEBUG) {
6097                 Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
6098             }
6099             addViewHolderToRecycledViewPool(viewHolder, true);
6100             mCachedViews.remove(cachedViewIndex);
6101         }
6102 
6103         /**
6104          * internal implementation checks if view is scrapped or attached and throws an exception
6105          * if so.
6106          * Public version un-scraps before calling recycle.
6107          */
recycleViewHolderInternal(ViewHolder holder)6108         void recycleViewHolderInternal(ViewHolder holder) {
6109             if (holder.isScrap() || holder.itemView.getParent() != null) {
6110                 throw new IllegalArgumentException(
6111                         "Scrapped or attached views may not be recycled. isScrap:"
6112                                 + holder.isScrap() + " isAttached:"
6113                                 + (holder.itemView.getParent() != null) + exceptionLabel());
6114             }
6115 
6116             if (holder.isTmpDetached()) {
6117                 throw new IllegalArgumentException("Tmp detached view should be removed "
6118                         + "from RecyclerView before it can be recycled: " + holder
6119                         + exceptionLabel());
6120             }
6121 
6122             if (holder.shouldIgnore()) {
6123                 throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
6124                         + " should first call stopIgnoringView(view) before calling recycle."
6125                         + exceptionLabel());
6126             }
6127             //noinspection unchecked
6128             final boolean transientStatePreventsRecycling = holder
6129                     .doesTransientStatePreventRecycling();
6130             final boolean forceRecycle = mAdapter != null
6131                     && transientStatePreventsRecycling
6132                     && mAdapter.onFailedToRecycleView(holder);
6133             boolean cached = false;
6134             boolean recycled = false;
6135             if (DEBUG && mCachedViews.contains(holder)) {
6136                 throw new IllegalArgumentException("cached view received recycle internal? "
6137                         + holder + exceptionLabel());
6138             }
6139             if (forceRecycle || holder.isRecyclable()) {
6140                 if (mViewCacheMax > 0
6141                         && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
6142                         | ViewHolder.FLAG_REMOVED
6143                         | ViewHolder.FLAG_UPDATE
6144                         | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
6145                     // Retire oldest cached view
6146                     int cachedViewSize = mCachedViews.size();
6147                     if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
6148                         recycleCachedViewAt(0);
6149                         cachedViewSize--;
6150                     }
6151 
6152                     int targetCacheIndex = cachedViewSize;
6153                     if (ALLOW_THREAD_GAP_WORK
6154                             && cachedViewSize > 0
6155                             && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
6156                         // when adding the view, skip past most recently prefetched views
6157                         int cacheIndex = cachedViewSize - 1;
6158                         while (cacheIndex >= 0) {
6159                             int cachedPos = mCachedViews.get(cacheIndex).mPosition;
6160                             if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
6161                                 break;
6162                             }
6163                             cacheIndex--;
6164                         }
6165                         targetCacheIndex = cacheIndex + 1;
6166                     }
6167                     mCachedViews.add(targetCacheIndex, holder);
6168                     cached = true;
6169                 }
6170                 if (!cached) {
6171                     addViewHolderToRecycledViewPool(holder, true);
6172                     recycled = true;
6173                 }
6174             } else {
6175                 // NOTE: A view can fail to be recycled when it is scrolled off while an animation
6176                 // runs. In this case, the item is eventually recycled by
6177                 // ItemAnimatorRestoreListener#onAnimationFinished.
6178 
6179                 // TODO: consider cancelling an animation when an item is removed scrollBy,
6180                 // to return it to the pool faster
6181                 if (DEBUG) {
6182                     Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
6183                             + "re-visit here. We are still removing it from animation lists"
6184                             + exceptionLabel());
6185                 }
6186             }
6187             // even if the holder is not removed, we still call this method so that it is removed
6188             // from view holder lists.
6189             mViewInfoStore.removeViewHolder(holder);
6190             if (!cached && !recycled && transientStatePreventsRecycling) {
6191                 holder.mOwnerRecyclerView = null;
6192             }
6193         }
6194 
6195         /**
6196          * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
6197          *
6198          * Pass false to dispatchRecycled for views that have not been bound.
6199          *
6200          * @param holder Holder to be added to the pool.
6201          * @param dispatchRecycled True to dispatch View recycled callbacks.
6202          */
addViewHolderToRecycledViewPool(@onNull ViewHolder holder, boolean dispatchRecycled)6203         void addViewHolderToRecycledViewPool(@NonNull ViewHolder holder, boolean dispatchRecycled) {
6204             clearNestedRecyclerViewIfNotNested(holder);
6205             if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE)) {
6206                 holder.setFlags(0, ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
6207                 ViewCompat.setAccessibilityDelegate(holder.itemView, null);
6208             }
6209             if (dispatchRecycled) {
6210                 dispatchViewRecycled(holder);
6211             }
6212             holder.mOwnerRecyclerView = null;
6213             getRecycledViewPool().putRecycledView(holder);
6214         }
6215 
6216         /**
6217          * Used as a fast path for unscrapping and recycling a view during a bulk operation.
6218          * The caller must call {@link #clearScrap()} when it's done to update the recycler's
6219          * internal bookkeeping.
6220          */
quickRecycleScrapView(View view)6221         void quickRecycleScrapView(View view) {
6222             final ViewHolder holder = getChildViewHolderInt(view);
6223             holder.mScrapContainer = null;
6224             holder.mInChangeScrap = false;
6225             holder.clearReturnedFromScrapFlag();
6226             recycleViewHolderInternal(holder);
6227         }
6228 
6229         /**
6230          * Mark an attached view as scrap.
6231          *
6232          * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
6233          * for rebinding and reuse. Requests for a view for a given position may return a
6234          * reused or rebound scrap view instance.</p>
6235          *
6236          * @param view View to scrap
6237          */
scrapView(View view)6238         void scrapView(View view) {
6239             final ViewHolder holder = getChildViewHolderInt(view);
6240             if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
6241                     || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
6242                 if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
6243                     throw new IllegalArgumentException("Called scrap view with an invalid view."
6244                             + " Invalid views cannot be reused from scrap, they should rebound from"
6245                             + " recycler pool." + exceptionLabel());
6246                 }
6247                 holder.setScrapContainer(this, false);
6248                 mAttachedScrap.add(holder);
6249             } else {
6250                 if (mChangedScrap == null) {
6251                     mChangedScrap = new ArrayList<ViewHolder>();
6252                 }
6253                 holder.setScrapContainer(this, true);
6254                 mChangedScrap.add(holder);
6255             }
6256         }
6257 
6258         /**
6259          * Remove a previously scrapped view from the pool of eligible scrap.
6260          *
6261          * <p>This view will no longer be eligible for reuse until re-scrapped or
6262          * until it is explicitly removed and recycled.</p>
6263          */
unscrapView(ViewHolder holder)6264         void unscrapView(ViewHolder holder) {
6265             if (holder.mInChangeScrap) {
6266                 mChangedScrap.remove(holder);
6267             } else {
6268                 mAttachedScrap.remove(holder);
6269             }
6270             holder.mScrapContainer = null;
6271             holder.mInChangeScrap = false;
6272             holder.clearReturnedFromScrapFlag();
6273         }
6274 
getScrapCount()6275         int getScrapCount() {
6276             return mAttachedScrap.size();
6277         }
6278 
getScrapViewAt(int index)6279         View getScrapViewAt(int index) {
6280             return mAttachedScrap.get(index).itemView;
6281         }
6282 
clearScrap()6283         void clearScrap() {
6284             mAttachedScrap.clear();
6285             if (mChangedScrap != null) {
6286                 mChangedScrap.clear();
6287             }
6288         }
6289 
getChangedScrapViewForPosition(int position)6290         ViewHolder getChangedScrapViewForPosition(int position) {
6291             // If pre-layout, check the changed scrap for an exact match.
6292             final int changedScrapSize;
6293             if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
6294                 return null;
6295             }
6296             // find by position
6297             for (int i = 0; i < changedScrapSize; i++) {
6298                 final ViewHolder holder = mChangedScrap.get(i);
6299                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
6300                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6301                     return holder;
6302                 }
6303             }
6304             // find by id
6305             if (mAdapter.hasStableIds()) {
6306                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
6307                 if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
6308                     final long id = mAdapter.getItemId(offsetPosition);
6309                     for (int i = 0; i < changedScrapSize; i++) {
6310                         final ViewHolder holder = mChangedScrap.get(i);
6311                         if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
6312                             holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6313                             return holder;
6314                         }
6315                     }
6316                 }
6317             }
6318             return null;
6319         }
6320 
6321         /**
6322          * Returns a view for the position either from attach scrap, hidden children, or cache.
6323          *
6324          * @param position Item position
6325          * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
6326          * @return a ViewHolder that can be re-used for this position.
6327          */
getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun)6328         ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
6329             final int scrapCount = mAttachedScrap.size();
6330 
6331             // Try first for an exact, non-invalid match from scrap.
6332             for (int i = 0; i < scrapCount; i++) {
6333                 final ViewHolder holder = mAttachedScrap.get(i);
6334                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
6335                         && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
6336                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6337                     return holder;
6338                 }
6339             }
6340 
6341             if (!dryRun) {
6342                 View view = mChildHelper.findHiddenNonRemovedView(position);
6343                 if (view != null) {
6344                     // This View is good to be used. We just need to unhide, detach and move to the
6345                     // scrap list.
6346                     final ViewHolder vh = getChildViewHolderInt(view);
6347                     mChildHelper.unhide(view);
6348                     int layoutIndex = mChildHelper.indexOfChild(view);
6349                     if (layoutIndex == RecyclerView.NO_POSITION) {
6350                         throw new IllegalStateException("layout index should not be -1 after "
6351                                 + "unhiding a view:" + vh + exceptionLabel());
6352                     }
6353                     mChildHelper.detachViewFromParent(layoutIndex);
6354                     scrapView(view);
6355                     vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
6356                             | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
6357                     return vh;
6358                 }
6359             }
6360 
6361             // Search in our first-level recycled view cache.
6362             final int cacheSize = mCachedViews.size();
6363             for (int i = 0; i < cacheSize; i++) {
6364                 final ViewHolder holder = mCachedViews.get(i);
6365                 // invalid view holders may be in cache if adapter has stable ids as they can be
6366                 // retrieved via getScrapOrCachedViewForId
6367                 if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
6368                     if (!dryRun) {
6369                         mCachedViews.remove(i);
6370                     }
6371                     if (DEBUG) {
6372                         Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
6373                                 + ") found match in cache: " + holder);
6374                     }
6375                     return holder;
6376                 }
6377             }
6378             return null;
6379         }
6380 
getScrapOrCachedViewForId(long id, int type, boolean dryRun)6381         ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
6382             // Look in our attached views first
6383             final int count = mAttachedScrap.size();
6384             for (int i = count - 1; i >= 0; i--) {
6385                 final ViewHolder holder = mAttachedScrap.get(i);
6386                 if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
6387                     if (type == holder.getItemViewType()) {
6388                         holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6389                         if (holder.isRemoved()) {
6390                             // this might be valid in two cases:
6391                             // > item is removed but we are in pre-layout pass
6392                             // >> do nothing. return as is. make sure we don't rebind
6393                             // > item is removed then added to another position and we are in
6394                             // post layout.
6395                             // >> remove removed and invalid flags, add update flag to rebind
6396                             // because item was invisible to us and we don't know what happened in
6397                             // between.
6398                             if (!mState.isPreLayout()) {
6399                                 holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
6400                                         | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
6401                             }
6402                         }
6403                         return holder;
6404                     } else if (!dryRun) {
6405                         // if we are running animations, it is actually better to keep it in scrap
6406                         // but this would force layout manager to lay it out which would be bad.
6407                         // Recycle this scrap. Type mismatch.
6408                         mAttachedScrap.remove(i);
6409                         removeDetachedView(holder.itemView, false);
6410                         quickRecycleScrapView(holder.itemView);
6411                     }
6412                 }
6413             }
6414 
6415             // Search the first-level cache
6416             final int cacheSize = mCachedViews.size();
6417             for (int i = cacheSize - 1; i >= 0; i--) {
6418                 final ViewHolder holder = mCachedViews.get(i);
6419                 if (holder.getItemId() == id) {
6420                     if (type == holder.getItemViewType()) {
6421                         if (!dryRun) {
6422                             mCachedViews.remove(i);
6423                         }
6424                         return holder;
6425                     } else if (!dryRun) {
6426                         recycleCachedViewAt(i);
6427                         return null;
6428                     }
6429                 }
6430             }
6431             return null;
6432         }
6433 
dispatchViewRecycled(@onNull ViewHolder holder)6434         void dispatchViewRecycled(@NonNull ViewHolder holder) {
6435             if (mRecyclerListener != null) {
6436                 mRecyclerListener.onViewRecycled(holder);
6437             }
6438             if (mAdapter != null) {
6439                 mAdapter.onViewRecycled(holder);
6440             }
6441             if (mState != null) {
6442                 mViewInfoStore.removeViewHolder(holder);
6443             }
6444             if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
6445         }
6446 
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, boolean compatibleWithPrevious)6447         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
6448                 boolean compatibleWithPrevious) {
6449             clear();
6450             getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
6451         }
6452 
offsetPositionRecordsForMove(int from, int to)6453         void offsetPositionRecordsForMove(int from, int to) {
6454             final int start, end, inBetweenOffset;
6455             if (from < to) {
6456                 start = from;
6457                 end = to;
6458                 inBetweenOffset = -1;
6459             } else {
6460                 start = to;
6461                 end = from;
6462                 inBetweenOffset = 1;
6463             }
6464             final int cachedCount = mCachedViews.size();
6465             for (int i = 0; i < cachedCount; i++) {
6466                 final ViewHolder holder = mCachedViews.get(i);
6467                 if (holder == null || holder.mPosition < start || holder.mPosition > end) {
6468                     continue;
6469                 }
6470                 if (holder.mPosition == from) {
6471                     holder.offsetPosition(to - from, false);
6472                 } else {
6473                     holder.offsetPosition(inBetweenOffset, false);
6474                 }
6475                 if (DEBUG) {
6476                     Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
6477                             + holder);
6478                 }
6479             }
6480         }
6481 
offsetPositionRecordsForInsert(int insertedAt, int count)6482         void offsetPositionRecordsForInsert(int insertedAt, int count) {
6483             final int cachedCount = mCachedViews.size();
6484             for (int i = 0; i < cachedCount; i++) {
6485                 final ViewHolder holder = mCachedViews.get(i);
6486                 if (holder != null && holder.mPosition >= insertedAt) {
6487                     if (DEBUG) {
6488                         Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder "
6489                                 + holder + " now at position " + (holder.mPosition + count));
6490                     }
6491                     holder.offsetPosition(count, true);
6492                 }
6493             }
6494         }
6495 
6496         /**
6497          * @param removedFrom Remove start index
6498          * @param count Remove count
6499          * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
6500          *                         false, they'll be applied before the second layout pass
6501          */
offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout)6502         void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
6503             final int removedEnd = removedFrom + count;
6504             final int cachedCount = mCachedViews.size();
6505             for (int i = cachedCount - 1; i >= 0; i--) {
6506                 final ViewHolder holder = mCachedViews.get(i);
6507                 if (holder != null) {
6508                     if (holder.mPosition >= removedEnd) {
6509                         if (DEBUG) {
6510                             Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
6511                                     + " holder " + holder + " now at position "
6512                                     + (holder.mPosition - count));
6513                         }
6514                         holder.offsetPosition(-count, applyToPreLayout);
6515                     } else if (holder.mPosition >= removedFrom) {
6516                         // Item for this view was removed. Dump it from the cache.
6517                         holder.addFlags(ViewHolder.FLAG_REMOVED);
6518                         recycleCachedViewAt(i);
6519                     }
6520                 }
6521             }
6522         }
6523 
setViewCacheExtension(ViewCacheExtension extension)6524         void setViewCacheExtension(ViewCacheExtension extension) {
6525             mViewCacheExtension = extension;
6526         }
6527 
setRecycledViewPool(RecycledViewPool pool)6528         void setRecycledViewPool(RecycledViewPool pool) {
6529             if (mRecyclerPool != null) {
6530                 mRecyclerPool.detach();
6531             }
6532             mRecyclerPool = pool;
6533             if (mRecyclerPool != null && getAdapter() != null) {
6534                 mRecyclerPool.attach();
6535             }
6536         }
6537 
getRecycledViewPool()6538         RecycledViewPool getRecycledViewPool() {
6539             if (mRecyclerPool == null) {
6540                 mRecyclerPool = new RecycledViewPool();
6541             }
6542             return mRecyclerPool;
6543         }
6544 
viewRangeUpdate(int positionStart, int itemCount)6545         void viewRangeUpdate(int positionStart, int itemCount) {
6546             final int positionEnd = positionStart + itemCount;
6547             final int cachedCount = mCachedViews.size();
6548             for (int i = cachedCount - 1; i >= 0; i--) {
6549                 final ViewHolder holder = mCachedViews.get(i);
6550                 if (holder == null) {
6551                     continue;
6552                 }
6553 
6554                 final int pos = holder.mPosition;
6555                 if (pos >= positionStart && pos < positionEnd) {
6556                     holder.addFlags(ViewHolder.FLAG_UPDATE);
6557                     recycleCachedViewAt(i);
6558                     // cached views should not be flagged as changed because this will cause them
6559                     // to animate when they are returned from cache.
6560                 }
6561             }
6562         }
6563 
markKnownViewsInvalid()6564         void markKnownViewsInvalid() {
6565             final int cachedCount = mCachedViews.size();
6566             for (int i = 0; i < cachedCount; i++) {
6567                 final ViewHolder holder = mCachedViews.get(i);
6568                 if (holder != null) {
6569                     holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
6570                     holder.addChangePayload(null);
6571                 }
6572             }
6573 
6574             if (mAdapter == null || !mAdapter.hasStableIds()) {
6575                 // we cannot re-use cached views in this case. Recycle them all
6576                 recycleAndClearCachedViews();
6577             }
6578         }
6579 
clearOldPositions()6580         void clearOldPositions() {
6581             final int cachedCount = mCachedViews.size();
6582             for (int i = 0; i < cachedCount; i++) {
6583                 final ViewHolder holder = mCachedViews.get(i);
6584                 holder.clearOldPosition();
6585             }
6586             final int scrapCount = mAttachedScrap.size();
6587             for (int i = 0; i < scrapCount; i++) {
6588                 mAttachedScrap.get(i).clearOldPosition();
6589             }
6590             if (mChangedScrap != null) {
6591                 final int changedScrapCount = mChangedScrap.size();
6592                 for (int i = 0; i < changedScrapCount; i++) {
6593                     mChangedScrap.get(i).clearOldPosition();
6594                 }
6595             }
6596         }
6597 
markItemDecorInsetsDirty()6598         void markItemDecorInsetsDirty() {
6599             final int cachedCount = mCachedViews.size();
6600             for (int i = 0; i < cachedCount; i++) {
6601                 final ViewHolder holder = mCachedViews.get(i);
6602                 LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
6603                 if (layoutParams != null) {
6604                     layoutParams.mInsetsDirty = true;
6605                 }
6606             }
6607         }
6608     }
6609 
6610     /**
6611      * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
6612      * be controlled by the developer.
6613      * <p>
6614      * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
6615      * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
6616      * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
6617      * {@link RecycledViewPool}.
6618      * <p>
6619      * Note that, Recycler never sends Views to this method to be cached. It is developers
6620      * responsibility to decide whether they want to keep their Views in this custom cache or let
6621      * the default recycling policy handle it.
6622      */
6623     public abstract static class ViewCacheExtension {
6624 
6625         /**
6626          * Returns a View that can be binded to the given Adapter position.
6627          * <p>
6628          * This method should <b>not</b> create a new View. Instead, it is expected to return
6629          * an already created View that can be re-used for the given type and position.
6630          * If the View is marked as ignored, it should first call
6631          * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
6632          * <p>
6633          * RecyclerView will re-bind the returned View to the position if necessary.
6634          *
6635          * @param recycler The Recycler that can be used to bind the View
6636          * @param position The adapter position
6637          * @param type     The type of the View, defined by adapter
6638          * @return A View that is bound to the given position or NULL if there is no View to re-use
6639          * @see LayoutManager#ignoreView(View)
6640          */
6641         @Nullable
getViewForPositionAndType(@onNull Recycler recycler, int position, int type)6642         public abstract View getViewForPositionAndType(@NonNull Recycler recycler, int position,
6643                 int type);
6644     }
6645 
6646     /**
6647      * Base class for an Adapter
6648      *
6649      * <p>Adapters provide a binding from an app-specific data set to views that are displayed
6650      * within a {@link RecyclerView}.</p>
6651      *
6652      * @param <VH> A class that extends ViewHolder that will be used by the adapter.
6653      */
6654     public abstract static class Adapter<VH extends ViewHolder> {
6655         private final AdapterDataObservable mObservable = new AdapterDataObservable();
6656         private boolean mHasStableIds = false;
6657 
6658         /**
6659          * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
6660          * an item.
6661          * <p>
6662          * This new ViewHolder should be constructed with a new View that can represent the items
6663          * of the given type. You can either create a new View manually or inflate it from an XML
6664          * layout file.
6665          * <p>
6666          * The new ViewHolder will be used to display items of the adapter using
6667          * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
6668          * different items in the data set, it is a good idea to cache references to sub views of
6669          * the View to avoid unnecessary {@link View#findViewById(int)} calls.
6670          *
6671          * @param parent The ViewGroup into which the new View will be added after it is bound to
6672          *               an adapter position.
6673          * @param viewType The view type of the new View.
6674          *
6675          * @return A new ViewHolder that holds a View of the given view type.
6676          * @see #getItemViewType(int)
6677          * @see #onBindViewHolder(ViewHolder, int)
6678          */
6679         @NonNull
onCreateViewHolder(@onNull ViewGroup parent, int viewType)6680         public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
6681 
6682         /**
6683          * Called by RecyclerView to display the data at the specified position. This method should
6684          * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
6685          * position.
6686          * <p>
6687          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6688          * again if the position of the item changes in the data set unless the item itself is
6689          * invalidated or the new position cannot be determined. For this reason, you should only
6690          * use the <code>position</code> parameter while acquiring the related data item inside
6691          * this method and should not keep a copy of it. If you need the position of an item later
6692          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6693          * have the updated adapter position.
6694          *
6695          * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
6696          * handle efficient partial bind.
6697          *
6698          * @param holder The ViewHolder which should be updated to represent the contents of the
6699          *        item at the given position in the data set.
6700          * @param position The position of the item within the adapter's data set.
6701          */
onBindViewHolder(@onNull VH holder, int position)6702         public abstract void onBindViewHolder(@NonNull VH holder, int position);
6703 
6704         /**
6705          * Called by RecyclerView to display the data at the specified position. This method
6706          * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
6707          * the given position.
6708          * <p>
6709          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6710          * again if the position of the item changes in the data set unless the item itself is
6711          * invalidated or the new position cannot be determined. For this reason, you should only
6712          * use the <code>position</code> parameter while acquiring the related data item inside
6713          * this method and should not keep a copy of it. If you need the position of an item later
6714          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6715          * have the updated adapter position.
6716          * <p>
6717          * Partial bind vs full bind:
6718          * <p>
6719          * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
6720          * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
6721          * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
6722          * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
6723          * Adapter should not assume that the payload passed in notify methods will be received by
6724          * onBindViewHolder().  For example when the view is not attached to the screen, the
6725          * payload in notifyItemChange() will be simply dropped.
6726          *
6727          * @param holder The ViewHolder which should be updated to represent the contents of the
6728          *               item at the given position in the data set.
6729          * @param position The position of the item within the adapter's data set.
6730          * @param payloads A non-null list of merged payloads. Can be empty list if requires full
6731          *                 update.
6732          */
onBindViewHolder(@onNull VH holder, int position, @NonNull List<Object> payloads)6733         public void onBindViewHolder(@NonNull VH holder, int position,
6734                 @NonNull List<Object> payloads) {
6735             onBindViewHolder(holder, position);
6736         }
6737 
6738         /**
6739          * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
6740          * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
6741          *
6742          * @see #onCreateViewHolder(ViewGroup, int)
6743          */
6744         @NonNull
createViewHolder(@onNull ViewGroup parent, int viewType)6745         public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
6746             try {
6747                 TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
6748                 final VH holder = onCreateViewHolder(parent, viewType);
6749                 if (holder.itemView.getParent() != null) {
6750                     throw new IllegalStateException("ViewHolder views must not be attached when"
6751                             + " created. Ensure that you are not passing 'true' to the attachToRoot"
6752                             + " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
6753                 }
6754                 holder.mItemViewType = viewType;
6755                 return holder;
6756             } finally {
6757                 TraceCompat.endSection();
6758             }
6759         }
6760 
6761         /**
6762          * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
6763          * {@link ViewHolder} contents with the item at the given position and also sets up some
6764          * private fields to be used by RecyclerView.
6765          *
6766          * @see #onBindViewHolder(ViewHolder, int)
6767          */
bindViewHolder(@onNull VH holder, int position)6768         public final void bindViewHolder(@NonNull VH holder, int position) {
6769             holder.mPosition = position;
6770             if (hasStableIds()) {
6771                 holder.mItemId = getItemId(position);
6772             }
6773             holder.setFlags(ViewHolder.FLAG_BOUND,
6774                     ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
6775                             | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
6776             TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
6777             onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
6778             holder.clearPayload();
6779             final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
6780             if (layoutParams instanceof RecyclerView.LayoutParams) {
6781                 ((LayoutParams) layoutParams).mInsetsDirty = true;
6782             }
6783             TraceCompat.endSection();
6784         }
6785 
6786         /**
6787          * Return the view type of the item at <code>position</code> for the purposes
6788          * of view recycling.
6789          *
6790          * <p>The default implementation of this method returns 0, making the assumption of
6791          * a single view type for the adapter. Unlike ListView adapters, types need not
6792          * be contiguous. Consider using id resources to uniquely identify item view types.
6793          *
6794          * @param position position to query
6795          * @return integer value identifying the type of the view needed to represent the item at
6796          *                 <code>position</code>. Type codes need not be contiguous.
6797          */
getItemViewType(int position)6798         public int getItemViewType(int position) {
6799             return 0;
6800         }
6801 
6802         /**
6803          * Indicates whether each item in the data set can be represented with a unique identifier
6804          * of type {@link java.lang.Long}.
6805          *
6806          * @param hasStableIds Whether items in data set have unique identifiers or not.
6807          * @see #hasStableIds()
6808          * @see #getItemId(int)
6809          */
setHasStableIds(boolean hasStableIds)6810         public void setHasStableIds(boolean hasStableIds) {
6811             if (hasObservers()) {
6812                 throw new IllegalStateException("Cannot change whether this adapter has "
6813                         + "stable IDs while the adapter has registered observers.");
6814             }
6815             mHasStableIds = hasStableIds;
6816         }
6817 
6818         /**
6819          * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
6820          * would return false this method should return {@link #NO_ID}. The default implementation
6821          * of this method returns {@link #NO_ID}.
6822          *
6823          * @param position Adapter position to query
6824          * @return the stable ID of the item at position
6825          */
getItemId(int position)6826         public long getItemId(int position) {
6827             return NO_ID;
6828         }
6829 
6830         /**
6831          * Returns the total number of items in the data set held by the adapter.
6832          *
6833          * @return The total number of items in this adapter.
6834          */
getItemCount()6835         public abstract int getItemCount();
6836 
6837         /**
6838          * Returns true if this adapter publishes a unique <code>long</code> value that can
6839          * act as a key for the item at a given position in the data set. If that item is relocated
6840          * in the data set, the ID returned for that item should be the same.
6841          *
6842          * @return true if this adapter's items have stable IDs
6843          */
hasStableIds()6844         public final boolean hasStableIds() {
6845             return mHasStableIds;
6846         }
6847 
6848         /**
6849          * Called when a view created by this adapter has been recycled.
6850          *
6851          * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
6852          * needs to be attached to its parent {@link RecyclerView}. This can be because it has
6853          * fallen out of visibility or a set of cached views represented by views still
6854          * attached to the parent RecyclerView. If an item view has large or expensive data
6855          * bound to it such as large bitmaps, this may be a good place to release those
6856          * resources.</p>
6857          * <p>
6858          * RecyclerView calls this method right before clearing ViewHolder's internal data and
6859          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
6860          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
6861          * its adapter position.
6862          *
6863          * @param holder The ViewHolder for the view being recycled
6864          */
onViewRecycled(@onNull VH holder)6865         public void onViewRecycled(@NonNull VH holder) {
6866         }
6867 
6868         /**
6869          * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
6870          * due to its transient state. Upon receiving this callback, Adapter can clear the
6871          * animation(s) that effect the View's transient state and return <code>true</code> so that
6872          * the View can be recycled. Keep in mind that the View in question is already removed from
6873          * the RecyclerView.
6874          * <p>
6875          * In some cases, it is acceptable to recycle a View although it has transient state. Most
6876          * of the time, this is a case where the transient state will be cleared in
6877          * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
6878          * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
6879          * value of this method to decide whether the View should be recycled or not.
6880          * <p>
6881          * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
6882          * should never receive this callback because RecyclerView keeps those Views as children
6883          * until their animations are complete. This callback is useful when children of the item
6884          * views create animations which may not be easy to implement using an {@link ItemAnimator}.
6885          * <p>
6886          * You should <em>never</em> fix this issue by calling
6887          * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
6888          * <code>holder.itemView.setHasTransientState(true);</code>. Each
6889          * <code>View.setHasTransientState(true)</code> call must be matched by a
6890          * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
6891          * may become inconsistent. You should always prefer to end or cancel animations that are
6892          * triggering the transient state instead of handling it manually.
6893          *
6894          * @param holder The ViewHolder containing the View that could not be recycled due to its
6895          *               transient state.
6896          * @return True if the View should be recycled, false otherwise. Note that if this method
6897          * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
6898          * the View and recycle it regardless. If this method returns <code>false</code>,
6899          * RecyclerView will check the View's transient state again before giving a final decision.
6900          * Default implementation returns false.
6901          */
onFailedToRecycleView(@onNull VH holder)6902         public boolean onFailedToRecycleView(@NonNull VH holder) {
6903             return false;
6904         }
6905 
6906         /**
6907          * Called when a view created by this adapter has been attached to a window.
6908          *
6909          * <p>This can be used as a reasonable signal that the view is about to be seen
6910          * by the user. If the adapter previously freed any resources in
6911          * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
6912          * those resources should be restored here.</p>
6913          *
6914          * @param holder Holder of the view being attached
6915          */
onViewAttachedToWindow(@onNull VH holder)6916         public void onViewAttachedToWindow(@NonNull VH holder) {
6917         }
6918 
6919         /**
6920          * Called when a view created by this adapter has been detached from its window.
6921          *
6922          * <p>Becoming detached from the window is not necessarily a permanent condition;
6923          * the consumer of an Adapter's views may choose to cache views offscreen while they
6924          * are not visible, attaching and detaching them as appropriate.</p>
6925          *
6926          * @param holder Holder of the view being detached
6927          */
onViewDetachedFromWindow(@onNull VH holder)6928         public void onViewDetachedFromWindow(@NonNull VH holder) {
6929         }
6930 
6931         /**
6932          * Returns true if one or more observers are attached to this adapter.
6933          *
6934          * @return true if this adapter has observers
6935          */
hasObservers()6936         public final boolean hasObservers() {
6937             return mObservable.hasObservers();
6938         }
6939 
6940         /**
6941          * Register a new observer to listen for data changes.
6942          *
6943          * <p>The adapter may publish a variety of events describing specific changes.
6944          * Not all adapters may support all change types and some may fall back to a generic
6945          * {@link RecyclerView.AdapterDataObserver#onChanged()
6946          * "something changed"} event if more specific data is not available.</p>
6947          *
6948          * <p>Components registering observers with an adapter are responsible for
6949          * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6950          * unregistering} those observers when finished.</p>
6951          *
6952          * @param observer Observer to register
6953          *
6954          * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6955          */
registerAdapterDataObserver(@onNull AdapterDataObserver observer)6956         public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
6957             mObservable.registerObserver(observer);
6958         }
6959 
6960         /**
6961          * Unregister an observer currently listening for data changes.
6962          *
6963          * <p>The unregistered observer will no longer receive events about changes
6964          * to the adapter.</p>
6965          *
6966          * @param observer Observer to unregister
6967          *
6968          * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
6969          */
unregisterAdapterDataObserver(@onNull AdapterDataObserver observer)6970         public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {
6971             mObservable.unregisterObserver(observer);
6972         }
6973 
6974         /**
6975          * Called by RecyclerView when it starts observing this Adapter.
6976          * <p>
6977          * Keep in mind that same adapter may be observed by multiple RecyclerViews.
6978          *
6979          * @param recyclerView The RecyclerView instance which started observing this adapter.
6980          * @see #onDetachedFromRecyclerView(RecyclerView)
6981          */
onAttachedToRecyclerView(@onNull RecyclerView recyclerView)6982         public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
6983         }
6984 
6985         /**
6986          * Called by RecyclerView when it stops observing this Adapter.
6987          *
6988          * @param recyclerView The RecyclerView instance which stopped observing this adapter.
6989          * @see #onAttachedToRecyclerView(RecyclerView)
6990          */
onDetachedFromRecyclerView(@onNull RecyclerView recyclerView)6991         public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
6992         }
6993 
6994         /**
6995          * Notify any registered observers that the data set has changed.
6996          *
6997          * <p>There are two different classes of data change events, item changes and structural
6998          * changes. Item changes are when a single item has its data updated but no positional
6999          * changes have occurred. Structural changes are when items are inserted, removed or moved
7000          * within the data set.</p>
7001          *
7002          * <p>This event does not specify what about the data set has changed, forcing
7003          * any observers to assume that all existing items and structure may no longer be valid.
7004          * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
7005          *
7006          * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
7007          * for adapters that report that they have {@link #hasStableIds() stable IDs} when
7008          * this method is used. This can help for the purposes of animation and visual
7009          * object persistence but individual item views will still need to be rebound
7010          * and relaid out.</p>
7011          *
7012          * <p>If you are writing an adapter it will always be more efficient to use the more
7013          * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
7014          * as a last resort.</p>
7015          *
7016          * @see #notifyItemChanged(int)
7017          * @see #notifyItemInserted(int)
7018          * @see #notifyItemRemoved(int)
7019          * @see #notifyItemRangeChanged(int, int)
7020          * @see #notifyItemRangeInserted(int, int)
7021          * @see #notifyItemRangeRemoved(int, int)
7022          */
notifyDataSetChanged()7023         public final void notifyDataSetChanged() {
7024             mObservable.notifyChanged();
7025         }
7026 
7027         /**
7028          * Notify any registered observers that the item at <code>position</code> has changed.
7029          * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
7030          *
7031          * <p>This is an item change event, not a structural change event. It indicates that any
7032          * reflection of the data at <code>position</code> is out of date and should be updated.
7033          * The item at <code>position</code> retains the same identity.</p>
7034          *
7035          * @param position Position of the item that has changed
7036          *
7037          * @see #notifyItemRangeChanged(int, int)
7038          */
notifyItemChanged(int position)7039         public final void notifyItemChanged(int position) {
7040             mObservable.notifyItemRangeChanged(position, 1);
7041         }
7042 
7043         /**
7044          * Notify any registered observers that the item at <code>position</code> has changed with
7045          * an optional payload object.
7046          *
7047          * <p>This is an item change event, not a structural change event. It indicates that any
7048          * reflection of the data at <code>position</code> is out of date and should be updated.
7049          * The item at <code>position</code> retains the same identity.
7050          * </p>
7051          *
7052          * <p>
7053          * Client can optionally pass a payload for partial change. These payloads will be merged
7054          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
7055          * item is already represented by a ViewHolder and it will be rebound to the same
7056          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
7057          * payloads on that item and prevent future payload until
7058          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
7059          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
7060          * attached, the payload will be simply dropped.
7061          *
7062          * @param position Position of the item that has changed
7063          * @param payload Optional parameter, use null to identify a "full" update
7064          *
7065          * @see #notifyItemRangeChanged(int, int)
7066          */
notifyItemChanged(int position, @Nullable Object payload)7067         public final void notifyItemChanged(int position, @Nullable Object payload) {
7068             mObservable.notifyItemRangeChanged(position, 1, payload);
7069         }
7070 
7071         /**
7072          * Notify any registered observers that the <code>itemCount</code> items starting at
7073          * position <code>positionStart</code> have changed.
7074          * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
7075          *
7076          * <p>This is an item change event, not a structural change event. It indicates that
7077          * any reflection of the data in the given position range is out of date and should
7078          * be updated. The items in the given range retain the same identity.</p>
7079          *
7080          * @param positionStart Position of the first item that has changed
7081          * @param itemCount Number of items that have changed
7082          *
7083          * @see #notifyItemChanged(int)
7084          */
notifyItemRangeChanged(int positionStart, int itemCount)7085         public final void notifyItemRangeChanged(int positionStart, int itemCount) {
7086             mObservable.notifyItemRangeChanged(positionStart, itemCount);
7087         }
7088 
7089         /**
7090          * Notify any registered observers that the <code>itemCount</code> items starting at
7091          * position <code>positionStart</code> have changed. An optional payload can be
7092          * passed to each changed item.
7093          *
7094          * <p>This is an item change event, not a structural change event. It indicates that any
7095          * reflection of the data in the given position range is out of date and should be updated.
7096          * The items in the given range retain the same identity.
7097          * </p>
7098          *
7099          * <p>
7100          * Client can optionally pass a payload for partial change. These payloads will be merged
7101          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
7102          * item is already represented by a ViewHolder and it will be rebound to the same
7103          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
7104          * payloads on that item and prevent future payload until
7105          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
7106          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
7107          * attached, the payload will be simply dropped.
7108          *
7109          * @param positionStart Position of the first item that has changed
7110          * @param itemCount Number of items that have changed
7111          * @param payload  Optional parameter, use null to identify a "full" update
7112          *
7113          * @see #notifyItemChanged(int)
7114          */
notifyItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload)7115         public final void notifyItemRangeChanged(int positionStart, int itemCount,
7116                 @Nullable Object payload) {
7117             mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
7118         }
7119 
7120         /**
7121          * Notify any registered observers that the item reflected at <code>position</code>
7122          * has been newly inserted. The item previously at <code>position</code> is now at
7123          * position <code>position + 1</code>.
7124          *
7125          * <p>This is a structural change event. Representations of other existing items in the
7126          * data set are still considered up to date and will not be rebound, though their
7127          * positions may be altered.</p>
7128          *
7129          * @param position Position of the newly inserted item in the data set
7130          *
7131          * @see #notifyItemRangeInserted(int, int)
7132          */
notifyItemInserted(int position)7133         public final void notifyItemInserted(int position) {
7134             mObservable.notifyItemRangeInserted(position, 1);
7135         }
7136 
7137         /**
7138          * Notify any registered observers that the item reflected at <code>fromPosition</code>
7139          * has been moved to <code>toPosition</code>.
7140          *
7141          * <p>This is a structural change event. Representations of other existing items in the
7142          * data set are still considered up to date and will not be rebound, though their
7143          * positions may be altered.</p>
7144          *
7145          * @param fromPosition Previous position of the item.
7146          * @param toPosition New position of the item.
7147          */
notifyItemMoved(int fromPosition, int toPosition)7148         public final void notifyItemMoved(int fromPosition, int toPosition) {
7149             mObservable.notifyItemMoved(fromPosition, toPosition);
7150         }
7151 
7152         /**
7153          * Notify any registered observers that the currently reflected <code>itemCount</code>
7154          * items starting at <code>positionStart</code> have been newly inserted. The items
7155          * previously located at <code>positionStart</code> and beyond can now be found starting
7156          * at position <code>positionStart + itemCount</code>.
7157          *
7158          * <p>This is a structural change event. Representations of other existing items in the
7159          * data set are still considered up to date and will not be rebound, though their positions
7160          * may be altered.</p>
7161          *
7162          * @param positionStart Position of the first item that was inserted
7163          * @param itemCount Number of items inserted
7164          *
7165          * @see #notifyItemInserted(int)
7166          */
notifyItemRangeInserted(int positionStart, int itemCount)7167         public final void notifyItemRangeInserted(int positionStart, int itemCount) {
7168             mObservable.notifyItemRangeInserted(positionStart, itemCount);
7169         }
7170 
7171         /**
7172          * Notify any registered observers that the item previously located at <code>position</code>
7173          * has been removed from the data set. The items previously located at and after
7174          * <code>position</code> may now be found at <code>oldPosition - 1</code>.
7175          *
7176          * <p>This is a structural change event. Representations of other existing items in the
7177          * data set are still considered up to date and will not be rebound, though their positions
7178          * may be altered.</p>
7179          *
7180          * @param position Position of the item that has now been removed
7181          *
7182          * @see #notifyItemRangeRemoved(int, int)
7183          */
notifyItemRemoved(int position)7184         public final void notifyItemRemoved(int position) {
7185             mObservable.notifyItemRangeRemoved(position, 1);
7186         }
7187 
7188         /**
7189          * Notify any registered observers that the <code>itemCount</code> items previously
7190          * located at <code>positionStart</code> have been removed from the data set. The items
7191          * previously located at and after <code>positionStart + itemCount</code> may now be found
7192          * at <code>oldPosition - itemCount</code>.
7193          *
7194          * <p>This is a structural change event. Representations of other existing items in the data
7195          * set are still considered up to date and will not be rebound, though their positions
7196          * may be altered.</p>
7197          *
7198          * @param positionStart Previous position of the first item that was removed
7199          * @param itemCount Number of items removed from the data set
7200          */
notifyItemRangeRemoved(int positionStart, int itemCount)7201         public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
7202             mObservable.notifyItemRangeRemoved(positionStart, itemCount);
7203         }
7204     }
7205 
dispatchChildDetached(View child)7206     void dispatchChildDetached(View child) {
7207         final ViewHolder viewHolder = getChildViewHolderInt(child);
7208         onChildDetachedFromWindow(child);
7209         if (mAdapter != null && viewHolder != null) {
7210             mAdapter.onViewDetachedFromWindow(viewHolder);
7211         }
7212         if (mOnChildAttachStateListeners != null) {
7213             final int cnt = mOnChildAttachStateListeners.size();
7214             for (int i = cnt - 1; i >= 0; i--) {
7215                 mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
7216             }
7217         }
7218     }
7219 
dispatchChildAttached(View child)7220     void dispatchChildAttached(View child) {
7221         final ViewHolder viewHolder = getChildViewHolderInt(child);
7222         onChildAttachedToWindow(child);
7223         if (mAdapter != null && viewHolder != null) {
7224             mAdapter.onViewAttachedToWindow(viewHolder);
7225         }
7226         if (mOnChildAttachStateListeners != null) {
7227             final int cnt = mOnChildAttachStateListeners.size();
7228             for (int i = cnt - 1; i >= 0; i--) {
7229                 mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
7230             }
7231         }
7232     }
7233 
7234     /**
7235      * A <code>LayoutManager</code> is responsible for measuring and positioning item views
7236      * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
7237      * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
7238      * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
7239      * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
7240      * layout managers are provided for general use.
7241      * <p/>
7242      * If the LayoutManager specifies a default constructor or one with the signature
7243      * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
7244      * instantiate and set the LayoutManager when being inflated. Most used properties can
7245      * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
7246      * a LayoutManager specifies both constructors, the non-default constructor will take
7247      * precedence.
7248      *
7249      */
7250     public abstract static class LayoutManager {
7251         ChildHelper mChildHelper;
7252         RecyclerView mRecyclerView;
7253 
7254         /**
7255          * The callback used for retrieving information about a RecyclerView and its children in the
7256          * horizontal direction.
7257          */
7258         private final ViewBoundsCheck.Callback mHorizontalBoundCheckCallback =
7259                 new ViewBoundsCheck.Callback() {
7260                     @Override
7261                     public int getChildCount() {
7262                         return LayoutManager.this.getChildCount();
7263                     }
7264 
7265                     @Override
7266                     public View getParent() {
7267                         return mRecyclerView;
7268                     }
7269 
7270                     @Override
7271                     public View getChildAt(int index) {
7272                         return LayoutManager.this.getChildAt(index);
7273                     }
7274 
7275                     @Override
7276                     public int getParentStart() {
7277                         return LayoutManager.this.getPaddingLeft();
7278                     }
7279 
7280                     @Override
7281                     public int getParentEnd() {
7282                         return LayoutManager.this.getWidth() - LayoutManager.this.getPaddingRight();
7283                     }
7284 
7285                     @Override
7286                     public int getChildStart(View view) {
7287                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7288                                 view.getLayoutParams();
7289                         return LayoutManager.this.getDecoratedLeft(view) - params.leftMargin;
7290                     }
7291 
7292                     @Override
7293                     public int getChildEnd(View view) {
7294                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7295                                 view.getLayoutParams();
7296                         return LayoutManager.this.getDecoratedRight(view) + params.rightMargin;
7297                     }
7298                 };
7299 
7300         /**
7301          * The callback used for retrieving information about a RecyclerView and its children in the
7302          * vertical direction.
7303          */
7304         private final ViewBoundsCheck.Callback mVerticalBoundCheckCallback =
7305                 new ViewBoundsCheck.Callback() {
7306                     @Override
7307                     public int getChildCount() {
7308                         return LayoutManager.this.getChildCount();
7309                     }
7310 
7311                     @Override
7312                     public View getParent() {
7313                         return mRecyclerView;
7314                     }
7315 
7316                     @Override
7317                     public View getChildAt(int index) {
7318                         return LayoutManager.this.getChildAt(index);
7319                     }
7320 
7321                     @Override
7322                     public int getParentStart() {
7323                         return LayoutManager.this.getPaddingTop();
7324                     }
7325 
7326                     @Override
7327                     public int getParentEnd() {
7328                         return LayoutManager.this.getHeight()
7329                                 - LayoutManager.this.getPaddingBottom();
7330                     }
7331 
7332                     @Override
7333                     public int getChildStart(View view) {
7334                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7335                                 view.getLayoutParams();
7336                         return LayoutManager.this.getDecoratedTop(view) - params.topMargin;
7337                     }
7338 
7339                     @Override
7340                     public int getChildEnd(View view) {
7341                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7342                                 view.getLayoutParams();
7343                         return LayoutManager.this.getDecoratedBottom(view) + params.bottomMargin;
7344                     }
7345                 };
7346 
7347         /**
7348          * Utility objects used to check the boundaries of children against their parent
7349          * RecyclerView.
7350          * @see #isViewPartiallyVisible(View, boolean, boolean),
7351          * {@link LinearLayoutManager#findOneVisibleChild(int, int, boolean, boolean)},
7352          * and {@link LinearLayoutManager#findOnePartiallyOrCompletelyInvisibleChild(int, int)}.
7353          */
7354         ViewBoundsCheck mHorizontalBoundCheck = new ViewBoundsCheck(mHorizontalBoundCheckCallback);
7355         ViewBoundsCheck mVerticalBoundCheck = new ViewBoundsCheck(mVerticalBoundCheckCallback);
7356 
7357         @Nullable
7358         SmoothScroller mSmoothScroller;
7359 
7360         boolean mRequestedSimpleAnimations = false;
7361 
7362         boolean mIsAttachedToWindow = false;
7363 
7364         /**
7365          * This field is only set via the deprecated {@link #setAutoMeasureEnabled(boolean)} and is
7366          * only accessed via {@link #isAutoMeasureEnabled()} for backwards compatability reasons.
7367          */
7368         boolean mAutoMeasure = false;
7369 
7370         /**
7371          * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
7372          * if the space that will be given to it is already larger than what it has measured before.
7373          */
7374         private boolean mMeasurementCacheEnabled = true;
7375 
7376         private boolean mItemPrefetchEnabled = true;
7377 
7378         /**
7379          * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
7380          * requested by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)} or
7381          * {@link #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)} call.
7382          *
7383          * If expanded by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)},
7384          * will be reset upon layout to prevent initial prefetches (often large, since they're
7385          * proportional to expected child count) from expanding cache permanently.
7386          */
7387         int mPrefetchMaxCountObserved;
7388 
7389         /**
7390          * If true, mPrefetchMaxCountObserved is only valid until next layout, and should be reset.
7391          */
7392         boolean mPrefetchMaxObservedInInitialPrefetch;
7393 
7394         /**
7395          * These measure specs might be the measure specs that were passed into RecyclerView's
7396          * onMeasure method OR fake measure specs created by the RecyclerView.
7397          * For example, when a layout is run, RecyclerView always sets these specs to be
7398          * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
7399          * <p>
7400          * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
7401          * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
7402          * corrupt values. Older platforms have no responsibility to provide a size if they set
7403          * mode to unspecified.
7404          */
7405         private int mWidthMode, mHeightMode;
7406         private int mWidth, mHeight;
7407 
7408 
7409         /**
7410          * Interface for LayoutManagers to request items to be prefetched, based on position, with
7411          * specified distance from viewport, which indicates priority.
7412          *
7413          * @see LayoutManager#collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
7414          * @see LayoutManager#collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
7415          */
7416         public interface LayoutPrefetchRegistry {
7417             /**
7418              * Requests an an item to be prefetched, based on position, with a specified distance,
7419              * indicating priority.
7420              *
7421              * @param layoutPosition Position of the item to prefetch.
7422              * @param pixelDistance Distance from the current viewport to the bounds of the item,
7423              *                      must be non-negative.
7424              */
addPosition(int layoutPosition, int pixelDistance)7425             void addPosition(int layoutPosition, int pixelDistance);
7426         }
7427 
setRecyclerView(RecyclerView recyclerView)7428         void setRecyclerView(RecyclerView recyclerView) {
7429             if (recyclerView == null) {
7430                 mRecyclerView = null;
7431                 mChildHelper = null;
7432                 mWidth = 0;
7433                 mHeight = 0;
7434             } else {
7435                 mRecyclerView = recyclerView;
7436                 mChildHelper = recyclerView.mChildHelper;
7437                 mWidth = recyclerView.getWidth();
7438                 mHeight = recyclerView.getHeight();
7439             }
7440             mWidthMode = MeasureSpec.EXACTLY;
7441             mHeightMode = MeasureSpec.EXACTLY;
7442         }
7443 
setMeasureSpecs(int wSpec, int hSpec)7444         void setMeasureSpecs(int wSpec, int hSpec) {
7445             mWidth = MeasureSpec.getSize(wSpec);
7446             mWidthMode = MeasureSpec.getMode(wSpec);
7447             if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
7448                 mWidth = 0;
7449             }
7450 
7451             mHeight = MeasureSpec.getSize(hSpec);
7452             mHeightMode = MeasureSpec.getMode(hSpec);
7453             if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
7454                 mHeight = 0;
7455             }
7456         }
7457 
7458         /**
7459          * Called after a layout is calculated during a measure pass when using auto-measure.
7460          * <p>
7461          * It simply traverses all children to calculate a bounding box then calls
7462          * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
7463          * if they need to handle the bounding box differently.
7464          * <p>
7465          * For example, GridLayoutManager override that method to ensure that even if a column is
7466          * empty, the GridLayoutManager still measures wide enough to include it.
7467          *
7468          * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
7469          * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
7470          */
setMeasuredDimensionFromChildren(int widthSpec, int heightSpec)7471         void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
7472             final int count = getChildCount();
7473             if (count == 0) {
7474                 mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
7475                 return;
7476             }
7477             int minX = Integer.MAX_VALUE;
7478             int minY = Integer.MAX_VALUE;
7479             int maxX = Integer.MIN_VALUE;
7480             int maxY = Integer.MIN_VALUE;
7481 
7482             for (int i = 0; i < count; i++) {
7483                 View child = getChildAt(i);
7484                 final Rect bounds = mRecyclerView.mTempRect;
7485                 getDecoratedBoundsWithMargins(child, bounds);
7486                 if (bounds.left < minX) {
7487                     minX = bounds.left;
7488                 }
7489                 if (bounds.right > maxX) {
7490                     maxX = bounds.right;
7491                 }
7492                 if (bounds.top < minY) {
7493                     minY = bounds.top;
7494                 }
7495                 if (bounds.bottom > maxY) {
7496                     maxY = bounds.bottom;
7497                 }
7498             }
7499             mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
7500             setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
7501         }
7502 
7503         /**
7504          * Sets the measured dimensions from the given bounding box of the children and the
7505          * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
7506          * only called if a LayoutManager returns <code>true</code> from
7507          * {@link #isAutoMeasureEnabled()} and it is called after the RecyclerView calls
7508          * {@link LayoutManager#onLayoutChildren(Recycler, State)} in the execution of
7509          * {@link RecyclerView#onMeasure(int, int)}.
7510          * <p>
7511          * This method must call {@link #setMeasuredDimension(int, int)}.
7512          * <p>
7513          * The default implementation adds the RecyclerView's padding to the given bounding box
7514          * then caps the value to be within the given measurement specs.
7515          *
7516          * @param childrenBounds The bounding box of all children
7517          * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
7518          * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
7519          *
7520          * @see #isAutoMeasureEnabled()
7521          * @see #setMeasuredDimension(int, int)
7522          */
setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec)7523         public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
7524             int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
7525             int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
7526             int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
7527             int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
7528             setMeasuredDimension(width, height);
7529         }
7530 
7531         /**
7532          * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
7533          */
requestLayout()7534         public void requestLayout() {
7535             if (mRecyclerView != null) {
7536                 mRecyclerView.requestLayout();
7537             }
7538         }
7539 
7540         /**
7541          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7542          * {@link IllegalStateException} if it <b>is not</b>.
7543          *
7544          * @param message The message for the exception. Can be null.
7545          * @see #assertNotInLayoutOrScroll(String)
7546          */
assertInLayoutOrScroll(String message)7547         public void assertInLayoutOrScroll(String message) {
7548             if (mRecyclerView != null) {
7549                 mRecyclerView.assertInLayoutOrScroll(message);
7550             }
7551         }
7552 
7553         /**
7554          * Chooses a size from the given specs and parameters that is closest to the desired size
7555          * and also complies with the spec.
7556          *
7557          * @param spec The measureSpec
7558          * @param desired The preferred measurement
7559          * @param min The minimum value
7560          *
7561          * @return A size that fits to the given specs
7562          */
chooseSize(int spec, int desired, int min)7563         public static int chooseSize(int spec, int desired, int min) {
7564             final int mode = View.MeasureSpec.getMode(spec);
7565             final int size = View.MeasureSpec.getSize(spec);
7566             switch (mode) {
7567                 case View.MeasureSpec.EXACTLY:
7568                     return size;
7569                 case View.MeasureSpec.AT_MOST:
7570                     return Math.min(size, Math.max(desired, min));
7571                 case View.MeasureSpec.UNSPECIFIED:
7572                 default:
7573                     return Math.max(desired, min);
7574             }
7575         }
7576 
7577         /**
7578          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7579          * {@link IllegalStateException} if it <b>is</b>.
7580          *
7581          * @param message The message for the exception. Can be null.
7582          * @see #assertInLayoutOrScroll(String)
7583          */
assertNotInLayoutOrScroll(String message)7584         public void assertNotInLayoutOrScroll(String message) {
7585             if (mRecyclerView != null) {
7586                 mRecyclerView.assertNotInLayoutOrScroll(message);
7587             }
7588         }
7589 
7590         /**
7591          * Defines whether the measuring pass of layout should use the AutoMeasure mechanism of
7592          * {@link RecyclerView} or if it should be done by the LayoutManager's implementation of
7593          * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
7594          *
7595          * @param enabled <code>True</code> if layout measurement should be done by the
7596          *                RecyclerView, <code>false</code> if it should be done by this
7597          *                LayoutManager.
7598          *
7599          * @see #isAutoMeasureEnabled()
7600          *
7601          * @deprecated Implementors of LayoutManager should define whether or not it uses
7602          *             AutoMeasure by overriding {@link #isAutoMeasureEnabled()}.
7603          */
7604         @Deprecated
setAutoMeasureEnabled(boolean enabled)7605         public void setAutoMeasureEnabled(boolean enabled) {
7606             mAutoMeasure = enabled;
7607         }
7608 
7609         /**
7610          * Returns whether the measuring pass of layout should use the AutoMeasure mechanism of
7611          * {@link RecyclerView} or if it should be done by the LayoutManager's implementation of
7612          * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
7613          * <p>
7614          * This method returns false by default (it actually returns the value passed to the
7615          * deprecated {@link #setAutoMeasureEnabled(boolean)}) and should be overridden to return
7616          * true if a LayoutManager wants to be auto measured by the RecyclerView.
7617          * <p>
7618          * If this method is overridden to return true,
7619          * {@link LayoutManager#onMeasure(Recycler, State, int, int)} should not be overridden.
7620          * <p>
7621          * AutoMeasure is a RecyclerView mechanism that handles the measuring pass of layout in a
7622          * simple and contract satisfying way, including the wrapping of children laid out by
7623          * LayoutManager. Simply put, it handles wrapping children by calling
7624          * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a call to
7625          * {@link RecyclerView#onMeasure(int, int)}, and then calculating desired dimensions based
7626          * on children's dimensions and positions. It does this while supporting all existing
7627          * animation capabilities of the RecyclerView.
7628          * <p>
7629          * More specifically:
7630          * <ol>
7631          * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided measure
7632          * specs both have a mode of {@link View.MeasureSpec#EXACTLY}, RecyclerView will set its
7633          * measured dimensions accordingly and return, allowing layout to continue as normal
7634          * (Actually, RecyclerView will call
7635          * {@link LayoutManager#onMeasure(Recycler, State, int, int)} for backwards compatibility
7636          * reasons but it should not be overridden if AutoMeasure is being used).</li>
7637          * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
7638          * layout process. It will first process all pending Adapter updates and
7639          * then decide whether to run a predictive layout. If it decides to do so, it will first
7640          * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
7641          * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
7642          * return the width and height of the RecyclerView as of the last layout calculation.
7643          * <p>
7644          * After handling the predictive case, RecyclerView will call
7645          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7646          * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
7647          * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
7648          * {@link #getWidth()} and {@link #getWidthMode()}.</li>
7649          * <li>After the layout calculation, RecyclerView sets the measured width & height by
7650          * calculating the bounding box for the children (+ RecyclerView's padding). The
7651          * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
7652          * different values. For instance, GridLayoutManager overrides this value to handle the case
7653          * where if it is vertical and has 3 columns but only 2 items, it should still measure its
7654          * width to fit 3 items, not 2.</li>
7655          * <li>Any following calls to {@link RecyclerView#onMeasure(int, int)} will run
7656          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7657          * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
7658          * take care of which views are actually added / removed / moved / changed for animations so
7659          * that the LayoutManager should not worry about them and handle each
7660          * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.</li>
7661          * <li>When measure is complete and RecyclerView's
7662          * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
7663          * whether it already did layout calculations during the measure pass and if so, it re-uses
7664          * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
7665          * if the last measure spec was different from the final dimensions or adapter contents
7666          * have changed between the measure call and the layout call.</li>
7667          * <li>Finally, animations are calculated and run as usual.</li>
7668          * </ol>
7669          *
7670          * @return <code>True</code> if the measuring pass of layout should use the AutoMeasure
7671          * mechanism of {@link RecyclerView} or <code>False</code> if it should be done by the
7672          * LayoutManager's implementation of
7673          * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
7674          *
7675          * @see #setMeasuredDimension(Rect, int, int)
7676          * @see #onMeasure(Recycler, State, int, int)
7677          */
isAutoMeasureEnabled()7678         public boolean isAutoMeasureEnabled() {
7679             return mAutoMeasure;
7680         }
7681 
7682         /**
7683          * Returns whether this LayoutManager supports "predictive item animations".
7684          * <p>
7685          * "Predictive item animations" are automatically created animations that show
7686          * where items came from, and where they are going to, as items are added, removed,
7687          * or moved within a layout.
7688          * <p>
7689          * A LayoutManager wishing to support predictive item animations must override this
7690          * method to return true (the default implementation returns false) and must obey certain
7691          * behavioral contracts outlined in {@link #onLayoutChildren(Recycler, State)}.
7692          * <p>
7693          * Whether item animations actually occur in a RecyclerView is actually determined by both
7694          * the return value from this method and the
7695          * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
7696          * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
7697          * method returns false, then only "simple item animations" will be enabled in the
7698          * RecyclerView, in which views whose position are changing are simply faded in/out. If the
7699          * RecyclerView has a non-null ItemAnimator and this method returns true, then predictive
7700          * item animations will be enabled in the RecyclerView.
7701          *
7702          * @return true if this LayoutManager supports predictive item animations, false otherwise.
7703          */
supportsPredictiveItemAnimations()7704         public boolean supportsPredictiveItemAnimations() {
7705             return false;
7706         }
7707 
7708         /**
7709          * Sets whether the LayoutManager should be queried for views outside of
7710          * its viewport while the UI thread is idle between frames.
7711          *
7712          * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
7713          * view system traversals on devices running API 21 or greater. Default value is true.</p>
7714          *
7715          * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
7716          * to RenderThread and the starting up its next frame at the next VSync pulse. By
7717          * prefetching out of window views in this time period, delays from inflation and view
7718          * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
7719          *
7720          * <p>While prefetch is enabled, it will have the side effect of expanding the effective
7721          * size of the View cache to hold prefetched views.</p>
7722          *
7723          * @param enabled <code>True</code> if items should be prefetched in between traversals.
7724          *
7725          * @see #isItemPrefetchEnabled()
7726          */
setItemPrefetchEnabled(boolean enabled)7727         public final void setItemPrefetchEnabled(boolean enabled) {
7728             if (enabled != mItemPrefetchEnabled) {
7729                 mItemPrefetchEnabled = enabled;
7730                 mPrefetchMaxCountObserved = 0;
7731                 if (mRecyclerView != null) {
7732                     mRecyclerView.mRecycler.updateViewCacheSize();
7733                 }
7734             }
7735         }
7736 
7737         /**
7738          * Sets whether the LayoutManager should be queried for views outside of
7739          * its viewport while the UI thread is idle between frames.
7740          *
7741          * @see #setItemPrefetchEnabled(boolean)
7742          *
7743          * @return true if item prefetch is enabled, false otherwise
7744          */
isItemPrefetchEnabled()7745         public final boolean isItemPrefetchEnabled() {
7746             return mItemPrefetchEnabled;
7747         }
7748 
7749         /**
7750          * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
7751          *
7752          * <p>If item prefetch is enabled, this method is called in between traversals to gather
7753          * which positions the LayoutManager will soon need, given upcoming movement in subsequent
7754          * traversals.</p>
7755          *
7756          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7757          * each item to be prepared, and these positions will have their ViewHolders created and
7758          * bound, if there is sufficient time available, in advance of being needed by a
7759          * scroll or layout.</p>
7760          *
7761          * @param dx X movement component.
7762          * @param dy Y movement component.
7763          * @param state State of RecyclerView
7764          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7765          *
7766          * @see #isItemPrefetchEnabled()
7767          * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
7768          */
collectAdjacentPrefetchPositions(int dx, int dy, State state, LayoutPrefetchRegistry layoutPrefetchRegistry)7769         public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
7770                 LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7771 
7772         /**
7773          * Gather all positions from the LayoutManager to be prefetched in preperation for its
7774          * RecyclerView to come on screen, due to the movement of another, containing RecyclerView.
7775          *
7776          * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
7777          *
7778          * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
7779          * LayoutManager, this method is called in between draw traversals to gather
7780          * which positions this LayoutManager will first need, once it appears on the screen.</p>
7781          *
7782          * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
7783          * vertically scrolling LayoutManager, this method would be called when the horizontal list
7784          * is about to come onscreen.</p>
7785          *
7786          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7787          * each item to be prepared, and these positions will have their ViewHolders created and
7788          * bound, if there is sufficient time available, in advance of being needed by a
7789          * scroll or layout.</p>
7790          *
7791          * @param adapterItemCount number of items in the associated adapter.
7792          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7793          *
7794          * @see #isItemPrefetchEnabled()
7795          * @see #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
7796          */
collectInitialPrefetchPositions(int adapterItemCount, LayoutPrefetchRegistry layoutPrefetchRegistry)7797         public void collectInitialPrefetchPositions(int adapterItemCount,
7798                 LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7799 
dispatchAttachedToWindow(RecyclerView view)7800         void dispatchAttachedToWindow(RecyclerView view) {
7801             mIsAttachedToWindow = true;
7802             onAttachedToWindow(view);
7803         }
7804 
dispatchDetachedFromWindow(RecyclerView view, Recycler recycler)7805         void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
7806             mIsAttachedToWindow = false;
7807             onDetachedFromWindow(view, recycler);
7808         }
7809 
7810         /**
7811          * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
7812          * to a window.
7813          *
7814          * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
7815          * is attached to window.
7816          */
isAttachedToWindow()7817         public boolean isAttachedToWindow() {
7818             return mIsAttachedToWindow;
7819         }
7820 
7821         /**
7822          * Causes the Runnable to execute on the next animation time step.
7823          * The runnable will be run on the user interface thread.
7824          * <p>
7825          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7826          *
7827          * @param action The Runnable that will be executed.
7828          *
7829          * @see #removeCallbacks
7830          */
postOnAnimation(Runnable action)7831         public void postOnAnimation(Runnable action) {
7832             if (mRecyclerView != null) {
7833                 ViewCompat.postOnAnimation(mRecyclerView, action);
7834             }
7835         }
7836 
7837         /**
7838          * Removes the specified Runnable from the message queue.
7839          * <p>
7840          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7841          *
7842          * @param action The Runnable to remove from the message handling queue
7843          *
7844          * @return true if RecyclerView could ask the Handler to remove the Runnable,
7845          *         false otherwise. When the returned value is true, the Runnable
7846          *         may or may not have been actually removed from the message queue
7847          *         (for instance, if the Runnable was not in the queue already.)
7848          *
7849          * @see #postOnAnimation
7850          */
removeCallbacks(Runnable action)7851         public boolean removeCallbacks(Runnable action) {
7852             if (mRecyclerView != null) {
7853                 return mRecyclerView.removeCallbacks(action);
7854             }
7855             return false;
7856         }
7857         /**
7858          * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
7859          * is attached to a window.
7860          * <p>
7861          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7862          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7863          * not requested on the RecyclerView while it was detached.
7864          * <p>
7865          * Subclass implementations should always call through to the superclass implementation.
7866          *
7867          * @param view The RecyclerView this LayoutManager is bound to
7868          *
7869          * @see #onDetachedFromWindow(RecyclerView, Recycler)
7870          */
7871         @CallSuper
onAttachedToWindow(RecyclerView view)7872         public void onAttachedToWindow(RecyclerView view) {
7873         }
7874 
7875         /**
7876          * @deprecated
7877          * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
7878          */
7879         @Deprecated
onDetachedFromWindow(RecyclerView view)7880         public void onDetachedFromWindow(RecyclerView view) {
7881 
7882         }
7883 
7884         /**
7885          * Called when this LayoutManager is detached from its parent RecyclerView or when
7886          * its parent RecyclerView is detached from its window.
7887          * <p>
7888          * LayoutManager should clear all of its View references as another LayoutManager might be
7889          * assigned to the RecyclerView.
7890          * <p>
7891          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7892          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7893          * not requested on the RecyclerView while it was detached.
7894          * <p>
7895          * If your LayoutManager has View references that it cleans in on-detach, it should also
7896          * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
7897          * RecyclerView is re-attached.
7898          * <p>
7899          * Subclass implementations should always call through to the superclass implementation.
7900          *
7901          * @param view The RecyclerView this LayoutManager is bound to
7902          * @param recycler The recycler to use if you prefer to recycle your children instead of
7903          *                 keeping them around.
7904          *
7905          * @see #onAttachedToWindow(RecyclerView)
7906          */
7907         @CallSuper
onDetachedFromWindow(RecyclerView view, Recycler recycler)7908         public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
7909             onDetachedFromWindow(view);
7910         }
7911 
7912         /**
7913          * Check if the RecyclerView is configured to clip child views to its padding.
7914          *
7915          * @return true if this RecyclerView clips children to its padding, false otherwise
7916          */
getClipToPadding()7917         public boolean getClipToPadding() {
7918             return mRecyclerView != null && mRecyclerView.mClipToPadding;
7919         }
7920 
7921         /**
7922          * Lay out all relevant child views from the given adapter.
7923          *
7924          * The LayoutManager is in charge of the behavior of item animations. By default,
7925          * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
7926          * item animations are enabled. This means that add/remove operations on the
7927          * adapter will result in animations to add new or appearing items, removed or
7928          * disappearing items, and moved items. If a LayoutManager returns false from
7929          * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
7930          * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
7931          * RecyclerView will have enough information to run those animations in a simple
7932          * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
7933          * simply fade views in and out, whether they are actually added/removed or whether
7934          * they are moved on or off the screen due to other add/remove operations.
7935          *
7936          * <p>A LayoutManager wanting a better item animation experience, where items can be
7937          * animated onto and off of the screen according to where the items exist when they
7938          * are not on screen, then the LayoutManager should return true from
7939          * {@link #supportsPredictiveItemAnimations()} and add additional logic to
7940          * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
7941          * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
7942          * once as a "pre" layout step to determine where items would have been prior to
7943          * a real layout, and again to do the "real" layout. In the pre-layout phase,
7944          * items will remember their pre-layout positions to allow them to be laid out
7945          * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
7946          * be returned from the scrap to help determine correct placement of other items.
7947          * These removed items should not be added to the child list, but should be used
7948          * to help calculate correct positioning of other views, including views that
7949          * were not previously onscreen (referred to as APPEARING views), but whose
7950          * pre-layout offscreen position can be determined given the extra
7951          * information about the pre-layout removed views.</p>
7952          *
7953          * <p>The second layout pass is the real layout in which only non-removed views
7954          * will be used. The only additional requirement during this pass is, if
7955          * {@link #supportsPredictiveItemAnimations()} returns true, to note which
7956          * views exist in the child list prior to layout and which are not there after
7957          * layout (referred to as DISAPPEARING views), and to position/layout those views
7958          * appropriately, without regard to the actual bounds of the RecyclerView. This allows
7959          * the animation system to know the location to which to animate these disappearing
7960          * views.</p>
7961          *
7962          * <p>The default LayoutManager implementations for RecyclerView handle all of these
7963          * requirements for animations already. Clients of RecyclerView can either use one
7964          * of these layout managers directly or look at their implementations of
7965          * onLayoutChildren() to see how they account for the APPEARING and
7966          * DISAPPEARING views.</p>
7967          *
7968          * @param recycler         Recycler to use for fetching potentially cached views for a
7969          *                         position
7970          * @param state            Transient state of RecyclerView
7971          */
onLayoutChildren(Recycler recycler, State state)7972         public void onLayoutChildren(Recycler recycler, State state) {
7973             Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
7974         }
7975 
7976         /**
7977          * Called after a full layout calculation is finished. The layout calculation may include
7978          * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
7979          * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
7980          * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
7981          * <p>
7982          * This is a good place for the LayoutManager to do some cleanup like pending scroll
7983          * position, saved state etc.
7984          *
7985          * @param state Transient state of RecyclerView
7986          */
onLayoutCompleted(State state)7987         public void onLayoutCompleted(State state) {
7988         }
7989 
7990         /**
7991          * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
7992          *
7993          * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
7994          * to store extra information specific to the layout. Client code should subclass
7995          * {@link RecyclerView.LayoutParams} for this purpose.</p>
7996          *
7997          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7998          * you must also override
7999          * {@link #checkLayoutParams(LayoutParams)},
8000          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
8001          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
8002          *
8003          * @return A new LayoutParams for a child view
8004          */
generateDefaultLayoutParams()8005         public abstract LayoutParams generateDefaultLayoutParams();
8006 
8007         /**
8008          * Determines the validity of the supplied LayoutParams object.
8009          *
8010          * <p>This should check to make sure that the object is of the correct type
8011          * and all values are within acceptable ranges. The default implementation
8012          * returns <code>true</code> for non-null params.</p>
8013          *
8014          * @param lp LayoutParams object to check
8015          * @return true if this LayoutParams object is valid, false otherwise
8016          */
checkLayoutParams(LayoutParams lp)8017         public boolean checkLayoutParams(LayoutParams lp) {
8018             return lp != null;
8019         }
8020 
8021         /**
8022          * Create a LayoutParams object suitable for this LayoutManager, copying relevant
8023          * values from the supplied LayoutParams object if possible.
8024          *
8025          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
8026          * you must also override
8027          * {@link #checkLayoutParams(LayoutParams)},
8028          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
8029          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
8030          *
8031          * @param lp Source LayoutParams object to copy values from
8032          * @return a new LayoutParams object
8033          */
generateLayoutParams(ViewGroup.LayoutParams lp)8034         public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
8035             if (lp instanceof LayoutParams) {
8036                 return new LayoutParams((LayoutParams) lp);
8037             } else if (lp instanceof MarginLayoutParams) {
8038                 return new LayoutParams((MarginLayoutParams) lp);
8039             } else {
8040                 return new LayoutParams(lp);
8041             }
8042         }
8043 
8044         /**
8045          * Create a LayoutParams object suitable for this LayoutManager from
8046          * an inflated layout resource.
8047          *
8048          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
8049          * you must also override
8050          * {@link #checkLayoutParams(LayoutParams)},
8051          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
8052          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
8053          *
8054          * @param c Context for obtaining styled attributes
8055          * @param attrs AttributeSet describing the supplied arguments
8056          * @return a new LayoutParams object
8057          */
generateLayoutParams(Context c, AttributeSet attrs)8058         public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
8059             return new LayoutParams(c, attrs);
8060         }
8061 
8062         /**
8063          * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
8064          * The default implementation does nothing and returns 0.
8065          *
8066          * @param dx            distance to scroll by in pixels. X increases as scroll position
8067          *                      approaches the right.
8068          * @param recycler      Recycler to use for fetching potentially cached views for a
8069          *                      position
8070          * @param state         Transient state of RecyclerView
8071          * @return The actual distance scrolled. The return value will be negative if dx was
8072          * negative and scrolling proceeeded in that direction.
8073          * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
8074          */
scrollHorizontallyBy(int dx, Recycler recycler, State state)8075         public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
8076             return 0;
8077         }
8078 
8079         /**
8080          * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
8081          * The default implementation does nothing and returns 0.
8082          *
8083          * @param dy            distance to scroll in pixels. Y increases as scroll position
8084          *                      approaches the bottom.
8085          * @param recycler      Recycler to use for fetching potentially cached views for a
8086          *                      position
8087          * @param state         Transient state of RecyclerView
8088          * @return The actual distance scrolled. The return value will be negative if dy was
8089          * negative and scrolling proceeeded in that direction.
8090          * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
8091          */
scrollVerticallyBy(int dy, Recycler recycler, State state)8092         public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
8093             return 0;
8094         }
8095 
8096         /**
8097          * Query if horizontal scrolling is currently supported. The default implementation
8098          * returns false.
8099          *
8100          * @return True if this LayoutManager can scroll the current contents horizontally
8101          */
canScrollHorizontally()8102         public boolean canScrollHorizontally() {
8103             return false;
8104         }
8105 
8106         /**
8107          * Query if vertical scrolling is currently supported. The default implementation
8108          * returns false.
8109          *
8110          * @return True if this LayoutManager can scroll the current contents vertically
8111          */
canScrollVertically()8112         public boolean canScrollVertically() {
8113             return false;
8114         }
8115 
8116         /**
8117          * Scroll to the specified adapter position.
8118          *
8119          * Actual position of the item on the screen depends on the LayoutManager implementation.
8120          * @param position Scroll to this adapter position.
8121          */
scrollToPosition(int position)8122         public void scrollToPosition(int position) {
8123             if (DEBUG) {
8124                 Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
8125             }
8126         }
8127 
8128         /**
8129          * <p>Smooth scroll to the specified adapter position.</p>
8130          * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
8131          * instance and call {@link #startSmoothScroll(SmoothScroller)}.
8132          * </p>
8133          * @param recyclerView The RecyclerView to which this layout manager is attached
8134          * @param state    Current State of RecyclerView
8135          * @param position Scroll to this adapter position.
8136          */
smoothScrollToPosition(RecyclerView recyclerView, State state, int position)8137         public void smoothScrollToPosition(RecyclerView recyclerView, State state,
8138                 int position) {
8139             Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
8140         }
8141 
8142         /**
8143          * Starts a smooth scroll using the provided {@link SmoothScroller}.
8144          *
8145          * <p>Each instance of SmoothScroller is intended to only be used once. Provide a new
8146          * SmoothScroller instance each time this method is called.
8147          *
8148          * <p>Calling this method will cancel any previous smooth scroll request.
8149          *
8150          * @param smoothScroller Instance which defines how smooth scroll should be animated
8151          */
startSmoothScroll(SmoothScroller smoothScroller)8152         public void startSmoothScroll(SmoothScroller smoothScroller) {
8153             if (mSmoothScroller != null && smoothScroller != mSmoothScroller
8154                     && mSmoothScroller.isRunning()) {
8155                 mSmoothScroller.stop();
8156             }
8157             mSmoothScroller = smoothScroller;
8158             mSmoothScroller.start(mRecyclerView, this);
8159         }
8160 
8161         /**
8162          * @return true if RecyclerView is currently in the state of smooth scrolling.
8163          */
isSmoothScrolling()8164         public boolean isSmoothScrolling() {
8165             return mSmoothScroller != null && mSmoothScroller.isRunning();
8166         }
8167 
8168 
8169         /**
8170          * Returns the resolved layout direction for this RecyclerView.
8171          *
8172          * @return {@link androidx.core.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout
8173          * direction is RTL or returns
8174          * {@link androidx.core.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction
8175          * is not RTL.
8176          */
getLayoutDirection()8177         public int getLayoutDirection() {
8178             return ViewCompat.getLayoutDirection(mRecyclerView);
8179         }
8180 
8181         /**
8182          * Ends all animations on the view created by the {@link ItemAnimator}.
8183          *
8184          * @param view The View for which the animations should be ended.
8185          * @see RecyclerView.ItemAnimator#endAnimations()
8186          */
endAnimation(View view)8187         public void endAnimation(View view) {
8188             if (mRecyclerView.mItemAnimator != null) {
8189                 mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
8190             }
8191         }
8192 
8193         /**
8194          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
8195          * to the layout that is known to be going away, either because it has been
8196          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
8197          * visible portion of the container but is being laid out in order to inform RecyclerView
8198          * in how to animate the item out of view.
8199          * <p>
8200          * Views added via this method are going to be invisible to LayoutManager after the
8201          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
8202          * or won't be included in {@link #getChildCount()} method.
8203          *
8204          * @param child View to add and then remove with animation.
8205          */
addDisappearingView(View child)8206         public void addDisappearingView(View child) {
8207             addDisappearingView(child, -1);
8208         }
8209 
8210         /**
8211          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
8212          * to the layout that is known to be going away, either because it has been
8213          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
8214          * visible portion of the container but is being laid out in order to inform RecyclerView
8215          * in how to animate the item out of view.
8216          * <p>
8217          * Views added via this method are going to be invisible to LayoutManager after the
8218          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
8219          * or won't be included in {@link #getChildCount()} method.
8220          *
8221          * @param child View to add and then remove with animation.
8222          * @param index Index of the view.
8223          */
addDisappearingView(View child, int index)8224         public void addDisappearingView(View child, int index) {
8225             addViewInt(child, index, true);
8226         }
8227 
8228         /**
8229          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
8230          * use this method to add views obtained from a {@link Recycler} using
8231          * {@link Recycler#getViewForPosition(int)}.
8232          *
8233          * @param child View to add
8234          */
addView(View child)8235         public void addView(View child) {
8236             addView(child, -1);
8237         }
8238 
8239         /**
8240          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
8241          * use this method to add views obtained from a {@link Recycler} using
8242          * {@link Recycler#getViewForPosition(int)}.
8243          *
8244          * @param child View to add
8245          * @param index Index to add child at
8246          */
addView(View child, int index)8247         public void addView(View child, int index) {
8248             addViewInt(child, index, false);
8249         }
8250 
addViewInt(View child, int index, boolean disappearing)8251         private void addViewInt(View child, int index, boolean disappearing) {
8252             final ViewHolder holder = getChildViewHolderInt(child);
8253             if (disappearing || holder.isRemoved()) {
8254                 // these views will be hidden at the end of the layout pass.
8255                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
8256             } else {
8257                 // This may look like unnecessary but may happen if layout manager supports
8258                 // predictive layouts and adapter removed then re-added the same item.
8259                 // In this case, added version will be visible in the post layout (because add is
8260                 // deferred) but RV will still bind it to the same View.
8261                 // So if a View re-appears in post layout pass, remove it from disappearing list.
8262                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
8263             }
8264             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8265             if (holder.wasReturnedFromScrap() || holder.isScrap()) {
8266                 if (holder.isScrap()) {
8267                     holder.unScrap();
8268                 } else {
8269                     holder.clearReturnedFromScrapFlag();
8270                 }
8271                 mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
8272                 if (DISPATCH_TEMP_DETACH) {
8273                     ViewCompat.dispatchFinishTemporaryDetach(child);
8274                 }
8275             } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
8276                 // ensure in correct position
8277                 int currentIndex = mChildHelper.indexOfChild(child);
8278                 if (index == -1) {
8279                     index = mChildHelper.getChildCount();
8280                 }
8281                 if (currentIndex == -1) {
8282                     throw new IllegalStateException("Added View has RecyclerView as parent but"
8283                             + " view is not a real child. Unfiltered index:"
8284                             + mRecyclerView.indexOfChild(child) + mRecyclerView.exceptionLabel());
8285                 }
8286                 if (currentIndex != index) {
8287                     mRecyclerView.mLayout.moveView(currentIndex, index);
8288                 }
8289             } else {
8290                 mChildHelper.addView(child, index, false);
8291                 lp.mInsetsDirty = true;
8292                 if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
8293                     mSmoothScroller.onChildAttachedToWindow(child);
8294                 }
8295             }
8296             if (lp.mPendingInvalidate) {
8297                 if (DEBUG) {
8298                     Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
8299                 }
8300                 holder.itemView.invalidate();
8301                 lp.mPendingInvalidate = false;
8302             }
8303         }
8304 
8305         /**
8306          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
8307          * use this method to completely remove a child view that is no longer needed.
8308          * LayoutManagers should strongly consider recycling removed views using
8309          * {@link Recycler#recycleView(android.view.View)}.
8310          *
8311          * @param child View to remove
8312          */
removeView(View child)8313         public void removeView(View child) {
8314             mChildHelper.removeView(child);
8315         }
8316 
8317         /**
8318          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
8319          * use this method to completely remove a child view that is no longer needed.
8320          * LayoutManagers should strongly consider recycling removed views using
8321          * {@link Recycler#recycleView(android.view.View)}.
8322          *
8323          * @param index Index of the child view to remove
8324          */
removeViewAt(int index)8325         public void removeViewAt(int index) {
8326             final View child = getChildAt(index);
8327             if (child != null) {
8328                 mChildHelper.removeViewAt(index);
8329             }
8330         }
8331 
8332         /**
8333          * Remove all views from the currently attached RecyclerView. This will not recycle
8334          * any of the affected views; the LayoutManager is responsible for doing so if desired.
8335          */
removeAllViews()8336         public void removeAllViews() {
8337             // Only remove non-animating views
8338             final int childCount = getChildCount();
8339             for (int i = childCount - 1; i >= 0; i--) {
8340                 mChildHelper.removeViewAt(i);
8341             }
8342         }
8343 
8344         /**
8345          * Returns offset of the RecyclerView's text baseline from the its top boundary.
8346          *
8347          * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
8348          * there is no baseline.
8349          */
getBaseline()8350         public int getBaseline() {
8351             return -1;
8352         }
8353 
8354         /**
8355          * Returns the adapter position of the item represented by the given View. This does not
8356          * contain any adapter changes that might have happened after the last layout.
8357          *
8358          * @param view The view to query
8359          * @return The adapter position of the item which is rendered by this View.
8360          */
getPosition(@onNull View view)8361         public int getPosition(@NonNull View view) {
8362             return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
8363         }
8364 
8365         /**
8366          * Returns the View type defined by the adapter.
8367          *
8368          * @param view The view to query
8369          * @return The type of the view assigned by the adapter.
8370          */
getItemViewType(@onNull View view)8371         public int getItemViewType(@NonNull View view) {
8372             return getChildViewHolderInt(view).getItemViewType();
8373         }
8374 
8375         /**
8376          * Traverses the ancestors of the given view and returns the item view that contains it
8377          * and also a direct child of the LayoutManager.
8378          * <p>
8379          * Note that this method may return null if the view is a child of the RecyclerView but
8380          * not a child of the LayoutManager (e.g. running a disappear animation).
8381          *
8382          * @param view The view that is a descendant of the LayoutManager.
8383          *
8384          * @return The direct child of the LayoutManager which contains the given view or null if
8385          * the provided view is not a descendant of this LayoutManager.
8386          *
8387          * @see RecyclerView#getChildViewHolder(View)
8388          * @see RecyclerView#findContainingViewHolder(View)
8389          */
8390         @Nullable
findContainingItemView(@onNull View view)8391         public View findContainingItemView(@NonNull View view) {
8392             if (mRecyclerView == null) {
8393                 return null;
8394             }
8395             View found = mRecyclerView.findContainingItemView(view);
8396             if (found == null) {
8397                 return null;
8398             }
8399             if (mChildHelper.isHidden(found)) {
8400                 return null;
8401             }
8402             return found;
8403         }
8404 
8405         /**
8406          * Finds the view which represents the given adapter position.
8407          * <p>
8408          * This method traverses each child since it has no information about child order.
8409          * Override this method to improve performance if your LayoutManager keeps data about
8410          * child views.
8411          * <p>
8412          * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
8413          *
8414          * @param position Position of the item in adapter
8415          * @return The child view that represents the given position or null if the position is not
8416          * laid out
8417          */
8418         @Nullable
findViewByPosition(int position)8419         public View findViewByPosition(int position) {
8420             final int childCount = getChildCount();
8421             for (int i = 0; i < childCount; i++) {
8422                 View child = getChildAt(i);
8423                 ViewHolder vh = getChildViewHolderInt(child);
8424                 if (vh == null) {
8425                     continue;
8426                 }
8427                 if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
8428                         && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
8429                     return child;
8430                 }
8431             }
8432             return null;
8433         }
8434 
8435         /**
8436          * Temporarily detach a child view.
8437          *
8438          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
8439          * views currently attached to the RecyclerView. Generally LayoutManager implementations
8440          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
8441          * so that the detached view may be rebound and reused.</p>
8442          *
8443          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
8444          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
8445          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
8446          * before the LayoutManager entry point method called by RecyclerView returns.</p>
8447          *
8448          * @param child Child to detach
8449          */
detachView(@onNull View child)8450         public void detachView(@NonNull View child) {
8451             final int ind = mChildHelper.indexOfChild(child);
8452             if (ind >= 0) {
8453                 detachViewInternal(ind, child);
8454             }
8455         }
8456 
8457         /**
8458          * Temporarily detach a child view.
8459          *
8460          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
8461          * views currently attached to the RecyclerView. Generally LayoutManager implementations
8462          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
8463          * so that the detached view may be rebound and reused.</p>
8464          *
8465          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
8466          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
8467          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
8468          * before the LayoutManager entry point method called by RecyclerView returns.</p>
8469          *
8470          * @param index Index of the child to detach
8471          */
detachViewAt(int index)8472         public void detachViewAt(int index) {
8473             detachViewInternal(index, getChildAt(index));
8474         }
8475 
detachViewInternal(int index, @NonNull View view)8476         private void detachViewInternal(int index, @NonNull View view) {
8477             if (DISPATCH_TEMP_DETACH) {
8478                 ViewCompat.dispatchStartTemporaryDetach(view);
8479             }
8480             mChildHelper.detachViewFromParent(index);
8481         }
8482 
8483         /**
8484          * Reattach a previously {@link #detachView(android.view.View) detached} view.
8485          * This method should not be used to reattach views that were previously
8486          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8487          *
8488          * @param child Child to reattach
8489          * @param index Intended child index for child
8490          * @param lp LayoutParams for child
8491          */
attachView(@onNull View child, int index, LayoutParams lp)8492         public void attachView(@NonNull View child, int index, LayoutParams lp) {
8493             ViewHolder vh = getChildViewHolderInt(child);
8494             if (vh.isRemoved()) {
8495                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
8496             } else {
8497                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
8498             }
8499             mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
8500             if (DISPATCH_TEMP_DETACH)  {
8501                 ViewCompat.dispatchFinishTemporaryDetach(child);
8502             }
8503         }
8504 
8505         /**
8506          * Reattach a previously {@link #detachView(android.view.View) detached} view.
8507          * This method should not be used to reattach views that were previously
8508          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8509          *
8510          * @param child Child to reattach
8511          * @param index Intended child index for child
8512          */
attachView(@onNull View child, int index)8513         public void attachView(@NonNull View child, int index) {
8514             attachView(child, index, (LayoutParams) child.getLayoutParams());
8515         }
8516 
8517         /**
8518          * Reattach a previously {@link #detachView(android.view.View) detached} view.
8519          * This method should not be used to reattach views that were previously
8520          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8521          *
8522          * @param child Child to reattach
8523          */
attachView(@onNull View child)8524         public void attachView(@NonNull View child) {
8525             attachView(child, -1);
8526         }
8527 
8528         /**
8529          * Finish removing a view that was previously temporarily
8530          * {@link #detachView(android.view.View) detached}.
8531          *
8532          * @param child Detached child to remove
8533          */
removeDetachedView(@onNull View child)8534         public void removeDetachedView(@NonNull View child) {
8535             mRecyclerView.removeDetachedView(child, false);
8536         }
8537 
8538         /**
8539          * Moves a View from one position to another.
8540          *
8541          * @param fromIndex The View's initial index
8542          * @param toIndex The View's target index
8543          */
moveView(int fromIndex, int toIndex)8544         public void moveView(int fromIndex, int toIndex) {
8545             View view = getChildAt(fromIndex);
8546             if (view == null) {
8547                 throw new IllegalArgumentException("Cannot move a child from non-existing index:"
8548                         + fromIndex + mRecyclerView.toString());
8549             }
8550             detachViewAt(fromIndex);
8551             attachView(view, toIndex);
8552         }
8553 
8554         /**
8555          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8556          *
8557          * <p>Scrapping a view allows it to be rebound and reused to show updated or
8558          * different data.</p>
8559          *
8560          * @param child Child to detach and scrap
8561          * @param recycler Recycler to deposit the new scrap view into
8562          */
detachAndScrapView(@onNull View child, @NonNull Recycler recycler)8563         public void detachAndScrapView(@NonNull View child, @NonNull Recycler recycler) {
8564             int index = mChildHelper.indexOfChild(child);
8565             scrapOrRecycleView(recycler, index, child);
8566         }
8567 
8568         /**
8569          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8570          *
8571          * <p>Scrapping a view allows it to be rebound and reused to show updated or
8572          * different data.</p>
8573          *
8574          * @param index Index of child to detach and scrap
8575          * @param recycler Recycler to deposit the new scrap view into
8576          */
detachAndScrapViewAt(int index, @NonNull Recycler recycler)8577         public void detachAndScrapViewAt(int index, @NonNull Recycler recycler) {
8578             final View child = getChildAt(index);
8579             scrapOrRecycleView(recycler, index, child);
8580         }
8581 
8582         /**
8583          * Remove a child view and recycle it using the given Recycler.
8584          *
8585          * @param child Child to remove and recycle
8586          * @param recycler Recycler to use to recycle child
8587          */
removeAndRecycleView(@onNull View child, @NonNull Recycler recycler)8588         public void removeAndRecycleView(@NonNull View child, @NonNull Recycler recycler) {
8589             removeView(child);
8590             recycler.recycleView(child);
8591         }
8592 
8593         /**
8594          * Remove a child view and recycle it using the given Recycler.
8595          *
8596          * @param index Index of child to remove and recycle
8597          * @param recycler Recycler to use to recycle child
8598          */
removeAndRecycleViewAt(int index, @NonNull Recycler recycler)8599         public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
8600             final View view = getChildAt(index);
8601             removeViewAt(index);
8602             recycler.recycleView(view);
8603         }
8604 
8605         /**
8606          * Return the current number of child views attached to the parent RecyclerView.
8607          * This does not include child views that were temporarily detached and/or scrapped.
8608          *
8609          * @return Number of attached children
8610          */
getChildCount()8611         public int getChildCount() {
8612             return mChildHelper != null ? mChildHelper.getChildCount() : 0;
8613         }
8614 
8615         /**
8616          * Return the child view at the given index
8617          * @param index Index of child to return
8618          * @return Child view at index
8619          */
8620         @Nullable
getChildAt(int index)8621         public View getChildAt(int index) {
8622             return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
8623         }
8624 
8625         /**
8626          * Return the width measurement spec mode that is currently relevant to the LayoutManager.
8627          *
8628          * <p>This value is set only if the LayoutManager opts into the AutoMeasure api via
8629          * {@link #setAutoMeasureEnabled(boolean)}.
8630          *
8631          * <p>When RecyclerView is running a layout, this value is always set to
8632          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8633          *
8634          * @return Width measure spec mode
8635          *
8636          * @see View.MeasureSpec#getMode(int)
8637          */
getWidthMode()8638         public int getWidthMode() {
8639             return mWidthMode;
8640         }
8641 
8642         /**
8643          * Return the height measurement spec mode that is currently relevant to the LayoutManager.
8644          *
8645          * <p>This value is set only if the LayoutManager opts into the AutoMeasure api via
8646          * {@link #setAutoMeasureEnabled(boolean)}.
8647          *
8648          * <p>When RecyclerView is running a layout, this value is always set to
8649          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8650          *
8651          * @return Height measure spec mode
8652          *
8653          * @see View.MeasureSpec#getMode(int)
8654          */
getHeightMode()8655         public int getHeightMode() {
8656             return mHeightMode;
8657         }
8658 
8659         /**
8660          * Returns the width that is currently relevant to the LayoutManager.
8661          *
8662          * <p>This value is usually equal to the laid out width of the {@link RecyclerView} but may
8663          * reflect the current {@link android.view.View.MeasureSpec} width if the
8664          * {@link LayoutManager} is using AutoMeasure and the RecyclerView is in the process of
8665          * measuring. The LayoutManager must always use this method to retrieve the width relevant
8666          * to it at any given time.
8667          *
8668          * @return Width in pixels
8669          */
8670         @Px
getWidth()8671         public int getWidth() {
8672             return mWidth;
8673         }
8674 
8675         /**
8676          * Returns the height that is currently relevant to the LayoutManager.
8677          *
8678          * <p>This value is usually equal to the laid out height of the {@link RecyclerView} but may
8679          * reflect the current {@link android.view.View.MeasureSpec} height if the
8680          * {@link LayoutManager} is using AutoMeasure and the RecyclerView is in the process of
8681          * measuring. The LayoutManager must always use this method to retrieve the height relevant
8682          * to it at any given time.
8683          *
8684          * @return Height in pixels
8685          */
8686         @Px
getHeight()8687         public int getHeight() {
8688             return mHeight;
8689         }
8690 
8691         /**
8692          * Return the left padding of the parent RecyclerView
8693          *
8694          * @return Padding in pixels
8695          */
8696         @Px
getPaddingLeft()8697         public int getPaddingLeft() {
8698             return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
8699         }
8700 
8701         /**
8702          * Return the top padding of the parent RecyclerView
8703          *
8704          * @return Padding in pixels
8705          */
8706         @Px
getPaddingTop()8707         public int getPaddingTop() {
8708             return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
8709         }
8710 
8711         /**
8712          * Return the right padding of the parent RecyclerView
8713          *
8714          * @return Padding in pixels
8715          */
8716         @Px
getPaddingRight()8717         public int getPaddingRight() {
8718             return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
8719         }
8720 
8721         /**
8722          * Return the bottom padding of the parent RecyclerView
8723          *
8724          * @return Padding in pixels
8725          */
8726         @Px
getPaddingBottom()8727         public int getPaddingBottom() {
8728             return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
8729         }
8730 
8731         /**
8732          * Return the start padding of the parent RecyclerView
8733          *
8734          * @return Padding in pixels
8735          */
8736         @Px
getPaddingStart()8737         public int getPaddingStart() {
8738             return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;
8739         }
8740 
8741         /**
8742          * Return the end padding of the parent RecyclerView
8743          *
8744          * @return Padding in pixels
8745          */
8746         @Px
getPaddingEnd()8747         public int getPaddingEnd() {
8748             return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;
8749         }
8750 
8751         /**
8752          * Returns true if the RecyclerView this LayoutManager is bound to has focus.
8753          *
8754          * @return True if the RecyclerView has focus, false otherwise.
8755          * @see View#isFocused()
8756          */
isFocused()8757         public boolean isFocused() {
8758             return mRecyclerView != null && mRecyclerView.isFocused();
8759         }
8760 
8761         /**
8762          * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
8763          *
8764          * @return true if the RecyclerView has or contains focus
8765          * @see View#hasFocus()
8766          */
hasFocus()8767         public boolean hasFocus() {
8768             return mRecyclerView != null && mRecyclerView.hasFocus();
8769         }
8770 
8771         /**
8772          * Returns the item View which has or contains focus.
8773          *
8774          * @return A direct child of RecyclerView which has focus or contains the focused child.
8775          */
8776         @Nullable
getFocusedChild()8777         public View getFocusedChild() {
8778             if (mRecyclerView == null) {
8779                 return null;
8780             }
8781             final View focused = mRecyclerView.getFocusedChild();
8782             if (focused == null || mChildHelper.isHidden(focused)) {
8783                 return null;
8784             }
8785             return focused;
8786         }
8787 
8788         /**
8789          * Returns the number of items in the adapter bound to the parent RecyclerView.
8790          * <p>
8791          * Note that this number is not necessarily equal to
8792          * {@link State#getItemCount() State#getItemCount()}. In methods where {@link State} is
8793          * available, you should use {@link State#getItemCount() State#getItemCount()} instead.
8794          * For more details, check the documentation for
8795          * {@link State#getItemCount() State#getItemCount()}.
8796          *
8797          * @return The number of items in the bound adapter
8798          * @see State#getItemCount()
8799          */
getItemCount()8800         public int getItemCount() {
8801             final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
8802             return a != null ? a.getItemCount() : 0;
8803         }
8804 
8805         /**
8806          * Offset all child views attached to the parent RecyclerView by dx pixels along
8807          * the horizontal axis.
8808          *
8809          * @param dx Pixels to offset by
8810          */
offsetChildrenHorizontal(@x int dx)8811         public void offsetChildrenHorizontal(@Px int dx) {
8812             if (mRecyclerView != null) {
8813                 mRecyclerView.offsetChildrenHorizontal(dx);
8814             }
8815         }
8816 
8817         /**
8818          * Offset all child views attached to the parent RecyclerView by dy pixels along
8819          * the vertical axis.
8820          *
8821          * @param dy Pixels to offset by
8822          */
offsetChildrenVertical(@x int dy)8823         public void offsetChildrenVertical(@Px int dy) {
8824             if (mRecyclerView != null) {
8825                 mRecyclerView.offsetChildrenVertical(dy);
8826             }
8827         }
8828 
8829         /**
8830          * Flags a view so that it will not be scrapped or recycled.
8831          * <p>
8832          * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
8833          * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
8834          * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
8835          * ignore the child.
8836          * <p>
8837          * Before this child can be recycled again, you have to call
8838          * {@link #stopIgnoringView(View)}.
8839          * <p>
8840          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8841          *
8842          * @param view View to ignore.
8843          * @see #stopIgnoringView(View)
8844          */
ignoreView(@onNull View view)8845         public void ignoreView(@NonNull View view) {
8846             if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
8847                 // checking this because calling this method on a recycled or detached view may
8848                 // cause loss of state.
8849                 throw new IllegalArgumentException("View should be fully attached to be ignored"
8850                         + mRecyclerView.exceptionLabel());
8851             }
8852             final ViewHolder vh = getChildViewHolderInt(view);
8853             vh.addFlags(ViewHolder.FLAG_IGNORE);
8854             mRecyclerView.mViewInfoStore.removeViewHolder(vh);
8855         }
8856 
8857         /**
8858          * View can be scrapped and recycled again.
8859          * <p>
8860          * Note that calling this method removes all information in the view holder.
8861          * <p>
8862          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8863          *
8864          * @param view View to ignore.
8865          */
stopIgnoringView(@onNull View view)8866         public void stopIgnoringView(@NonNull View view) {
8867             final ViewHolder vh = getChildViewHolderInt(view);
8868             vh.stopIgnoring();
8869             vh.resetInternal();
8870             vh.addFlags(ViewHolder.FLAG_INVALID);
8871         }
8872 
8873         /**
8874          * Temporarily detach and scrap all currently attached child views. Views will be scrapped
8875          * into the given Recycler. The Recycler may prefer to reuse scrap views before
8876          * other views that were previously recycled.
8877          *
8878          * @param recycler Recycler to scrap views into
8879          */
detachAndScrapAttachedViews(@onNull Recycler recycler)8880         public void detachAndScrapAttachedViews(@NonNull Recycler recycler) {
8881             final int childCount = getChildCount();
8882             for (int i = childCount - 1; i >= 0; i--) {
8883                 final View v = getChildAt(i);
8884                 scrapOrRecycleView(recycler, i, v);
8885             }
8886         }
8887 
scrapOrRecycleView(Recycler recycler, int index, View view)8888         private void scrapOrRecycleView(Recycler recycler, int index, View view) {
8889             final ViewHolder viewHolder = getChildViewHolderInt(view);
8890             if (viewHolder.shouldIgnore()) {
8891                 if (DEBUG) {
8892                     Log.d(TAG, "ignoring view " + viewHolder);
8893                 }
8894                 return;
8895             }
8896             if (viewHolder.isInvalid() && !viewHolder.isRemoved()
8897                     && !mRecyclerView.mAdapter.hasStableIds()) {
8898                 removeViewAt(index);
8899                 recycler.recycleViewHolderInternal(viewHolder);
8900             } else {
8901                 detachViewAt(index);
8902                 recycler.scrapView(view);
8903                 mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
8904             }
8905         }
8906 
8907         /**
8908          * Recycles the scrapped views.
8909          * <p>
8910          * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
8911          * the expected behavior if scrapped views are used for animations. Otherwise, we need to
8912          * call remove and invalidate RecyclerView to ensure UI update.
8913          *
8914          * @param recycler Recycler
8915          */
removeAndRecycleScrapInt(Recycler recycler)8916         void removeAndRecycleScrapInt(Recycler recycler) {
8917             final int scrapCount = recycler.getScrapCount();
8918             // Loop backward, recycler might be changed by removeDetachedView()
8919             for (int i = scrapCount - 1; i >= 0; i--) {
8920                 final View scrap = recycler.getScrapViewAt(i);
8921                 final ViewHolder vh = getChildViewHolderInt(scrap);
8922                 if (vh.shouldIgnore()) {
8923                     continue;
8924                 }
8925                 // If the scrap view is animating, we need to cancel them first. If we cancel it
8926                 // here, ItemAnimator callback may recycle it which will cause double recycling.
8927                 // To avoid this, we mark it as not recycleable before calling the item animator.
8928                 // Since removeDetachedView calls a user API, a common mistake (ending animations on
8929                 // the view) may recycle it too, so we guard it before we call user APIs.
8930                 vh.setIsRecyclable(false);
8931                 if (vh.isTmpDetached()) {
8932                     mRecyclerView.removeDetachedView(scrap, false);
8933                 }
8934                 if (mRecyclerView.mItemAnimator != null) {
8935                     mRecyclerView.mItemAnimator.endAnimation(vh);
8936                 }
8937                 vh.setIsRecyclable(true);
8938                 recycler.quickRecycleScrapView(scrap);
8939             }
8940             recycler.clearScrap();
8941             if (scrapCount > 0) {
8942                 mRecyclerView.invalidate();
8943             }
8944         }
8945 
8946 
8947         /**
8948          * Measure a child view using standard measurement policy, taking the padding
8949          * of the parent RecyclerView and any added item decorations into account.
8950          *
8951          * <p>If the RecyclerView can be scrolled in either dimension the caller may
8952          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8953          *
8954          * @param child Child view to measure
8955          * @param widthUsed Width in pixels currently consumed by other views, if relevant
8956          * @param heightUsed Height in pixels currently consumed by other views, if relevant
8957          */
measureChild(@onNull View child, int widthUsed, int heightUsed)8958         public void measureChild(@NonNull View child, int widthUsed, int heightUsed) {
8959             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8960 
8961             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8962             widthUsed += insets.left + insets.right;
8963             heightUsed += insets.top + insets.bottom;
8964             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8965                     getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
8966                     canScrollHorizontally());
8967             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8968                     getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
8969                     canScrollVertically());
8970             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8971                 child.measure(widthSpec, heightSpec);
8972             }
8973         }
8974 
8975         /**
8976          * RecyclerView internally does its own View measurement caching which should help with
8977          * WRAP_CONTENT.
8978          * <p>
8979          * Use this method if the View is already measured once in this layout pass.
8980          */
shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp)8981         boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8982             return !mMeasurementCacheEnabled
8983                     || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
8984                     || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
8985         }
8986 
8987         // we may consider making this public
8988         /**
8989          * RecyclerView internally does its own View measurement caching which should help with
8990          * WRAP_CONTENT.
8991          * <p>
8992          * Use this method if the View is not yet measured and you need to decide whether to
8993          * measure this View or not.
8994          */
shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp)8995         boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8996             return child.isLayoutRequested()
8997                     || !mMeasurementCacheEnabled
8998                     || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
8999                     || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
9000         }
9001 
9002         /**
9003          * In addition to the View Framework's measurement cache, RecyclerView uses its own
9004          * additional measurement cache for its children to avoid re-measuring them when not
9005          * necessary. It is on by default but it can be turned off via
9006          * {@link #setMeasurementCacheEnabled(boolean)}.
9007          *
9008          * @return True if measurement cache is enabled, false otherwise.
9009          *
9010          * @see #setMeasurementCacheEnabled(boolean)
9011          */
isMeasurementCacheEnabled()9012         public boolean isMeasurementCacheEnabled() {
9013             return mMeasurementCacheEnabled;
9014         }
9015 
9016         /**
9017          * Sets whether RecyclerView should use its own measurement cache for the children. This is
9018          * a more aggressive cache than the framework uses.
9019          *
9020          * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
9021          *
9022          * @see #isMeasurementCacheEnabled()
9023          */
setMeasurementCacheEnabled(boolean measurementCacheEnabled)9024         public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
9025             mMeasurementCacheEnabled = measurementCacheEnabled;
9026         }
9027 
isMeasurementUpToDate(int childSize, int spec, int dimension)9028         private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
9029             final int specMode = MeasureSpec.getMode(spec);
9030             final int specSize = MeasureSpec.getSize(spec);
9031             if (dimension > 0 && childSize != dimension) {
9032                 return false;
9033             }
9034             switch (specMode) {
9035                 case MeasureSpec.UNSPECIFIED:
9036                     return true;
9037                 case MeasureSpec.AT_MOST:
9038                     return specSize >= childSize;
9039                 case MeasureSpec.EXACTLY:
9040                     return  specSize == childSize;
9041             }
9042             return false;
9043         }
9044 
9045         /**
9046          * Measure a child view using standard measurement policy, taking the padding
9047          * of the parent RecyclerView, any added item decorations and the child margins
9048          * into account.
9049          *
9050          * <p>If the RecyclerView can be scrolled in either dimension the caller may
9051          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
9052          *
9053          * @param child Child view to measure
9054          * @param widthUsed Width in pixels currently consumed by other views, if relevant
9055          * @param heightUsed Height in pixels currently consumed by other views, if relevant
9056          */
measureChildWithMargins(@onNull View child, int widthUsed, int heightUsed)9057         public void measureChildWithMargins(@NonNull View child, int widthUsed, int heightUsed) {
9058             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
9059 
9060             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
9061             widthUsed += insets.left + insets.right;
9062             heightUsed += insets.top + insets.bottom;
9063 
9064             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
9065                     getPaddingLeft() + getPaddingRight()
9066                             + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
9067                     canScrollHorizontally());
9068             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
9069                     getPaddingTop() + getPaddingBottom()
9070                             + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
9071                     canScrollVertically());
9072             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
9073                 child.measure(widthSpec, heightSpec);
9074             }
9075         }
9076 
9077         /**
9078          * Calculate a MeasureSpec value for measuring a child view in one dimension.
9079          *
9080          * @param parentSize Size of the parent view where the child will be placed
9081          * @param padding Total space currently consumed by other elements of the parent
9082          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
9083          *                       Generally obtained from the child view's LayoutParams
9084          * @param canScroll true if the parent RecyclerView can scroll in this dimension
9085          *
9086          * @return a MeasureSpec value for the child view
9087          * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
9088          */
9089         @Deprecated
getChildMeasureSpec(int parentSize, int padding, int childDimension, boolean canScroll)9090         public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
9091                 boolean canScroll) {
9092             int size = Math.max(0, parentSize - padding);
9093             int resultSize = 0;
9094             int resultMode = 0;
9095             if (canScroll) {
9096                 if (childDimension >= 0) {
9097                     resultSize = childDimension;
9098                     resultMode = MeasureSpec.EXACTLY;
9099                 } else {
9100                     // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
9101                     // instead using UNSPECIFIED.
9102                     resultSize = 0;
9103                     resultMode = MeasureSpec.UNSPECIFIED;
9104                 }
9105             } else {
9106                 if (childDimension >= 0) {
9107                     resultSize = childDimension;
9108                     resultMode = MeasureSpec.EXACTLY;
9109                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
9110                     resultSize = size;
9111                     // TODO this should be my spec.
9112                     resultMode = MeasureSpec.EXACTLY;
9113                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
9114                     resultSize = size;
9115                     resultMode = MeasureSpec.AT_MOST;
9116                 }
9117             }
9118             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
9119         }
9120 
9121         /**
9122          * Calculate a MeasureSpec value for measuring a child view in one dimension.
9123          *
9124          * @param parentSize Size of the parent view where the child will be placed
9125          * @param parentMode The measurement spec mode of the parent
9126          * @param padding Total space currently consumed by other elements of parent
9127          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
9128          *                       Generally obtained from the child view's LayoutParams
9129          * @param canScroll true if the parent RecyclerView can scroll in this dimension
9130          *
9131          * @return a MeasureSpec value for the child view
9132          */
getChildMeasureSpec(int parentSize, int parentMode, int padding, int childDimension, boolean canScroll)9133         public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
9134                 int childDimension, boolean canScroll) {
9135             int size = Math.max(0, parentSize - padding);
9136             int resultSize = 0;
9137             int resultMode = 0;
9138             if (canScroll) {
9139                 if (childDimension >= 0) {
9140                     resultSize = childDimension;
9141                     resultMode = MeasureSpec.EXACTLY;
9142                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
9143                     switch (parentMode) {
9144                         case MeasureSpec.AT_MOST:
9145                         case MeasureSpec.EXACTLY:
9146                             resultSize = size;
9147                             resultMode = parentMode;
9148                             break;
9149                         case MeasureSpec.UNSPECIFIED:
9150                             resultSize = 0;
9151                             resultMode = MeasureSpec.UNSPECIFIED;
9152                             break;
9153                     }
9154                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
9155                     resultSize = 0;
9156                     resultMode = MeasureSpec.UNSPECIFIED;
9157                 }
9158             } else {
9159                 if (childDimension >= 0) {
9160                     resultSize = childDimension;
9161                     resultMode = MeasureSpec.EXACTLY;
9162                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
9163                     resultSize = size;
9164                     resultMode = parentMode;
9165                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
9166                     resultSize = size;
9167                     if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
9168                         resultMode = MeasureSpec.AT_MOST;
9169                     } else {
9170                         resultMode = MeasureSpec.UNSPECIFIED;
9171                     }
9172 
9173                 }
9174             }
9175             //noinspection WrongConstant
9176             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
9177         }
9178 
9179         /**
9180          * Returns the measured width of the given child, plus the additional size of
9181          * any insets applied by {@link ItemDecoration ItemDecorations}.
9182          *
9183          * @param child Child view to query
9184          * @return child's measured width plus <code>ItemDecoration</code> insets
9185          *
9186          * @see View#getMeasuredWidth()
9187          */
getDecoratedMeasuredWidth(@onNull View child)9188         public int getDecoratedMeasuredWidth(@NonNull View child) {
9189             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9190             return child.getMeasuredWidth() + insets.left + insets.right;
9191         }
9192 
9193         /**
9194          * Returns the measured height of the given child, plus the additional size of
9195          * any insets applied by {@link ItemDecoration ItemDecorations}.
9196          *
9197          * @param child Child view to query
9198          * @return child's measured height plus <code>ItemDecoration</code> insets
9199          *
9200          * @see View#getMeasuredHeight()
9201          */
getDecoratedMeasuredHeight(@onNull View child)9202         public int getDecoratedMeasuredHeight(@NonNull View child) {
9203             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9204             return child.getMeasuredHeight() + insets.top + insets.bottom;
9205         }
9206 
9207         /**
9208          * Lay out the given child view within the RecyclerView using coordinates that
9209          * include any current {@link ItemDecoration ItemDecorations}.
9210          *
9211          * <p>LayoutManagers should prefer working in sizes and coordinates that include
9212          * item decoration insets whenever possible. This allows the LayoutManager to effectively
9213          * ignore decoration insets within measurement and layout code. See the following
9214          * methods:</p>
9215          * <ul>
9216          *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
9217          *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
9218          *     <li>{@link #measureChild(View, int, int)}</li>
9219          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
9220          *     <li>{@link #getDecoratedLeft(View)}</li>
9221          *     <li>{@link #getDecoratedTop(View)}</li>
9222          *     <li>{@link #getDecoratedRight(View)}</li>
9223          *     <li>{@link #getDecoratedBottom(View)}</li>
9224          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
9225          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
9226          * </ul>
9227          *
9228          * @param child Child to lay out
9229          * @param left Left edge, with item decoration insets included
9230          * @param top Top edge, with item decoration insets included
9231          * @param right Right edge, with item decoration insets included
9232          * @param bottom Bottom edge, with item decoration insets included
9233          *
9234          * @see View#layout(int, int, int, int)
9235          * @see #layoutDecoratedWithMargins(View, int, int, int, int)
9236          */
layoutDecorated(@onNull View child, int left, int top, int right, int bottom)9237         public void layoutDecorated(@NonNull View child, int left, int top, int right, int bottom) {
9238             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9239             child.layout(left + insets.left, top + insets.top, right - insets.right,
9240                     bottom - insets.bottom);
9241         }
9242 
9243         /**
9244          * Lay out the given child view within the RecyclerView using coordinates that
9245          * include any current {@link ItemDecoration ItemDecorations} and margins.
9246          *
9247          * <p>LayoutManagers should prefer working in sizes and coordinates that include
9248          * item decoration insets whenever possible. This allows the LayoutManager to effectively
9249          * ignore decoration insets within measurement and layout code. See the following
9250          * methods:</p>
9251          * <ul>
9252          *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
9253          *     <li>{@link #measureChild(View, int, int)}</li>
9254          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
9255          *     <li>{@link #getDecoratedLeft(View)}</li>
9256          *     <li>{@link #getDecoratedTop(View)}</li>
9257          *     <li>{@link #getDecoratedRight(View)}</li>
9258          *     <li>{@link #getDecoratedBottom(View)}</li>
9259          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
9260          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
9261          * </ul>
9262          *
9263          * @param child Child to lay out
9264          * @param left Left edge, with item decoration insets and left margin included
9265          * @param top Top edge, with item decoration insets and top margin included
9266          * @param right Right edge, with item decoration insets and right margin included
9267          * @param bottom Bottom edge, with item decoration insets and bottom margin included
9268          *
9269          * @see View#layout(int, int, int, int)
9270          * @see #layoutDecorated(View, int, int, int, int)
9271          */
layoutDecoratedWithMargins(@onNull View child, int left, int top, int right, int bottom)9272         public void layoutDecoratedWithMargins(@NonNull View child, int left, int top, int right,
9273                 int bottom) {
9274             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
9275             final Rect insets = lp.mDecorInsets;
9276             child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
9277                     right - insets.right - lp.rightMargin,
9278                     bottom - insets.bottom - lp.bottomMargin);
9279         }
9280 
9281         /**
9282          * Calculates the bounding box of the View while taking into account its matrix changes
9283          * (translation, scale etc) with respect to the RecyclerView.
9284          * <p>
9285          * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
9286          * the View's matrix so that the decor offsets also go through the same transformation.
9287          *
9288          * @param child The ItemView whose bounding box should be calculated.
9289          * @param includeDecorInsets True if the decor insets should be included in the bounding box
9290          * @param out The rectangle into which the output will be written.
9291          */
getTransformedBoundingBox(@onNull View child, boolean includeDecorInsets, @NonNull Rect out)9292         public void getTransformedBoundingBox(@NonNull View child, boolean includeDecorInsets,
9293                 @NonNull Rect out) {
9294             if (includeDecorInsets) {
9295                 Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9296                 out.set(-insets.left, -insets.top,
9297                         child.getWidth() + insets.right, child.getHeight() + insets.bottom);
9298             } else {
9299                 out.set(0, 0, child.getWidth(), child.getHeight());
9300             }
9301 
9302             if (mRecyclerView != null) {
9303                 final Matrix childMatrix = child.getMatrix();
9304                 if (childMatrix != null && !childMatrix.isIdentity()) {
9305                     final RectF tempRectF = mRecyclerView.mTempRectF;
9306                     tempRectF.set(out);
9307                     childMatrix.mapRect(tempRectF);
9308                     out.set(
9309                             (int) Math.floor(tempRectF.left),
9310                             (int) Math.floor(tempRectF.top),
9311                             (int) Math.ceil(tempRectF.right),
9312                             (int) Math.ceil(tempRectF.bottom)
9313                     );
9314                 }
9315             }
9316             out.offset(child.getLeft(), child.getTop());
9317         }
9318 
9319         /**
9320          * Returns the bounds of the view including its decoration and margins.
9321          *
9322          * @param view The view element to check
9323          * @param outBounds A rect that will receive the bounds of the element including its
9324          *                  decoration and margins.
9325          */
getDecoratedBoundsWithMargins(@onNull View view, @NonNull Rect outBounds)9326         public void getDecoratedBoundsWithMargins(@NonNull View view, @NonNull Rect outBounds) {
9327             RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
9328         }
9329 
9330         /**
9331          * Returns the left edge of the given child view within its parent, offset by any applied
9332          * {@link ItemDecoration ItemDecorations}.
9333          *
9334          * @param child Child to query
9335          * @return Child left edge with offsets applied
9336          * @see #getLeftDecorationWidth(View)
9337          */
getDecoratedLeft(@onNull View child)9338         public int getDecoratedLeft(@NonNull View child) {
9339             return child.getLeft() - getLeftDecorationWidth(child);
9340         }
9341 
9342         /**
9343          * Returns the top edge of the given child view within its parent, offset by any applied
9344          * {@link ItemDecoration ItemDecorations}.
9345          *
9346          * @param child Child to query
9347          * @return Child top edge with offsets applied
9348          * @see #getTopDecorationHeight(View)
9349          */
getDecoratedTop(@onNull View child)9350         public int getDecoratedTop(@NonNull View child) {
9351             return child.getTop() - getTopDecorationHeight(child);
9352         }
9353 
9354         /**
9355          * Returns the right edge of the given child view within its parent, offset by any applied
9356          * {@link ItemDecoration ItemDecorations}.
9357          *
9358          * @param child Child to query
9359          * @return Child right edge with offsets applied
9360          * @see #getRightDecorationWidth(View)
9361          */
getDecoratedRight(@onNull View child)9362         public int getDecoratedRight(@NonNull View child) {
9363             return child.getRight() + getRightDecorationWidth(child);
9364         }
9365 
9366         /**
9367          * Returns the bottom edge of the given child view within its parent, offset by any applied
9368          * {@link ItemDecoration ItemDecorations}.
9369          *
9370          * @param child Child to query
9371          * @return Child bottom edge with offsets applied
9372          * @see #getBottomDecorationHeight(View)
9373          */
getDecoratedBottom(@onNull View child)9374         public int getDecoratedBottom(@NonNull View child) {
9375             return child.getBottom() + getBottomDecorationHeight(child);
9376         }
9377 
9378         /**
9379          * Calculates the item decor insets applied to the given child and updates the provided
9380          * Rect instance with the inset values.
9381          * <ul>
9382          *     <li>The Rect's left is set to the total width of left decorations.</li>
9383          *     <li>The Rect's top is set to the total height of top decorations.</li>
9384          *     <li>The Rect's right is set to the total width of right decorations.</li>
9385          *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
9386          * </ul>
9387          * <p>
9388          * Note that item decorations are automatically calculated when one of the LayoutManager's
9389          * measure child methods is called. If you need to measure the child with custom specs via
9390          * {@link View#measure(int, int)}, you can use this method to get decorations.
9391          *
9392          * @param child The child view whose decorations should be calculated
9393          * @param outRect The Rect to hold result values
9394          */
calculateItemDecorationsForChild(@onNull View child, @NonNull Rect outRect)9395         public void calculateItemDecorationsForChild(@NonNull View child, @NonNull Rect outRect) {
9396             if (mRecyclerView == null) {
9397                 outRect.set(0, 0, 0, 0);
9398                 return;
9399             }
9400             Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
9401             outRect.set(insets);
9402         }
9403 
9404         /**
9405          * Returns the total height of item decorations applied to child's top.
9406          * <p>
9407          * Note that this value is not updated until the View is measured or
9408          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9409          *
9410          * @param child Child to query
9411          * @return The total height of item decorations applied to the child's top.
9412          * @see #getDecoratedTop(View)
9413          * @see #calculateItemDecorationsForChild(View, Rect)
9414          */
getTopDecorationHeight(@onNull View child)9415         public int getTopDecorationHeight(@NonNull View child) {
9416             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
9417         }
9418 
9419         /**
9420          * Returns the total height of item decorations applied to child's bottom.
9421          * <p>
9422          * Note that this value is not updated until the View is measured or
9423          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9424          *
9425          * @param child Child to query
9426          * @return The total height of item decorations applied to the child's bottom.
9427          * @see #getDecoratedBottom(View)
9428          * @see #calculateItemDecorationsForChild(View, Rect)
9429          */
getBottomDecorationHeight(@onNull View child)9430         public int getBottomDecorationHeight(@NonNull View child) {
9431             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
9432         }
9433 
9434         /**
9435          * Returns the total width of item decorations applied to child's left.
9436          * <p>
9437          * Note that this value is not updated until the View is measured or
9438          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9439          *
9440          * @param child Child to query
9441          * @return The total width of item decorations applied to the child's left.
9442          * @see #getDecoratedLeft(View)
9443          * @see #calculateItemDecorationsForChild(View, Rect)
9444          */
getLeftDecorationWidth(@onNull View child)9445         public int getLeftDecorationWidth(@NonNull View child) {
9446             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
9447         }
9448 
9449         /**
9450          * Returns the total width of item decorations applied to child's right.
9451          * <p>
9452          * Note that this value is not updated until the View is measured or
9453          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9454          *
9455          * @param child Child to query
9456          * @return The total width of item decorations applied to the child's right.
9457          * @see #getDecoratedRight(View)
9458          * @see #calculateItemDecorationsForChild(View, Rect)
9459          */
getRightDecorationWidth(@onNull View child)9460         public int getRightDecorationWidth(@NonNull View child) {
9461             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
9462         }
9463 
9464         /**
9465          * Called when searching for a focusable view in the given direction has failed
9466          * for the current content of the RecyclerView.
9467          *
9468          * <p>This is the LayoutManager's opportunity to populate views in the given direction
9469          * to fulfill the request if it can. The LayoutManager should attach and return
9470          * the view to be focused, if a focusable view in the given direction is found.
9471          * Otherwise, if all the existing (or the newly populated views) are unfocusable, it returns
9472          * the next unfocusable view to become visible on the screen. This unfocusable view is
9473          * typically the first view that's either partially or fully out of RV's padded bounded
9474          * area in the given direction. The default implementation returns null.</p>
9475          *
9476          * @param focused   The currently focused view
9477          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9478          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9479          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9480          *                  or 0 for not applicable
9481          * @param recycler  The recycler to use for obtaining views for currently offscreen items
9482          * @param state     Transient state of RecyclerView
9483          * @return The chosen view to be focused if a focusable view is found, otherwise an
9484          * unfocusable view to become visible onto the screen, else null.
9485          */
9486         @Nullable
onFocusSearchFailed(@onNull View focused, int direction, @NonNull Recycler recycler, @NonNull State state)9487         public View onFocusSearchFailed(@NonNull View focused, int direction,
9488                 @NonNull Recycler recycler, @NonNull State state) {
9489             return null;
9490         }
9491 
9492         /**
9493          * This method gives a LayoutManager an opportunity to intercept the initial focus search
9494          * before the default behavior of {@link FocusFinder} is used. If this method returns
9495          * null FocusFinder will attempt to find a focusable child view. If it fails
9496          * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
9497          * will be called to give the LayoutManager an opportunity to add new views for items
9498          * that did not have attached views representing them. The LayoutManager should not add
9499          * or remove views from this method.
9500          *
9501          * @param focused The currently focused view
9502          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9503          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9504          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9505          * @return A descendant view to focus or null to fall back to default behavior.
9506          *         The default implementation returns null.
9507          */
9508         @Nullable
onInterceptFocusSearch(@onNull View focused, int direction)9509         public View onInterceptFocusSearch(@NonNull View focused, int direction) {
9510             return null;
9511         }
9512 
9513         /**
9514          * Returns the scroll amount that brings the given rect in child's coordinate system within
9515          * the padded area of RecyclerView.
9516          * @param parent The parent RecyclerView.
9517          * @param child The direct child making the request.
9518          * @param rect The rectangle in the child's coordinates the child
9519          *             wishes to be on the screen.
9520          * @param immediate True to forbid animated or delayed scrolling,
9521          *                  false otherwise
9522          * @return The array containing the scroll amount in x and y directions that brings the
9523          * given rect into RV's padded area.
9524          */
getChildRectangleOnScreenScrollAmount(RecyclerView parent, View child, Rect rect, boolean immediate)9525         private int[] getChildRectangleOnScreenScrollAmount(RecyclerView parent, View child,
9526                 Rect rect, boolean immediate) {
9527             int[] out = new int[2];
9528             final int parentLeft = getPaddingLeft();
9529             final int parentTop = getPaddingTop();
9530             final int parentRight = getWidth() - getPaddingRight();
9531             final int parentBottom = getHeight() - getPaddingBottom();
9532             final int childLeft = child.getLeft() + rect.left - child.getScrollX();
9533             final int childTop = child.getTop() + rect.top - child.getScrollY();
9534             final int childRight = childLeft + rect.width();
9535             final int childBottom = childTop + rect.height();
9536 
9537             final int offScreenLeft = Math.min(0, childLeft - parentLeft);
9538             final int offScreenTop = Math.min(0, childTop - parentTop);
9539             final int offScreenRight = Math.max(0, childRight - parentRight);
9540             final int offScreenBottom = Math.max(0, childBottom - parentBottom);
9541 
9542             // Favor the "start" layout direction over the end when bringing one side or the other
9543             // of a large rect into view. If we decide to bring in end because start is already
9544             // visible, limit the scroll such that start won't go out of bounds.
9545             final int dx;
9546             if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) {
9547                 dx = offScreenRight != 0 ? offScreenRight
9548                         : Math.max(offScreenLeft, childRight - parentRight);
9549             } else {
9550                 dx = offScreenLeft != 0 ? offScreenLeft
9551                         : Math.min(childLeft - parentLeft, offScreenRight);
9552             }
9553 
9554             // Favor bringing the top into view over the bottom. If top is already visible and
9555             // we should scroll to make bottom visible, make sure top does not go out of bounds.
9556             final int dy = offScreenTop != 0 ? offScreenTop
9557                     : Math.min(childTop - parentTop, offScreenBottom);
9558             out[0] = dx;
9559             out[1] = dy;
9560             return out;
9561         }
9562         /**
9563          * Called when a child of the RecyclerView wants a particular rectangle to be positioned
9564          * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
9565          * android.graphics.Rect, boolean)} for more details.
9566          *
9567          * <p>The base implementation will attempt to perform a standard programmatic scroll
9568          * to bring the given rect into view, within the padded area of the RecyclerView.</p>
9569          *
9570          * @param child The direct child making the request.
9571          * @param rect  The rectangle in the child's coordinates the child
9572          *              wishes to be on the screen.
9573          * @param immediate True to forbid animated or delayed scrolling,
9574          *                  false otherwise
9575          * @return Whether the group scrolled to handle the operation
9576          */
requestChildRectangleOnScreen(@onNull RecyclerView parent, @NonNull View child, @NonNull Rect rect, boolean immediate)9577         public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
9578                 @NonNull View child, @NonNull Rect rect, boolean immediate) {
9579             return requestChildRectangleOnScreen(parent, child, rect, immediate, false);
9580         }
9581 
9582         /**
9583          * Requests that the given child of the RecyclerView be positioned onto the screen. This
9584          * method can be called for both unfocusable and focusable child views. For unfocusable
9585          * child views, focusedChildVisible is typically true in which case, layout manager
9586          * makes the child view visible only if the currently focused child stays in-bounds of RV.
9587          * @param parent The parent RecyclerView.
9588          * @param child The direct child making the request.
9589          * @param rect The rectangle in the child's coordinates the child
9590          *              wishes to be on the screen.
9591          * @param immediate True to forbid animated or delayed scrolling,
9592          *                  false otherwise
9593          * @param focusedChildVisible Whether the currently focused view must stay visible.
9594          * @return Whether the group scrolled to handle the operation
9595          */
requestChildRectangleOnScreen(@onNull RecyclerView parent, @NonNull View child, @NonNull Rect rect, boolean immediate, boolean focusedChildVisible)9596         public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
9597                 @NonNull View child, @NonNull Rect rect, boolean immediate,
9598                 boolean focusedChildVisible) {
9599             int[] scrollAmount = getChildRectangleOnScreenScrollAmount(parent, child, rect,
9600                     immediate);
9601             int dx = scrollAmount[0];
9602             int dy = scrollAmount[1];
9603             if (!focusedChildVisible || isFocusedChildVisibleAfterScrolling(parent, dx, dy)) {
9604                 if (dx != 0 || dy != 0) {
9605                     if (immediate) {
9606                         parent.scrollBy(dx, dy);
9607                     } else {
9608                         parent.smoothScrollBy(dx, dy);
9609                     }
9610                     return true;
9611                 }
9612             }
9613             return false;
9614         }
9615 
9616         /**
9617          * Returns whether the given child view is partially or fully visible within the padded
9618          * bounded area of RecyclerView, depending on the input parameters.
9619          * A view is partially visible if it has non-zero overlap with RV's padded bounded area.
9620          * If acceptEndPointInclusion flag is set to true, it's also considered partially
9621          * visible if it's located outside RV's bounds and it's hitting either RV's start or end
9622          * bounds.
9623          *
9624          * @param child The child view to be examined.
9625          * @param completelyVisible If true, the method returns true if and only if the child is
9626          *                          completely visible. If false, the method returns true if and
9627          *                          only if the child is only partially visible (that is it will
9628          *                          return false if the child is either completely visible or out
9629          *                          of RV's bounds).
9630          * @param acceptEndPointInclusion If the view's endpoint intersection with RV's start of end
9631          *                                bounds is enough to consider it partially visible,
9632          *                                false otherwise.
9633          * @return True if the given child is partially or fully visible, false otherwise.
9634          */
isViewPartiallyVisible(@onNull View child, boolean completelyVisible, boolean acceptEndPointInclusion)9635         public boolean isViewPartiallyVisible(@NonNull View child, boolean completelyVisible,
9636                 boolean acceptEndPointInclusion) {
9637             int boundsFlag = (ViewBoundsCheck.FLAG_CVS_GT_PVS | ViewBoundsCheck.FLAG_CVS_EQ_PVS
9638                     | ViewBoundsCheck.FLAG_CVE_LT_PVE | ViewBoundsCheck.FLAG_CVE_EQ_PVE);
9639             boolean isViewFullyVisible = mHorizontalBoundCheck.isViewWithinBoundFlags(child,
9640                     boundsFlag)
9641                     && mVerticalBoundCheck.isViewWithinBoundFlags(child, boundsFlag);
9642             if (completelyVisible) {
9643                 return isViewFullyVisible;
9644             } else {
9645                 return !isViewFullyVisible;
9646             }
9647         }
9648 
9649         /**
9650          * Returns whether the currently focused child stays within RV's bounds with the given
9651          * amount of scrolling.
9652          * @param parent The parent RecyclerView.
9653          * @param dx The scrolling in x-axis direction to be performed.
9654          * @param dy The scrolling in y-axis direction to be performed.
9655          * @return {@code false} if the focused child is not at least partially visible after
9656          *         scrolling or no focused child exists, {@code true} otherwise.
9657          */
isFocusedChildVisibleAfterScrolling(RecyclerView parent, int dx, int dy)9658         private boolean isFocusedChildVisibleAfterScrolling(RecyclerView parent, int dx, int dy) {
9659             final View focusedChild = parent.getFocusedChild();
9660             if (focusedChild == null) {
9661                 return false;
9662             }
9663             final int parentLeft = getPaddingLeft();
9664             final int parentTop = getPaddingTop();
9665             final int parentRight = getWidth() - getPaddingRight();
9666             final int parentBottom = getHeight() - getPaddingBottom();
9667             final Rect bounds = mRecyclerView.mTempRect;
9668             getDecoratedBoundsWithMargins(focusedChild, bounds);
9669 
9670             if (bounds.left - dx >= parentRight || bounds.right - dx <= parentLeft
9671                     || bounds.top - dy >= parentBottom || bounds.bottom - dy <= parentTop) {
9672                 return false;
9673             }
9674             return true;
9675         }
9676 
9677         /**
9678          * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
9679          */
9680         @Deprecated
onRequestChildFocus(@onNull RecyclerView parent, @NonNull View child, @Nullable View focused)9681         public boolean onRequestChildFocus(@NonNull RecyclerView parent, @NonNull View child,
9682                 @Nullable View focused) {
9683             // eat the request if we are in the middle of a scroll or layout
9684             return isSmoothScrolling() || parent.isComputingLayout();
9685         }
9686 
9687         /**
9688          * Called when a descendant view of the RecyclerView requests focus.
9689          *
9690          * <p>A LayoutManager wishing to keep focused views aligned in a specific
9691          * portion of the view may implement that behavior in an override of this method.</p>
9692          *
9693          * <p>If the LayoutManager executes different behavior that should override the default
9694          * behavior of scrolling the focused child on screen instead of running alongside it,
9695          * this method should return true.</p>
9696          *
9697          * @param parent  The RecyclerView hosting this LayoutManager
9698          * @param state   Current state of RecyclerView
9699          * @param child   Direct child of the RecyclerView containing the newly focused view
9700          * @param focused The newly focused view. This may be the same view as child or it may be
9701          *                null
9702          * @return true if the default scroll behavior should be suppressed
9703          */
onRequestChildFocus(@onNull RecyclerView parent, @NonNull State state, @NonNull View child, @Nullable View focused)9704         public boolean onRequestChildFocus(@NonNull RecyclerView parent, @NonNull State state,
9705                 @NonNull View child, @Nullable View focused) {
9706             return onRequestChildFocus(parent, child, focused);
9707         }
9708 
9709         /**
9710          * Called if the RecyclerView this LayoutManager is bound to has a different adapter set via
9711          * {@link RecyclerView#setAdapter(Adapter)} or
9712          * {@link RecyclerView#swapAdapter(Adapter, boolean)}. The LayoutManager may use this
9713          * opportunity to clear caches and configure state such that it can relayout appropriately
9714          * with the new data and potentially new view types.
9715          *
9716          * <p>The default implementation removes all currently attached views.</p>
9717          *
9718          * @param oldAdapter The previous adapter instance. Will be null if there was previously no
9719          *                   adapter.
9720          * @param newAdapter The new adapter instance. Might be null if
9721          *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
9722          */
onAdapterChanged(@ullable Adapter oldAdapter, @Nullable Adapter newAdapter)9723         public void onAdapterChanged(@Nullable Adapter oldAdapter, @Nullable Adapter newAdapter) {
9724         }
9725 
9726         /**
9727          * Called to populate focusable views within the RecyclerView.
9728          *
9729          * <p>The LayoutManager implementation should return <code>true</code> if the default
9730          * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
9731          * suppressed.</p>
9732          *
9733          * <p>The default implementation returns <code>false</code> to trigger RecyclerView
9734          * to fall back to the default ViewGroup behavior.</p>
9735          *
9736          * @param recyclerView The RecyclerView hosting this LayoutManager
9737          * @param views List of output views. This method should add valid focusable views
9738          *              to this list.
9739          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9740          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9741          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9742          * @param focusableMode The type of focusables to be added.
9743          *
9744          * @return true to suppress the default behavior, false to add default focusables after
9745          *         this method returns.
9746          *
9747          * @see #FOCUSABLES_ALL
9748          * @see #FOCUSABLES_TOUCH_MODE
9749          */
onAddFocusables(@onNull RecyclerView recyclerView, @NonNull ArrayList<View> views, int direction, int focusableMode)9750         public boolean onAddFocusables(@NonNull RecyclerView recyclerView,
9751                 @NonNull ArrayList<View> views, int direction, int focusableMode) {
9752             return false;
9753         }
9754 
9755         /**
9756          * Called in response to a call to {@link Adapter#notifyDataSetChanged()} or
9757          * {@link RecyclerView#swapAdapter(Adapter, boolean)} ()} and signals that the the entire
9758          * data set has changed.
9759          *
9760          * @param recyclerView
9761          */
onItemsChanged(@onNull RecyclerView recyclerView)9762         public void onItemsChanged(@NonNull RecyclerView recyclerView) {
9763         }
9764 
9765         /**
9766          * Called when items have been added to the adapter. The LayoutManager may choose to
9767          * requestLayout if the inserted items would require refreshing the currently visible set
9768          * of child views. (e.g. currently empty space would be filled by appended items, etc.)
9769          *
9770          * @param recyclerView
9771          * @param positionStart
9772          * @param itemCount
9773          */
onItemsAdded(@onNull RecyclerView recyclerView, int positionStart, int itemCount)9774         public void onItemsAdded(@NonNull RecyclerView recyclerView, int positionStart,
9775                 int itemCount) {
9776         }
9777 
9778         /**
9779          * Called when items have been removed from the adapter.
9780          *
9781          * @param recyclerView
9782          * @param positionStart
9783          * @param itemCount
9784          */
onItemsRemoved(@onNull RecyclerView recyclerView, int positionStart, int itemCount)9785         public void onItemsRemoved(@NonNull RecyclerView recyclerView, int positionStart,
9786                 int itemCount) {
9787         }
9788 
9789         /**
9790          * Called when items have been changed in the adapter.
9791          * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
9792          * instead, then this callback will not be invoked.
9793          *
9794          * @param recyclerView
9795          * @param positionStart
9796          * @param itemCount
9797          */
onItemsUpdated(@onNull RecyclerView recyclerView, int positionStart, int itemCount)9798         public void onItemsUpdated(@NonNull RecyclerView recyclerView, int positionStart,
9799                 int itemCount) {
9800         }
9801 
9802         /**
9803          * Called when items have been changed in the adapter and with optional payload.
9804          * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
9805          *
9806          * @param recyclerView
9807          * @param positionStart
9808          * @param itemCount
9809          * @param payload
9810          */
onItemsUpdated(@onNull RecyclerView recyclerView, int positionStart, int itemCount, @Nullable Object payload)9811         public void onItemsUpdated(@NonNull RecyclerView recyclerView, int positionStart,
9812                 int itemCount, @Nullable Object payload) {
9813             onItemsUpdated(recyclerView, positionStart, itemCount);
9814         }
9815 
9816         /**
9817          * Called when an item is moved withing the adapter.
9818          * <p>
9819          * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
9820          * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
9821          * is called.
9822          *
9823          * @param recyclerView
9824          * @param from
9825          * @param to
9826          * @param itemCount
9827          */
onItemsMoved(@onNull RecyclerView recyclerView, int from, int to, int itemCount)9828         public void onItemsMoved(@NonNull RecyclerView recyclerView, int from, int to,
9829                 int itemCount) {
9830 
9831         }
9832 
9833 
9834         /**
9835          * <p>Override this method if you want to support scroll bars.</p>
9836          *
9837          * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
9838          *
9839          * <p>Default implementation returns 0.</p>
9840          *
9841          * @param state Current state of RecyclerView
9842          * @return The horizontal extent of the scrollbar's thumb
9843          * @see RecyclerView#computeHorizontalScrollExtent()
9844          */
computeHorizontalScrollExtent(@onNull State state)9845         public int computeHorizontalScrollExtent(@NonNull State state) {
9846             return 0;
9847         }
9848 
9849         /**
9850          * <p>Override this method if you want to support scroll bars.</p>
9851          *
9852          * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
9853          *
9854          * <p>Default implementation returns 0.</p>
9855          *
9856          * @param state Current State of RecyclerView where you can find total item count
9857          * @return The horizontal offset of the scrollbar's thumb
9858          * @see RecyclerView#computeHorizontalScrollOffset()
9859          */
computeHorizontalScrollOffset(@onNull State state)9860         public int computeHorizontalScrollOffset(@NonNull State state) {
9861             return 0;
9862         }
9863 
9864         /**
9865          * <p>Override this method if you want to support scroll bars.</p>
9866          *
9867          * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
9868          *
9869          * <p>Default implementation returns 0.</p>
9870          *
9871          * @param state Current State of RecyclerView where you can find total item count
9872          * @return The total horizontal range represented by the vertical scrollbar
9873          * @see RecyclerView#computeHorizontalScrollRange()
9874          */
computeHorizontalScrollRange(@onNull State state)9875         public int computeHorizontalScrollRange(@NonNull State state) {
9876             return 0;
9877         }
9878 
9879         /**
9880          * <p>Override this method if you want to support scroll bars.</p>
9881          *
9882          * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
9883          *
9884          * <p>Default implementation returns 0.</p>
9885          *
9886          * @param state Current state of RecyclerView
9887          * @return The vertical extent of the scrollbar's thumb
9888          * @see RecyclerView#computeVerticalScrollExtent()
9889          */
computeVerticalScrollExtent(@onNull State state)9890         public int computeVerticalScrollExtent(@NonNull State state) {
9891             return 0;
9892         }
9893 
9894         /**
9895          * <p>Override this method if you want to support scroll bars.</p>
9896          *
9897          * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
9898          *
9899          * <p>Default implementation returns 0.</p>
9900          *
9901          * @param state Current State of RecyclerView where you can find total item count
9902          * @return The vertical offset of the scrollbar's thumb
9903          * @see RecyclerView#computeVerticalScrollOffset()
9904          */
computeVerticalScrollOffset(@onNull State state)9905         public int computeVerticalScrollOffset(@NonNull State state) {
9906             return 0;
9907         }
9908 
9909         /**
9910          * <p>Override this method if you want to support scroll bars.</p>
9911          *
9912          * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
9913          *
9914          * <p>Default implementation returns 0.</p>
9915          *
9916          * @param state Current State of RecyclerView where you can find total item count
9917          * @return The total vertical range represented by the vertical scrollbar
9918          * @see RecyclerView#computeVerticalScrollRange()
9919          */
computeVerticalScrollRange(@onNull State state)9920         public int computeVerticalScrollRange(@NonNull State state) {
9921             return 0;
9922         }
9923 
9924         /**
9925          * Measure the attached RecyclerView. Implementations must call
9926          * {@link #setMeasuredDimension(int, int)} before returning.
9927          * <p>
9928          * It is strongly advised to use the AutoMeasure mechanism by overriding
9929          * {@link #isAutoMeasureEnabled()} to return true as AutoMeasure handles all the standard
9930          * measure cases including when the RecyclerView's layout_width or layout_height have been
9931          * set to wrap_content.  If {@link #isAutoMeasureEnabled()} is overridden to return true,
9932          * this method should not be overridden.
9933          * <p>
9934          * The default implementation will handle EXACTLY measurements and respect
9935          * the minimum width and height properties of the host RecyclerView if measured
9936          * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
9937          * will consume all available space.
9938          *
9939          * @param recycler Recycler
9940          * @param state Transient state of RecyclerView
9941          * @param widthSpec Width {@link android.view.View.MeasureSpec}
9942          * @param heightSpec Height {@link android.view.View.MeasureSpec}
9943          *
9944          * @see #isAutoMeasureEnabled()
9945          * @see #setMeasuredDimension(int, int)
9946          */
onMeasure(@onNull Recycler recycler, @NonNull State state, int widthSpec, int heightSpec)9947         public void onMeasure(@NonNull Recycler recycler, @NonNull State state, int widthSpec,
9948                 int heightSpec) {
9949             mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
9950         }
9951 
9952         /**
9953          * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
9954          * host RecyclerView.
9955          *
9956          * @param widthSize Measured width
9957          * @param heightSize Measured height
9958          */
setMeasuredDimension(int widthSize, int heightSize)9959         public void setMeasuredDimension(int widthSize, int heightSize) {
9960             mRecyclerView.setMeasuredDimension(widthSize, heightSize);
9961         }
9962 
9963         /**
9964          * @return The host RecyclerView's {@link View#getMinimumWidth()}
9965          */
9966         @Px
getMinimumWidth()9967         public int getMinimumWidth() {
9968             return ViewCompat.getMinimumWidth(mRecyclerView);
9969         }
9970 
9971         /**
9972          * @return The host RecyclerView's {@link View#getMinimumHeight()}
9973          */
9974         @Px
getMinimumHeight()9975         public int getMinimumHeight() {
9976             return ViewCompat.getMinimumHeight(mRecyclerView);
9977         }
9978         /**
9979          * <p>Called when the LayoutManager should save its state. This is a good time to save your
9980          * scroll position, configuration and anything else that may be required to restore the same
9981          * layout state if the LayoutManager is recreated.</p>
9982          * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
9983          * restore. This will let you share information between your LayoutManagers but it is also
9984          * your responsibility to make sure they use the same parcelable class.</p>
9985          *
9986          * @return Necessary information for LayoutManager to be able to restore its state
9987          */
9988         @Nullable
onSaveInstanceState()9989         public Parcelable onSaveInstanceState() {
9990             return null;
9991         }
9992 
9993 
onRestoreInstanceState(Parcelable state)9994         public void onRestoreInstanceState(Parcelable state) {
9995 
9996         }
9997 
stopSmoothScroller()9998         void stopSmoothScroller() {
9999             if (mSmoothScroller != null) {
10000                 mSmoothScroller.stop();
10001             }
10002         }
10003 
onSmoothScrollerStopped(SmoothScroller smoothScroller)10004         private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
10005             if (mSmoothScroller == smoothScroller) {
10006                 mSmoothScroller = null;
10007             }
10008         }
10009 
10010         /**
10011          * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
10012          *
10013          * @param state The new scroll state for RecyclerView
10014          */
onScrollStateChanged(int state)10015         public void onScrollStateChanged(int state) {
10016         }
10017 
10018         /**
10019          * Removes all views and recycles them using the given recycler.
10020          * <p>
10021          * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
10022          * <p>
10023          * If a View is marked as "ignored", it is not removed nor recycled.
10024          *
10025          * @param recycler Recycler to use to recycle children
10026          * @see #removeAndRecycleView(View, Recycler)
10027          * @see #removeAndRecycleViewAt(int, Recycler)
10028          * @see #ignoreView(View)
10029          */
removeAndRecycleAllViews(@onNull Recycler recycler)10030         public void removeAndRecycleAllViews(@NonNull Recycler recycler) {
10031             for (int i = getChildCount() - 1; i >= 0; i--) {
10032                 final View view = getChildAt(i);
10033                 if (!getChildViewHolderInt(view).shouldIgnore()) {
10034                     removeAndRecycleViewAt(i, recycler);
10035                 }
10036             }
10037         }
10038 
10039         // called by accessibility delegate
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info)10040         void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
10041             onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
10042         }
10043 
10044         /**
10045          * Called by the AccessibilityDelegate when the information about the current layout should
10046          * be populated.
10047          * <p>
10048          * Default implementation adds a {@link
10049          * androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}.
10050          * <p>
10051          * You should override
10052          * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
10053          * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
10054          * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
10055          * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
10056          * more accurate accessibility information.
10057          *
10058          * @param recycler The Recycler that can be used to convert view positions into adapter
10059          *                 positions
10060          * @param state    The current state of RecyclerView
10061          * @param info     The info that should be filled by the LayoutManager
10062          * @see View#onInitializeAccessibilityNodeInfo(
10063          *android.view.accessibility.AccessibilityNodeInfo)
10064          * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
10065          * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
10066          * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
10067          * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
10068          */
onInitializeAccessibilityNodeInfo(@onNull Recycler recycler, @NonNull State state, @NonNull AccessibilityNodeInfoCompat info)10069         public void onInitializeAccessibilityNodeInfo(@NonNull Recycler recycler,
10070                 @NonNull State state, @NonNull AccessibilityNodeInfoCompat info) {
10071             if (mRecyclerView.canScrollVertically(-1) || mRecyclerView.canScrollHorizontally(-1)) {
10072                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
10073                 info.setScrollable(true);
10074             }
10075             if (mRecyclerView.canScrollVertically(1) || mRecyclerView.canScrollHorizontally(1)) {
10076                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
10077                 info.setScrollable(true);
10078             }
10079             final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo =
10080                     AccessibilityNodeInfoCompat.CollectionInfoCompat
10081                             .obtain(getRowCountForAccessibility(recycler, state),
10082                                     getColumnCountForAccessibility(recycler, state),
10083                                     isLayoutHierarchical(recycler, state),
10084                                     getSelectionModeForAccessibility(recycler, state));
10085             info.setCollectionInfo(collectionInfo);
10086         }
10087 
10088         // called by accessibility delegate
onInitializeAccessibilityEvent(@onNull AccessibilityEvent event)10089         public void onInitializeAccessibilityEvent(@NonNull AccessibilityEvent event) {
10090             onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
10091         }
10092 
10093         /**
10094          * Called by the accessibility delegate to initialize an accessibility event.
10095          * <p>
10096          * Default implementation adds item count and scroll information to the event.
10097          *
10098          * @param recycler The Recycler that can be used to convert view positions into adapter
10099          *                 positions
10100          * @param state    The current state of RecyclerView
10101          * @param event    The event instance to initialize
10102          * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
10103          */
onInitializeAccessibilityEvent(@onNull Recycler recycler, @NonNull State state, @NonNull AccessibilityEvent event)10104         public void onInitializeAccessibilityEvent(@NonNull Recycler recycler, @NonNull State state,
10105                 @NonNull AccessibilityEvent event) {
10106             if (mRecyclerView == null || event == null) {
10107                 return;
10108             }
10109             event.setScrollable(mRecyclerView.canScrollVertically(1)
10110                     || mRecyclerView.canScrollVertically(-1)
10111                     || mRecyclerView.canScrollHorizontally(-1)
10112                     || mRecyclerView.canScrollHorizontally(1));
10113 
10114             if (mRecyclerView.mAdapter != null) {
10115                 event.setItemCount(mRecyclerView.mAdapter.getItemCount());
10116             }
10117         }
10118 
10119         // called by accessibility delegate
onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info)10120         void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) {
10121             final ViewHolder vh = getChildViewHolderInt(host);
10122             // avoid trying to create accessibility node info for removed children
10123             if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
10124                 onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
10125                         mRecyclerView.mState, host, info);
10126             }
10127         }
10128 
10129         /**
10130          * Called by the AccessibilityDelegate when the accessibility information for a specific
10131          * item should be populated.
10132          * <p>
10133          * Default implementation adds basic positioning information about the item.
10134          *
10135          * @param recycler The Recycler that can be used to convert view positions into adapter
10136          *                 positions
10137          * @param state    The current state of RecyclerView
10138          * @param host     The child for which accessibility node info should be populated
10139          * @param info     The info to fill out about the item
10140          * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
10141          * android.view.accessibility.AccessibilityNodeInfo)
10142          */
onInitializeAccessibilityNodeInfoForItem(@onNull Recycler recycler, @NonNull State state, @NonNull View host, @NonNull AccessibilityNodeInfoCompat info)10143         public void onInitializeAccessibilityNodeInfoForItem(@NonNull Recycler recycler,
10144                 @NonNull State state, @NonNull View host,
10145                 @NonNull AccessibilityNodeInfoCompat info) {
10146             int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
10147             int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
10148             final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo =
10149                     AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
10150                             columnIndexGuess, 1, false, false);
10151             info.setCollectionItemInfo(itemInfo);
10152         }
10153 
10154         /**
10155          * A LayoutManager can call this method to force RecyclerView to run simple animations in
10156          * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
10157          * change).
10158          * <p>
10159          * Note that, calling this method will not guarantee that RecyclerView will run animations
10160          * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
10161          * not run any animations but will still clear this flag after the layout is complete.
10162          *
10163          */
requestSimpleAnimationsInNextLayout()10164         public void requestSimpleAnimationsInNextLayout() {
10165             mRequestedSimpleAnimations = true;
10166         }
10167 
10168         /**
10169          * Returns the selection mode for accessibility. Should be
10170          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE},
10171          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or
10172          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}.
10173          * <p>
10174          * Default implementation returns
10175          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
10176          *
10177          * @param recycler The Recycler that can be used to convert view positions into adapter
10178          *                 positions
10179          * @param state    The current state of RecyclerView
10180          * @return Selection mode for accessibility. Default implementation returns
10181          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
10182          */
getSelectionModeForAccessibility(@onNull Recycler recycler, @NonNull State state)10183         public int getSelectionModeForAccessibility(@NonNull Recycler recycler,
10184                 @NonNull State state) {
10185             return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
10186         }
10187 
10188         /**
10189          * Returns the number of rows for accessibility.
10190          * <p>
10191          * Default implementation returns the number of items in the adapter if LayoutManager
10192          * supports vertical scrolling or 1 if LayoutManager does not support vertical
10193          * scrolling.
10194          *
10195          * @param recycler The Recycler that can be used to convert view positions into adapter
10196          *                 positions
10197          * @param state    The current state of RecyclerView
10198          * @return The number of rows in LayoutManager for accessibility.
10199          */
getRowCountForAccessibility(@onNull Recycler recycler, @NonNull State state)10200         public int getRowCountForAccessibility(@NonNull Recycler recycler, @NonNull State state) {
10201             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
10202                 return 1;
10203             }
10204             return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
10205         }
10206 
10207         /**
10208          * Returns the number of columns for accessibility.
10209          * <p>
10210          * Default implementation returns the number of items in the adapter if LayoutManager
10211          * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
10212          * scrolling.
10213          *
10214          * @param recycler The Recycler that can be used to convert view positions into adapter
10215          *                 positions
10216          * @param state    The current state of RecyclerView
10217          * @return The number of rows in LayoutManager for accessibility.
10218          */
getColumnCountForAccessibility(@onNull Recycler recycler, @NonNull State state)10219         public int getColumnCountForAccessibility(@NonNull Recycler recycler,
10220                 @NonNull State state) {
10221             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
10222                 return 1;
10223             }
10224             return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
10225         }
10226 
10227         /**
10228          * Returns whether layout is hierarchical or not to be used for accessibility.
10229          * <p>
10230          * Default implementation returns false.
10231          *
10232          * @param recycler The Recycler that can be used to convert view positions into adapter
10233          *                 positions
10234          * @param state    The current state of RecyclerView
10235          * @return True if layout is hierarchical.
10236          */
isLayoutHierarchical(@onNull Recycler recycler, @NonNull State state)10237         public boolean isLayoutHierarchical(@NonNull Recycler recycler, @NonNull State state) {
10238             return false;
10239         }
10240 
10241         // called by accessibility delegate
performAccessibilityAction(int action, @Nullable Bundle args)10242         boolean performAccessibilityAction(int action, @Nullable Bundle args) {
10243             return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
10244                     action, args);
10245         }
10246 
10247         /**
10248          * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
10249          *
10250          * @param recycler  The Recycler that can be used to convert view positions into adapter
10251          *                  positions
10252          * @param state     The current state of RecyclerView
10253          * @param action    The action to perform
10254          * @param args      Optional action arguments
10255          * @see View#performAccessibilityAction(int, android.os.Bundle)
10256          */
performAccessibilityAction(@onNull Recycler recycler, @NonNull State state, int action, @Nullable Bundle args)10257         public boolean performAccessibilityAction(@NonNull Recycler recycler, @NonNull State state,
10258                 int action, @Nullable Bundle args) {
10259             if (mRecyclerView == null) {
10260                 return false;
10261             }
10262             int vScroll = 0, hScroll = 0;
10263             switch (action) {
10264                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
10265                     if (mRecyclerView.canScrollVertically(-1)) {
10266                         vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
10267                     }
10268                     if (mRecyclerView.canScrollHorizontally(-1)) {
10269                         hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
10270                     }
10271                     break;
10272                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
10273                     if (mRecyclerView.canScrollVertically(1)) {
10274                         vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
10275                     }
10276                     if (mRecyclerView.canScrollHorizontally(1)) {
10277                         hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
10278                     }
10279                     break;
10280             }
10281             if (vScroll == 0 && hScroll == 0) {
10282                 return false;
10283             }
10284             mRecyclerView.smoothScrollBy(hScroll, vScroll);
10285             return true;
10286         }
10287 
10288         // called by accessibility delegate
performAccessibilityActionForItem(@onNull View view, int action, @Nullable Bundle args)10289         boolean performAccessibilityActionForItem(@NonNull View view, int action,
10290                 @Nullable Bundle args) {
10291             return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
10292                     view, action, args);
10293         }
10294 
10295         /**
10296          * Called by AccessibilityDelegate when an accessibility action is requested on one of the
10297          * children of LayoutManager.
10298          * <p>
10299          * Default implementation does not do anything.
10300          *
10301          * @param recycler The Recycler that can be used to convert view positions into adapter
10302          *                 positions
10303          * @param state    The current state of RecyclerView
10304          * @param view     The child view on which the action is performed
10305          * @param action   The action to perform
10306          * @param args     Optional action arguments
10307          * @return true if action is handled
10308          * @see View#performAccessibilityAction(int, android.os.Bundle)
10309          */
performAccessibilityActionForItem(@onNull Recycler recycler, @NonNull State state, @NonNull View view, int action, @Nullable Bundle args)10310         public boolean performAccessibilityActionForItem(@NonNull Recycler recycler,
10311                 @NonNull State state, @NonNull View view, int action, @Nullable Bundle args) {
10312             return false;
10313         }
10314 
10315         /**
10316          * Parse the xml attributes to get the most common properties used by layout managers.
10317          *
10318          * @attr ref androidx.recyclerview.R.styleable#RecyclerView_android_orientation
10319          * @attr ref androidx.recyclerview.R.styleable#RecyclerView_spanCount
10320          * @attr ref androidx.recyclerview.R.styleable#RecyclerView_reverseLayout
10321          * @attr ref androidx.recyclerview.R.styleable#RecyclerView_stackFromEnd
10322          *
10323          * @return an object containing the properties as specified in the attrs.
10324          */
getProperties(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)10325         public static Properties getProperties(@NonNull Context context,
10326                 @Nullable AttributeSet attrs,
10327                 int defStyleAttr, int defStyleRes) {
10328             Properties properties = new Properties();
10329             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
10330                     defStyleAttr, defStyleRes);
10331             properties.orientation = a.getInt(R.styleable.RecyclerView_android_orientation,
10332                     DEFAULT_ORIENTATION);
10333             properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
10334             properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
10335             properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
10336             a.recycle();
10337             return properties;
10338         }
10339 
setExactMeasureSpecsFrom(RecyclerView recyclerView)10340         void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
10341             setMeasureSpecs(
10342                     MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
10343                     MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
10344             );
10345         }
10346 
10347         /**
10348          * Internal API to allow LayoutManagers to be measured twice.
10349          * <p>
10350          * This is not public because LayoutManagers should be able to handle their layouts in one
10351          * pass but it is very convenient to make existing LayoutManagers support wrapping content
10352          * when both orientations are undefined.
10353          * <p>
10354          * This API will be removed after default LayoutManagers properly implement wrap content in
10355          * non-scroll orientation.
10356          */
shouldMeasureTwice()10357         boolean shouldMeasureTwice() {
10358             return false;
10359         }
10360 
hasFlexibleChildInBothOrientations()10361         boolean hasFlexibleChildInBothOrientations() {
10362             final int childCount = getChildCount();
10363             for (int i = 0; i < childCount; i++) {
10364                 final View child = getChildAt(i);
10365                 final ViewGroup.LayoutParams lp = child.getLayoutParams();
10366                 if (lp.width < 0 && lp.height < 0) {
10367                     return true;
10368                 }
10369             }
10370             return false;
10371         }
10372 
10373         /**
10374          * Some general properties that a LayoutManager may want to use.
10375          */
10376         public static class Properties {
10377             /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_android_orientation */
10378             public int orientation;
10379             /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_spanCount */
10380             public int spanCount;
10381             /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_reverseLayout */
10382             public boolean reverseLayout;
10383             /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_stackFromEnd */
10384             public boolean stackFromEnd;
10385         }
10386     }
10387 
10388     /**
10389      * An ItemDecoration allows the application to add a special drawing and layout offset
10390      * to specific item views from the adapter's data set. This can be useful for drawing dividers
10391      * between items, highlights, visual grouping boundaries and more.
10392      *
10393      * <p>All ItemDecorations are drawn in the order they were added, before the item
10394      * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
10395      * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
10396      * RecyclerView.State)}.</p>
10397      */
10398     public abstract static class ItemDecoration {
10399         /**
10400          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
10401          * Any content drawn by this method will be drawn before the item views are drawn,
10402          * and will thus appear underneath the views.
10403          *
10404          * @param c Canvas to draw into
10405          * @param parent RecyclerView this ItemDecoration is drawing into
10406          * @param state The current state of RecyclerView
10407          */
onDraw(@onNull Canvas c, @NonNull RecyclerView parent, @NonNull State state)10408         public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull State state) {
10409             onDraw(c, parent);
10410         }
10411 
10412         /**
10413          * @deprecated
10414          * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
10415          */
10416         @Deprecated
onDraw(@onNull Canvas c, @NonNull RecyclerView parent)10417         public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent) {
10418         }
10419 
10420         /**
10421          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
10422          * Any content drawn by this method will be drawn after the item views are drawn
10423          * and will thus appear over the views.
10424          *
10425          * @param c Canvas to draw into
10426          * @param parent RecyclerView this ItemDecoration is drawing into
10427          * @param state The current state of RecyclerView.
10428          */
onDrawOver(@onNull Canvas c, @NonNull RecyclerView parent, @NonNull State state)10429         public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent,
10430                 @NonNull State state) {
10431             onDrawOver(c, parent);
10432         }
10433 
10434         /**
10435          * @deprecated
10436          * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
10437          */
10438         @Deprecated
onDrawOver(@onNull Canvas c, @NonNull RecyclerView parent)10439         public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent) {
10440         }
10441 
10442 
10443         /**
10444          * @deprecated
10445          * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
10446          */
10447         @Deprecated
getItemOffsets(@onNull Rect outRect, int itemPosition, @NonNull RecyclerView parent)10448         public void getItemOffsets(@NonNull Rect outRect, int itemPosition,
10449                 @NonNull RecyclerView parent) {
10450             outRect.set(0, 0, 0, 0);
10451         }
10452 
10453         /**
10454          * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
10455          * the number of pixels that the item view should be inset by, similar to padding or margin.
10456          * The default implementation sets the bounds of outRect to 0 and returns.
10457          *
10458          * <p>
10459          * If this ItemDecoration does not affect the positioning of item views, it should set
10460          * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
10461          * before returning.
10462          *
10463          * <p>
10464          * If you need to access Adapter for additional data, you can call
10465          * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
10466          * View.
10467          *
10468          * @param outRect Rect to receive the output.
10469          * @param view    The child view to decorate
10470          * @param parent  RecyclerView this ItemDecoration is decorating
10471          * @param state   The current state of RecyclerView.
10472          */
getItemOffsets(@onNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull State state)10473         public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
10474                 @NonNull RecyclerView parent, @NonNull State state) {
10475             getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
10476                     parent);
10477         }
10478     }
10479 
10480     /**
10481      * An OnItemTouchListener allows the application to intercept touch events in progress at the
10482      * view hierarchy level of the RecyclerView before those touch events are considered for
10483      * RecyclerView's own scrolling behavior.
10484      *
10485      * <p>This can be useful for applications that wish to implement various forms of gestural
10486      * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
10487      * a touch interaction already in progress even if the RecyclerView is already handling that
10488      * gesture stream itself for the purposes of scrolling.</p>
10489      *
10490      * @see SimpleOnItemTouchListener
10491      */
10492     public interface OnItemTouchListener {
10493         /**
10494          * Silently observe and/or take over touch events sent to the RecyclerView
10495          * before they are handled by either the RecyclerView itself or its child views.
10496          *
10497          * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
10498          * in the order in which each listener was added, before any other touch processing
10499          * by the RecyclerView itself or child views occurs.</p>
10500          *
10501          * @param e MotionEvent describing the touch event. All coordinates are in
10502          *          the RecyclerView's coordinate system.
10503          * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
10504          *         to continue with the current behavior and continue observing future events in
10505          *         the gesture.
10506          */
onInterceptTouchEvent(@onNull RecyclerView rv, @NonNull MotionEvent e)10507         boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
10508 
10509         /**
10510          * Process a touch event as part of a gesture that was claimed by returning true from
10511          * a previous call to {@link #onInterceptTouchEvent}.
10512          *
10513          * @param e MotionEvent describing the touch event. All coordinates are in
10514          *          the RecyclerView's coordinate system.
10515          */
onTouchEvent(@onNull RecyclerView rv, @NonNull MotionEvent e)10516         void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
10517 
10518         /**
10519          * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
10520          * intercept touch events with
10521          * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
10522          *
10523          * @param disallowIntercept True if the child does not want the parent to
10524          *            intercept touch events.
10525          * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
10526          */
onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)10527         void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
10528     }
10529 
10530     /**
10531      * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies
10532      * and default return values.
10533      * <p>
10534      * You may prefer to extend this class if you don't need to override all methods. Another
10535      * benefit of using this class is future compatibility. As the interface may change, we'll
10536      * always provide a default implementation on this class so that your code won't break when
10537      * you update to a new version of the support library.
10538      */
10539     public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
10540         @Override
onInterceptTouchEvent(@onNull RecyclerView rv, @NonNull MotionEvent e)10541         public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
10542             return false;
10543         }
10544 
10545         @Override
onTouchEvent(@onNull RecyclerView rv, @NonNull MotionEvent e)10546         public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
10547         }
10548 
10549         @Override
onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)10550         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
10551         }
10552     }
10553 
10554 
10555     /**
10556      * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
10557      * has occurred on that RecyclerView.
10558      * <p>
10559      * @see RecyclerView#addOnScrollListener(OnScrollListener)
10560      * @see RecyclerView#clearOnChildAttachStateChangeListeners()
10561      *
10562      */
10563     public abstract static class OnScrollListener {
10564         /**
10565          * Callback method to be invoked when RecyclerView's scroll state changes.
10566          *
10567          * @param recyclerView The RecyclerView whose scroll state has changed.
10568          * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
10569          *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
10570          */
onScrollStateChanged(@onNull RecyclerView recyclerView, int newState)10571         public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState){}
10572 
10573         /**
10574          * Callback method to be invoked when the RecyclerView has been scrolled. This will be
10575          * called after the scroll has completed.
10576          * <p>
10577          * This callback will also be called if visible item range changes after a layout
10578          * calculation. In that case, dx and dy will be 0.
10579          *
10580          * @param recyclerView The RecyclerView which scrolled.
10581          * @param dx The amount of horizontal scroll.
10582          * @param dy The amount of vertical scroll.
10583          */
onScrolled(@onNull RecyclerView recyclerView, int dx, int dy)10584         public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){}
10585     }
10586 
10587     /**
10588      * A RecyclerListener can be set on a RecyclerView to receive messages whenever
10589      * a view is recycled.
10590      *
10591      * @see RecyclerView#setRecyclerListener(RecyclerListener)
10592      */
10593     public interface RecyclerListener {
10594 
10595         /**
10596          * This method is called whenever the view in the ViewHolder is recycled.
10597          *
10598          * RecyclerView calls this method right before clearing ViewHolder's internal data and
10599          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
10600          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
10601          * its adapter position.
10602          *
10603          * @param holder The ViewHolder containing the view that was recycled
10604          */
onViewRecycled(@onNull ViewHolder holder)10605         void onViewRecycled(@NonNull ViewHolder holder);
10606     }
10607 
10608     /**
10609      * A Listener interface that can be attached to a RecylcerView to get notified
10610      * whenever a ViewHolder is attached to or detached from RecyclerView.
10611      */
10612     public interface OnChildAttachStateChangeListener {
10613 
10614         /**
10615          * Called when a view is attached to the RecyclerView.
10616          *
10617          * @param view The View which is attached to the RecyclerView
10618          */
onChildViewAttachedToWindow(@onNull View view)10619         void onChildViewAttachedToWindow(@NonNull View view);
10620 
10621         /**
10622          * Called when a view is detached from RecyclerView.
10623          *
10624          * @param view The View which is being detached from the RecyclerView
10625          */
onChildViewDetachedFromWindow(@onNull View view)10626         void onChildViewDetachedFromWindow(@NonNull View view);
10627     }
10628 
10629     /**
10630      * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
10631      *
10632      * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
10633      * potentially expensive {@link View#findViewById(int)} results.</p>
10634      *
10635      * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
10636      * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
10637      * their own custom ViewHolder implementations to store data that makes binding view contents
10638      * easier. Implementations should assume that individual item views will hold strong references
10639      * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
10640      * strong references to extra off-screen item views for caching purposes</p>
10641      */
10642     public abstract static class ViewHolder {
10643         @NonNull
10644         public final View itemView;
10645         WeakReference<RecyclerView> mNestedRecyclerView;
10646         int mPosition = NO_POSITION;
10647         int mOldPosition = NO_POSITION;
10648         long mItemId = NO_ID;
10649         int mItemViewType = INVALID_TYPE;
10650         int mPreLayoutPosition = NO_POSITION;
10651 
10652         // The item that this holder is shadowing during an item change event/animation
10653         ViewHolder mShadowedHolder = null;
10654         // The item that is shadowing this holder during an item change event/animation
10655         ViewHolder mShadowingHolder = null;
10656 
10657         /**
10658          * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
10659          * are all valid.
10660          */
10661         static final int FLAG_BOUND = 1 << 0;
10662 
10663         /**
10664          * The data this ViewHolder's view reflects is stale and needs to be rebound
10665          * by the adapter. mPosition and mItemId are consistent.
10666          */
10667         static final int FLAG_UPDATE = 1 << 1;
10668 
10669         /**
10670          * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
10671          * are not to be trusted and may no longer match the item view type.
10672          * This ViewHolder must be fully rebound to different data.
10673          */
10674         static final int FLAG_INVALID = 1 << 2;
10675 
10676         /**
10677          * This ViewHolder points at data that represents an item previously removed from the
10678          * data set. Its view may still be used for things like outgoing animations.
10679          */
10680         static final int FLAG_REMOVED = 1 << 3;
10681 
10682         /**
10683          * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
10684          * and is intended to keep views around during animations.
10685          */
10686         static final int FLAG_NOT_RECYCLABLE = 1 << 4;
10687 
10688         /**
10689          * This ViewHolder is returned from scrap which means we are expecting an addView call
10690          * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
10691          * the end of the layout pass and then recycled by RecyclerView if it is not added back to
10692          * the RecyclerView.
10693          */
10694         static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
10695 
10696         /**
10697          * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
10698          * it unless LayoutManager is replaced.
10699          * It is still fully visible to the LayoutManager.
10700          */
10701         static final int FLAG_IGNORE = 1 << 7;
10702 
10703         /**
10704          * When the View is detached form the parent, we set this flag so that we can take correct
10705          * action when we need to remove it or add it back.
10706          */
10707         static final int FLAG_TMP_DETACHED = 1 << 8;
10708 
10709         /**
10710          * Set when we can no longer determine the adapter position of this ViewHolder until it is
10711          * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
10712          * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
10713          * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
10714          * re-calculated.
10715          */
10716         static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
10717 
10718         /**
10719          * Set when a addChangePayload(null) is called
10720          */
10721         static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
10722 
10723         /**
10724          * Used by ItemAnimator when a ViewHolder's position changes
10725          */
10726         static final int FLAG_MOVED = 1 << 11;
10727 
10728         /**
10729          * Used by ItemAnimator when a ViewHolder appears in pre-layout
10730          */
10731         static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
10732 
10733         static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
10734 
10735         /**
10736          * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
10737          * hidden list (as if it was scrap) without being recycled in between.
10738          *
10739          * When a ViewHolder is hidden, there are 2 paths it can be re-used:
10740          *   a) Animation ends, view is recycled and used from the recycle pool.
10741          *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
10742          *
10743          * This flag is used to represent "case b" where the ViewHolder is reused without being
10744          * recycled (thus "bounced" from the hidden list). This state requires special handling
10745          * because the ViewHolder must be added to pre layout maps for animations as if it was
10746          * already there.
10747          */
10748         static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
10749 
10750         /**
10751          * Flags that RecyclerView assigned {@link RecyclerViewAccessibilityDelegate
10752          * #getItemDelegate()} in onBindView when app does not provide a delegate.
10753          */
10754         static final int FLAG_SET_A11Y_ITEM_DELEGATE = 1 << 14;
10755 
10756         private int mFlags;
10757 
10758         private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
10759 
10760         List<Object> mPayloads = null;
10761         List<Object> mUnmodifiedPayloads = null;
10762 
10763         private int mIsRecyclableCount = 0;
10764 
10765         // If non-null, view is currently considered scrap and may be reused for other data by the
10766         // scrap container.
10767         private Recycler mScrapContainer = null;
10768         // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
10769         private boolean mInChangeScrap = false;
10770 
10771         // Saves isImportantForAccessibility value for the view item while it's in hidden state and
10772         // marked as unimportant for accessibility.
10773         private int mWasImportantForAccessibilityBeforeHidden =
10774                 ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10775         // set if we defer the accessibility state change of the view holder
10776         @VisibleForTesting
10777         int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
10778 
10779         /**
10780          * Is set when VH is bound from the adapter and cleaned right before it is sent to
10781          * {@link RecycledViewPool}.
10782          */
10783         RecyclerView mOwnerRecyclerView;
10784 
ViewHolder(@onNull View itemView)10785         public ViewHolder(@NonNull View itemView) {
10786             if (itemView == null) {
10787                 throw new IllegalArgumentException("itemView may not be null");
10788             }
10789             this.itemView = itemView;
10790         }
10791 
flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout)10792         void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
10793             addFlags(ViewHolder.FLAG_REMOVED);
10794             offsetPosition(offset, applyToPreLayout);
10795             mPosition = mNewPosition;
10796         }
10797 
offsetPosition(int offset, boolean applyToPreLayout)10798         void offsetPosition(int offset, boolean applyToPreLayout) {
10799             if (mOldPosition == NO_POSITION) {
10800                 mOldPosition = mPosition;
10801             }
10802             if (mPreLayoutPosition == NO_POSITION) {
10803                 mPreLayoutPosition = mPosition;
10804             }
10805             if (applyToPreLayout) {
10806                 mPreLayoutPosition += offset;
10807             }
10808             mPosition += offset;
10809             if (itemView.getLayoutParams() != null) {
10810                 ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
10811             }
10812         }
10813 
clearOldPosition()10814         void clearOldPosition() {
10815             mOldPosition = NO_POSITION;
10816             mPreLayoutPosition = NO_POSITION;
10817         }
10818 
saveOldPosition()10819         void saveOldPosition() {
10820             if (mOldPosition == NO_POSITION) {
10821                 mOldPosition = mPosition;
10822             }
10823         }
10824 
shouldIgnore()10825         boolean shouldIgnore() {
10826             return (mFlags & FLAG_IGNORE) != 0;
10827         }
10828 
10829         /**
10830          * @deprecated This method is deprecated because its meaning is ambiguous due to the async
10831          * handling of adapter updates. You should use {@link #getLayoutPosition()} or
10832          * {@link #getAdapterPosition()} depending on your use case.
10833          *
10834          * @see #getLayoutPosition()
10835          * @see #getAdapterPosition()
10836          */
10837         @Deprecated
getPosition()10838         public final int getPosition() {
10839             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10840         }
10841 
10842         /**
10843          * Returns the position of the ViewHolder in terms of the latest layout pass.
10844          * <p>
10845          * This position is mostly used by RecyclerView components to be consistent while
10846          * RecyclerView lazily processes adapter updates.
10847          * <p>
10848          * For performance and animation reasons, RecyclerView batches all adapter updates until the
10849          * next layout pass. This may cause mismatches between the Adapter position of the item and
10850          * the position it had in the latest layout calculations.
10851          * <p>
10852          * LayoutManagers should always call this method while doing calculations based on item
10853          * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
10854          * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
10855          * of the item.
10856          * <p>
10857          * If LayoutManager needs to call an external method that requires the adapter position of
10858          * the item, it can use {@link #getAdapterPosition()} or
10859          * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
10860          *
10861          * @return Returns the adapter position of the ViewHolder in the latest layout pass.
10862          * @see #getAdapterPosition()
10863          */
getLayoutPosition()10864         public final int getLayoutPosition() {
10865             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10866         }
10867 
10868         /**
10869          * Returns the Adapter position of the item represented by this ViewHolder.
10870          * <p>
10871          * Note that this might be different than the {@link #getLayoutPosition()} if there are
10872          * pending adapter updates but a new layout pass has not happened yet.
10873          * <p>
10874          * RecyclerView does not handle any adapter updates until the next layout traversal. This
10875          * may create temporary inconsistencies between what user sees on the screen and what
10876          * adapter contents have. This inconsistency is not important since it will be less than
10877          * 16ms but it might be a problem if you want to use ViewHolder position to access the
10878          * adapter. Sometimes, you may need to get the exact adapter position to do
10879          * some actions in response to user events. In that case, you should use this method which
10880          * will calculate the Adapter position of the ViewHolder.
10881          * <p>
10882          * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
10883          * next layout pass, the return value of this method will be {@link #NO_POSITION}.
10884          *
10885          * @return The adapter position of the item if it still exists in the adapter.
10886          * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
10887          * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
10888          * layout pass or the ViewHolder has already been recycled.
10889          */
getAdapterPosition()10890         public final int getAdapterPosition() {
10891             if (mOwnerRecyclerView == null) {
10892                 return NO_POSITION;
10893             }
10894             return mOwnerRecyclerView.getAdapterPositionFor(this);
10895         }
10896 
10897         /**
10898          * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
10899          * to perform animations.
10900          * <p>
10901          * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
10902          * adapter index in the previous layout.
10903          *
10904          * @return The previous adapter index of the Item represented by this ViewHolder or
10905          * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
10906          * complete).
10907          */
getOldPosition()10908         public final int getOldPosition() {
10909             return mOldPosition;
10910         }
10911 
10912         /**
10913          * Returns The itemId represented by this ViewHolder.
10914          *
10915          * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
10916          * otherwise
10917          */
getItemId()10918         public final long getItemId() {
10919             return mItemId;
10920         }
10921 
10922         /**
10923          * @return The view type of this ViewHolder.
10924          */
getItemViewType()10925         public final int getItemViewType() {
10926             return mItemViewType;
10927         }
10928 
isScrap()10929         boolean isScrap() {
10930             return mScrapContainer != null;
10931         }
10932 
unScrap()10933         void unScrap() {
10934             mScrapContainer.unscrapView(this);
10935         }
10936 
wasReturnedFromScrap()10937         boolean wasReturnedFromScrap() {
10938             return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
10939         }
10940 
clearReturnedFromScrapFlag()10941         void clearReturnedFromScrapFlag() {
10942             mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
10943         }
10944 
clearTmpDetachFlag()10945         void clearTmpDetachFlag() {
10946             mFlags = mFlags & ~FLAG_TMP_DETACHED;
10947         }
10948 
stopIgnoring()10949         void stopIgnoring() {
10950             mFlags = mFlags & ~FLAG_IGNORE;
10951         }
10952 
setScrapContainer(Recycler recycler, boolean isChangeScrap)10953         void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
10954             mScrapContainer = recycler;
10955             mInChangeScrap = isChangeScrap;
10956         }
10957 
isInvalid()10958         boolean isInvalid() {
10959             return (mFlags & FLAG_INVALID) != 0;
10960         }
10961 
needsUpdate()10962         boolean needsUpdate() {
10963             return (mFlags & FLAG_UPDATE) != 0;
10964         }
10965 
isBound()10966         boolean isBound() {
10967             return (mFlags & FLAG_BOUND) != 0;
10968         }
10969 
isRemoved()10970         boolean isRemoved() {
10971             return (mFlags & FLAG_REMOVED) != 0;
10972         }
10973 
hasAnyOfTheFlags(int flags)10974         boolean hasAnyOfTheFlags(int flags) {
10975             return (mFlags & flags) != 0;
10976         }
10977 
isTmpDetached()10978         boolean isTmpDetached() {
10979             return (mFlags & FLAG_TMP_DETACHED) != 0;
10980         }
10981 
isAdapterPositionUnknown()10982         boolean isAdapterPositionUnknown() {
10983             return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
10984         }
10985 
setFlags(int flags, int mask)10986         void setFlags(int flags, int mask) {
10987             mFlags = (mFlags & ~mask) | (flags & mask);
10988         }
10989 
addFlags(int flags)10990         void addFlags(int flags) {
10991             mFlags |= flags;
10992         }
10993 
addChangePayload(Object payload)10994         void addChangePayload(Object payload) {
10995             if (payload == null) {
10996                 addFlags(FLAG_ADAPTER_FULLUPDATE);
10997             } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10998                 createPayloadsIfNeeded();
10999                 mPayloads.add(payload);
11000             }
11001         }
11002 
createPayloadsIfNeeded()11003         private void createPayloadsIfNeeded() {
11004             if (mPayloads == null) {
11005                 mPayloads = new ArrayList<Object>();
11006                 mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
11007             }
11008         }
11009 
clearPayload()11010         void clearPayload() {
11011             if (mPayloads != null) {
11012                 mPayloads.clear();
11013             }
11014             mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
11015         }
11016 
getUnmodifiedPayloads()11017         List<Object> getUnmodifiedPayloads() {
11018             if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
11019                 if (mPayloads == null || mPayloads.size() == 0) {
11020                     // Initial state,  no update being called.
11021                     return FULLUPDATE_PAYLOADS;
11022                 }
11023                 // there are none-null payloads
11024                 return mUnmodifiedPayloads;
11025             } else {
11026                 // a full update has been called.
11027                 return FULLUPDATE_PAYLOADS;
11028             }
11029         }
11030 
resetInternal()11031         void resetInternal() {
11032             mFlags = 0;
11033             mPosition = NO_POSITION;
11034             mOldPosition = NO_POSITION;
11035             mItemId = NO_ID;
11036             mPreLayoutPosition = NO_POSITION;
11037             mIsRecyclableCount = 0;
11038             mShadowedHolder = null;
11039             mShadowingHolder = null;
11040             clearPayload();
11041             mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
11042             mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
11043             clearNestedRecyclerViewIfNotNested(this);
11044         }
11045 
11046         /**
11047          * Called when the child view enters the hidden state
11048          */
onEnteredHiddenState(RecyclerView parent)11049         private void onEnteredHiddenState(RecyclerView parent) {
11050             // While the view item is in hidden state, make it invisible for the accessibility.
11051             if (mPendingAccessibilityState != PENDING_ACCESSIBILITY_STATE_NOT_SET) {
11052                 mWasImportantForAccessibilityBeforeHidden = mPendingAccessibilityState;
11053             } else {
11054                 mWasImportantForAccessibilityBeforeHidden =
11055                         ViewCompat.getImportantForAccessibility(itemView);
11056             }
11057             parent.setChildImportantForAccessibilityInternal(this,
11058                     ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
11059         }
11060 
11061         /**
11062          * Called when the child view leaves the hidden state
11063          */
onLeftHiddenState(RecyclerView parent)11064         private void onLeftHiddenState(RecyclerView parent) {
11065             parent.setChildImportantForAccessibilityInternal(this,
11066                     mWasImportantForAccessibilityBeforeHidden);
11067             mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
11068         }
11069 
11070         @Override
toString()11071         public String toString() {
11072             final StringBuilder sb = new StringBuilder("ViewHolder{"
11073                     + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
11074                     + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
11075             if (isScrap()) {
11076                 sb.append(" scrap ")
11077                         .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
11078             }
11079             if (isInvalid()) sb.append(" invalid");
11080             if (!isBound()) sb.append(" unbound");
11081             if (needsUpdate()) sb.append(" update");
11082             if (isRemoved()) sb.append(" removed");
11083             if (shouldIgnore()) sb.append(" ignored");
11084             if (isTmpDetached()) sb.append(" tmpDetached");
11085             if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
11086             if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
11087 
11088             if (itemView.getParent() == null) sb.append(" no parent");
11089             sb.append("}");
11090             return sb.toString();
11091         }
11092 
11093         /**
11094          * Informs the recycler whether this item can be recycled. Views which are not
11095          * recyclable will not be reused for other items until setIsRecyclable() is
11096          * later set to true. Calls to setIsRecyclable() should always be paired (one
11097          * call to setIsRecyclabe(false) should always be matched with a later call to
11098          * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
11099          * reference-counted.
11100          *
11101          * @param recyclable Whether this item is available to be recycled. Default value
11102          * is true.
11103          *
11104          * @see #isRecyclable()
11105          */
setIsRecyclable(boolean recyclable)11106         public final void setIsRecyclable(boolean recyclable) {
11107             mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
11108             if (mIsRecyclableCount < 0) {
11109                 mIsRecyclableCount = 0;
11110                 if (DEBUG) {
11111                     throw new RuntimeException("isRecyclable decremented below 0: "
11112                             + "unmatched pair of setIsRecyable() calls for " + this);
11113                 }
11114                 Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: "
11115                         + "unmatched pair of setIsRecyable() calls for " + this);
11116             } else if (!recyclable && mIsRecyclableCount == 1) {
11117                 mFlags |= FLAG_NOT_RECYCLABLE;
11118             } else if (recyclable && mIsRecyclableCount == 0) {
11119                 mFlags &= ~FLAG_NOT_RECYCLABLE;
11120             }
11121             if (DEBUG) {
11122                 Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
11123             }
11124         }
11125 
11126         /**
11127          * @return true if this item is available to be recycled, false otherwise.
11128          *
11129          * @see #setIsRecyclable(boolean)
11130          */
isRecyclable()11131         public final boolean isRecyclable() {
11132             return (mFlags & FLAG_NOT_RECYCLABLE) == 0
11133                     && !ViewCompat.hasTransientState(itemView);
11134         }
11135 
11136         /**
11137          * Returns whether we have animations referring to this view holder or not.
11138          * This is similar to isRecyclable flag but does not check transient state.
11139          */
shouldBeKeptAsChild()11140         private boolean shouldBeKeptAsChild() {
11141             return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
11142         }
11143 
11144         /**
11145          * @return True if ViewHolder is not referenced by RecyclerView animations but has
11146          * transient state which will prevent it from being recycled.
11147          */
doesTransientStatePreventRecycling()11148         private boolean doesTransientStatePreventRecycling() {
11149             return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView);
11150         }
11151 
isUpdated()11152         boolean isUpdated() {
11153             return (mFlags & FLAG_UPDATE) != 0;
11154         }
11155     }
11156 
11157     /**
11158      * This method is here so that we can control the important for a11y changes and test it.
11159      */
11160     @VisibleForTesting
setChildImportantForAccessibilityInternal(ViewHolder viewHolder, int importantForAccessibility)11161     boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
11162             int importantForAccessibility) {
11163         if (isComputingLayout()) {
11164             viewHolder.mPendingAccessibilityState = importantForAccessibility;
11165             mPendingAccessibilityImportanceChange.add(viewHolder);
11166             return false;
11167         }
11168         ViewCompat.setImportantForAccessibility(viewHolder.itemView, importantForAccessibility);
11169         return true;
11170     }
11171 
dispatchPendingImportantForAccessibilityChanges()11172     void dispatchPendingImportantForAccessibilityChanges() {
11173         for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
11174             ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
11175             if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
11176                 continue;
11177             }
11178             int state = viewHolder.mPendingAccessibilityState;
11179             if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
11180                 //noinspection WrongConstant
11181                 ViewCompat.setImportantForAccessibility(viewHolder.itemView, state);
11182                 viewHolder.mPendingAccessibilityState =
11183                         ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
11184             }
11185         }
11186         mPendingAccessibilityImportanceChange.clear();
11187     }
11188 
getAdapterPositionFor(ViewHolder viewHolder)11189     int getAdapterPositionFor(ViewHolder viewHolder) {
11190         if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
11191                 | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
11192                 || !viewHolder.isBound()) {
11193             return RecyclerView.NO_POSITION;
11194         }
11195         return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
11196     }
11197 
11198     @VisibleForTesting
initFastScroller(StateListDrawable verticalThumbDrawable, Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable, Drawable horizontalTrackDrawable)11199     void initFastScroller(StateListDrawable verticalThumbDrawable,
11200             Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable,
11201             Drawable horizontalTrackDrawable) {
11202         if (verticalThumbDrawable == null || verticalTrackDrawable == null
11203                 || horizontalThumbDrawable == null || horizontalTrackDrawable == null) {
11204             throw new IllegalArgumentException(
11205                 "Trying to set fast scroller without both required drawables." + exceptionLabel());
11206         }
11207 
11208         Resources resources = getContext().getResources();
11209         new FastScroller(this, verticalThumbDrawable, verticalTrackDrawable,
11210                 horizontalThumbDrawable, horizontalTrackDrawable,
11211                 resources.getDimensionPixelSize(R.dimen.fastscroll_default_thickness),
11212                 resources.getDimensionPixelSize(R.dimen.fastscroll_minimum_range),
11213                 resources.getDimensionPixelOffset(R.dimen.fastscroll_margin));
11214     }
11215 
11216     // NestedScrollingChild
11217 
11218     @Override
setNestedScrollingEnabled(boolean enabled)11219     public void setNestedScrollingEnabled(boolean enabled) {
11220         getScrollingChildHelper().setNestedScrollingEnabled(enabled);
11221     }
11222 
11223     @Override
isNestedScrollingEnabled()11224     public boolean isNestedScrollingEnabled() {
11225         return getScrollingChildHelper().isNestedScrollingEnabled();
11226     }
11227 
11228     @Override
startNestedScroll(int axes)11229     public boolean startNestedScroll(int axes) {
11230         return getScrollingChildHelper().startNestedScroll(axes);
11231     }
11232 
11233     @Override
startNestedScroll(int axes, int type)11234     public boolean startNestedScroll(int axes, int type) {
11235         return getScrollingChildHelper().startNestedScroll(axes, type);
11236     }
11237 
11238     @Override
stopNestedScroll()11239     public void stopNestedScroll() {
11240         getScrollingChildHelper().stopNestedScroll();
11241     }
11242 
11243     @Override
stopNestedScroll(int type)11244     public void stopNestedScroll(int type) {
11245         getScrollingChildHelper().stopNestedScroll(type);
11246     }
11247 
11248     @Override
hasNestedScrollingParent()11249     public boolean hasNestedScrollingParent() {
11250         return getScrollingChildHelper().hasNestedScrollingParent();
11251     }
11252 
11253     @Override
hasNestedScrollingParent(int type)11254     public boolean hasNestedScrollingParent(int type) {
11255         return getScrollingChildHelper().hasNestedScrollingParent(type);
11256     }
11257 
11258     @Override
dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow)11259     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
11260             int dyUnconsumed, int[] offsetInWindow) {
11261         return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
11262                 dxUnconsumed, dyUnconsumed, offsetInWindow);
11263     }
11264 
11265     @Override
dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow, int type)11266     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
11267             int dyUnconsumed, int[] offsetInWindow, int type) {
11268         return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
11269                 dxUnconsumed, dyUnconsumed, offsetInWindow, type);
11270     }
11271 
11272     @Override
dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow)11273     public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
11274         return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
11275     }
11276 
11277     @Override
dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow, int type)11278     public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
11279             int type) {
11280         return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow,
11281                 type);
11282     }
11283 
11284     @Override
dispatchNestedFling(float velocityX, float velocityY, boolean consumed)11285     public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
11286         return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
11287     }
11288 
11289     @Override
dispatchNestedPreFling(float velocityX, float velocityY)11290     public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
11291         return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
11292     }
11293 
11294     /**
11295      * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
11296      * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
11297      * to create their own subclass of this <code>LayoutParams</code> class
11298      * to store any additional required per-child view metadata about the layout.
11299      */
11300     public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
11301         ViewHolder mViewHolder;
11302         final Rect mDecorInsets = new Rect();
11303         boolean mInsetsDirty = true;
11304         // Flag is set to true if the view is bound while it is detached from RV.
11305         // In this case, we need to manually call invalidate after view is added to guarantee that
11306         // invalidation is populated through the View hierarchy
11307         boolean mPendingInvalidate = false;
11308 
LayoutParams(Context c, AttributeSet attrs)11309         public LayoutParams(Context c, AttributeSet attrs) {
11310             super(c, attrs);
11311         }
11312 
LayoutParams(int width, int height)11313         public LayoutParams(int width, int height) {
11314             super(width, height);
11315         }
11316 
LayoutParams(MarginLayoutParams source)11317         public LayoutParams(MarginLayoutParams source) {
11318             super(source);
11319         }
11320 
LayoutParams(ViewGroup.LayoutParams source)11321         public LayoutParams(ViewGroup.LayoutParams source) {
11322             super(source);
11323         }
11324 
LayoutParams(LayoutParams source)11325         public LayoutParams(LayoutParams source) {
11326             super((ViewGroup.LayoutParams) source);
11327         }
11328 
11329         /**
11330          * Returns true if the view this LayoutParams is attached to needs to have its content
11331          * updated from the corresponding adapter.
11332          *
11333          * @return true if the view should have its content updated
11334          */
viewNeedsUpdate()11335         public boolean viewNeedsUpdate() {
11336             return mViewHolder.needsUpdate();
11337         }
11338 
11339         /**
11340          * Returns true if the view this LayoutParams is attached to is now representing
11341          * potentially invalid data. A LayoutManager should scrap/recycle it.
11342          *
11343          * @return true if the view is invalid
11344          */
isViewInvalid()11345         public boolean isViewInvalid() {
11346             return mViewHolder.isInvalid();
11347         }
11348 
11349         /**
11350          * Returns true if the adapter data item corresponding to the view this LayoutParams
11351          * is attached to has been removed from the data set. A LayoutManager may choose to
11352          * treat it differently in order to animate its outgoing or disappearing state.
11353          *
11354          * @return true if the item the view corresponds to was removed from the data set
11355          */
isItemRemoved()11356         public boolean isItemRemoved() {
11357             return mViewHolder.isRemoved();
11358         }
11359 
11360         /**
11361          * Returns true if the adapter data item corresponding to the view this LayoutParams
11362          * is attached to has been changed in the data set. A LayoutManager may choose to
11363          * treat it differently in order to animate its changing state.
11364          *
11365          * @return true if the item the view corresponds to was changed in the data set
11366          */
isItemChanged()11367         public boolean isItemChanged() {
11368             return mViewHolder.isUpdated();
11369         }
11370 
11371         /**
11372          * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
11373          */
11374         @Deprecated
getViewPosition()11375         public int getViewPosition() {
11376             return mViewHolder.getPosition();
11377         }
11378 
11379         /**
11380          * Returns the adapter position that the view this LayoutParams is attached to corresponds
11381          * to as of latest layout calculation.
11382          *
11383          * @return the adapter position this view as of latest layout pass
11384          */
getViewLayoutPosition()11385         public int getViewLayoutPosition() {
11386             return mViewHolder.getLayoutPosition();
11387         }
11388 
11389         /**
11390          * Returns the up-to-date adapter position that the view this LayoutParams is attached to
11391          * corresponds to.
11392          *
11393          * @return the up-to-date adapter position this view. It may return
11394          * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
11395          * its up-to-date position cannot be calculated.
11396          */
getViewAdapterPosition()11397         public int getViewAdapterPosition() {
11398             return mViewHolder.getAdapterPosition();
11399         }
11400     }
11401 
11402     /**
11403      * Observer base class for watching changes to an {@link Adapter}.
11404      * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
11405      */
11406     public abstract static class AdapterDataObserver {
onChanged()11407         public void onChanged() {
11408             // Do nothing
11409         }
11410 
onItemRangeChanged(int positionStart, int itemCount)11411         public void onItemRangeChanged(int positionStart, int itemCount) {
11412             // do nothing
11413         }
11414 
onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload)11415         public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
11416             // fallback to onItemRangeChanged(positionStart, itemCount) if app
11417             // does not override this method.
11418             onItemRangeChanged(positionStart, itemCount);
11419         }
11420 
onItemRangeInserted(int positionStart, int itemCount)11421         public void onItemRangeInserted(int positionStart, int itemCount) {
11422             // do nothing
11423         }
11424 
onItemRangeRemoved(int positionStart, int itemCount)11425         public void onItemRangeRemoved(int positionStart, int itemCount) {
11426             // do nothing
11427         }
11428 
onItemRangeMoved(int fromPosition, int toPosition, int itemCount)11429         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
11430             // do nothing
11431         }
11432     }
11433 
11434     /**
11435      * Base class for smooth scrolling. Handles basic tracking of the target view position and
11436      * provides methods to trigger a programmatic scroll.
11437      *
11438      * <p>An instance of SmoothScroller is only intended to be used once.  You should create a new
11439      * instance for each call to {@link LayoutManager#startSmoothScroll(SmoothScroller)}.
11440      *
11441      * @see LinearSmoothScroller
11442      */
11443     public abstract static class SmoothScroller {
11444 
11445         private int mTargetPosition = RecyclerView.NO_POSITION;
11446 
11447         private RecyclerView mRecyclerView;
11448 
11449         private LayoutManager mLayoutManager;
11450 
11451         private boolean mPendingInitialRun;
11452 
11453         private boolean mRunning;
11454 
11455         private View mTargetView;
11456 
11457         private final Action mRecyclingAction;
11458 
11459         private boolean mStarted;
11460 
SmoothScroller()11461         public SmoothScroller() {
11462             mRecyclingAction = new Action(0, 0);
11463         }
11464 
11465         /**
11466          * Starts a smooth scroll for the given target position.
11467          * <p>In each animation step, {@link RecyclerView} will check
11468          * for the target view and call either
11469          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
11470          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
11471          * SmoothScroller is stopped.</p>
11472          *
11473          * <p>Note that if RecyclerView finds the target view, it will automatically stop the
11474          * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
11475          * stop calling SmoothScroller in each animation step.</p>
11476          */
start(RecyclerView recyclerView, LayoutManager layoutManager)11477         void start(RecyclerView recyclerView, LayoutManager layoutManager) {
11478             if (mStarted) {
11479                 Log.w(TAG, "An instance of " + this.getClass().getSimpleName() + " was started "
11480                         + "more than once. Each instance of" + this.getClass().getSimpleName() + " "
11481                         + "is intended to only be used once. You should create a new instance for "
11482                         + "each use.");
11483             }
11484 
11485             mRecyclerView = recyclerView;
11486             mLayoutManager = layoutManager;
11487             if (mTargetPosition == RecyclerView.NO_POSITION) {
11488                 throw new IllegalArgumentException("Invalid target position");
11489             }
11490             mRecyclerView.mState.mTargetPosition = mTargetPosition;
11491             mRunning = true;
11492             mPendingInitialRun = true;
11493             mTargetView = findViewByPosition(getTargetPosition());
11494             onStart();
11495             mRecyclerView.mViewFlinger.postOnAnimation();
11496 
11497             mStarted = true;
11498         }
11499 
setTargetPosition(int targetPosition)11500         public void setTargetPosition(int targetPosition) {
11501             mTargetPosition = targetPosition;
11502         }
11503 
11504         /**
11505          * Compute the scroll vector for a given target position.
11506          * <p>
11507          * This method can return null if the layout manager cannot calculate a scroll vector
11508          * for the given position (e.g. it has no current scroll position).
11509          *
11510          * @param targetPosition the position to which the scroller is scrolling
11511          *
11512          * @return the scroll vector for a given target position
11513          */
11514         @Nullable
computeScrollVectorForPosition(int targetPosition)11515         public PointF computeScrollVectorForPosition(int targetPosition) {
11516             LayoutManager layoutManager = getLayoutManager();
11517             if (layoutManager instanceof ScrollVectorProvider) {
11518                 return ((ScrollVectorProvider) layoutManager)
11519                         .computeScrollVectorForPosition(targetPosition);
11520             }
11521             Log.w(TAG, "You should override computeScrollVectorForPosition when the LayoutManager"
11522                     + " does not implement " + ScrollVectorProvider.class.getCanonicalName());
11523             return null;
11524         }
11525 
11526         /**
11527          * @return The LayoutManager to which this SmoothScroller is attached. Will return
11528          * <code>null</code> after the SmoothScroller is stopped.
11529          */
11530         @Nullable
getLayoutManager()11531         public LayoutManager getLayoutManager() {
11532             return mLayoutManager;
11533         }
11534 
11535         /**
11536          * Stops running the SmoothScroller in each animation callback. Note that this does not
11537          * cancel any existing {@link Action} updated by
11538          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
11539          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
11540          */
stop()11541         protected final void stop() {
11542             if (!mRunning) {
11543                 return;
11544             }
11545             mRunning = false;
11546             onStop();
11547             mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
11548             mTargetView = null;
11549             mTargetPosition = RecyclerView.NO_POSITION;
11550             mPendingInitialRun = false;
11551             // trigger a cleanup
11552             mLayoutManager.onSmoothScrollerStopped(this);
11553             // clear references to avoid any potential leak by a custom smooth scroller
11554             mLayoutManager = null;
11555             mRecyclerView = null;
11556         }
11557 
11558         /**
11559          * Returns true if SmoothScroller has been started but has not received the first
11560          * animation
11561          * callback yet.
11562          *
11563          * @return True if this SmoothScroller is waiting to start
11564          */
isPendingInitialRun()11565         public boolean isPendingInitialRun() {
11566             return mPendingInitialRun;
11567         }
11568 
11569 
11570         /**
11571          * @return True if SmoothScroller is currently active
11572          */
isRunning()11573         public boolean isRunning() {
11574             return mRunning;
11575         }
11576 
11577         /**
11578          * Returns the adapter position of the target item
11579          *
11580          * @return Adapter position of the target item or
11581          * {@link RecyclerView#NO_POSITION} if no target view is set.
11582          */
getTargetPosition()11583         public int getTargetPosition() {
11584             return mTargetPosition;
11585         }
11586 
onAnimation(int dx, int dy)11587         private void onAnimation(int dx, int dy) {
11588             // TODO(b/72745539): If mRunning is false, we call stop, which is a no op if mRunning
11589             // is false. Also, if recyclerView is null, we call stop, and stop assumes recyclerView
11590             // is not null (as does the code following this block).  This should be cleaned up.
11591             final RecyclerView recyclerView = mRecyclerView;
11592             if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
11593                 stop();
11594             }
11595 
11596             // The following if block exists to have the LayoutManager scroll 1 pixel in the correct
11597             // direction in order to cause the LayoutManager to draw two pages worth of views so
11598             // that the target view may be found before scrolling any further.  This is done to
11599             // prevent an initial scroll distance from scrolling past the view, which causes a
11600             // jittery looking animation. (This block also necessarily sets mPendingInitialRun to
11601             // false if it was true).
11602             if (mPendingInitialRun && mTargetView == null && mLayoutManager != null) {
11603                 PointF pointF = computeScrollVectorForPosition(mTargetPosition);
11604                 if (pointF != null && (pointF.x != 0 || pointF.y != 0)) {
11605                     recyclerView.scrollStep(
11606                             (int) Math.signum(pointF.x),
11607                             (int) Math.signum(pointF.y),
11608                             null);
11609                 }
11610             }
11611 
11612             mPendingInitialRun = false;
11613 
11614             if (mTargetView != null) {
11615                 // verify target position
11616                 if (getChildPosition(mTargetView) == mTargetPosition) {
11617                     onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
11618                     mRecyclingAction.runIfNecessary(recyclerView);
11619                     stop();
11620                 } else {
11621                     Log.e(TAG, "Passed over target position while smooth scrolling.");
11622                     mTargetView = null;
11623                 }
11624             }
11625             if (mRunning) {
11626                 onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
11627                 boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
11628                 mRecyclingAction.runIfNecessary(recyclerView);
11629                 if (hadJumpTarget) {
11630                     // It is not stopped so needs to be restarted
11631                     if (mRunning) {
11632                         mPendingInitialRun = true;
11633                         recyclerView.mViewFlinger.postOnAnimation();
11634                     } else {
11635                         // TODO(b/72745539): stop() is a no-op if mRunning is false, so this can be
11636                         // removed.
11637                         stop(); // done
11638                     }
11639                 }
11640             }
11641         }
11642 
11643         /**
11644          * @see RecyclerView#getChildLayoutPosition(android.view.View)
11645          */
getChildPosition(View view)11646         public int getChildPosition(View view) {
11647             return mRecyclerView.getChildLayoutPosition(view);
11648         }
11649 
11650         /**
11651          * @see RecyclerView.LayoutManager#getChildCount()
11652          */
getChildCount()11653         public int getChildCount() {
11654             return mRecyclerView.mLayout.getChildCount();
11655         }
11656 
11657         /**
11658          * @see RecyclerView.LayoutManager#findViewByPosition(int)
11659          */
findViewByPosition(int position)11660         public View findViewByPosition(int position) {
11661             return mRecyclerView.mLayout.findViewByPosition(position);
11662         }
11663 
11664         /**
11665          * @see RecyclerView#scrollToPosition(int)
11666          * @deprecated Use {@link Action#jumpTo(int)}.
11667          */
11668         @Deprecated
instantScrollToPosition(int position)11669         public void instantScrollToPosition(int position) {
11670             mRecyclerView.scrollToPosition(position);
11671         }
11672 
onChildAttachedToWindow(View child)11673         protected void onChildAttachedToWindow(View child) {
11674             if (getChildPosition(child) == getTargetPosition()) {
11675                 mTargetView = child;
11676                 if (DEBUG) {
11677                     Log.d(TAG, "smooth scroll target view has been attached");
11678                 }
11679             }
11680         }
11681 
11682         /**
11683          * Normalizes the vector.
11684          * @param scrollVector The vector that points to the target scroll position
11685          */
normalize(@onNull PointF scrollVector)11686         protected void normalize(@NonNull PointF scrollVector) {
11687             final float magnitude = (float) Math.sqrt(scrollVector.x * scrollVector.x
11688                     + scrollVector.y * scrollVector.y);
11689             scrollVector.x /= magnitude;
11690             scrollVector.y /= magnitude;
11691         }
11692 
11693         /**
11694          * Called when smooth scroll is started. This might be a good time to do setup.
11695          */
onStart()11696         protected abstract void onStart();
11697 
11698         /**
11699          * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
11700          * @see #stop()
11701          */
onStop()11702         protected abstract void onStop();
11703 
11704         /**
11705          * <p>RecyclerView will call this method each time it scrolls until it can find the target
11706          * position in the layout.</p>
11707          * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
11708          * provided {@link Action} to define the next scroll.</p>
11709          *
11710          * @param dx        Last scroll amount horizontally
11711          * @param dy        Last scroll amount vertically
11712          * @param state     Transient state of RecyclerView
11713          * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
11714          *                  update this object.
11715          */
onSeekTargetStep(@x int dx, @Px int dy, @NonNull State state, @NonNull Action action)11716         protected abstract void onSeekTargetStep(@Px int dx, @Px int dy, @NonNull State state,
11717                 @NonNull Action action);
11718 
11719         /**
11720          * Called when the target position is laid out. This is the last callback SmoothScroller
11721          * will receive and it should update the provided {@link Action} to define the scroll
11722          * details towards the target view.
11723          * @param targetView    The view element which render the target position.
11724          * @param state         Transient state of RecyclerView
11725          * @param action        Action instance that you should update to define final scroll action
11726          *                      towards the targetView
11727          */
onTargetFound(@onNull View targetView, @NonNull State state, @NonNull Action action)11728         protected abstract void onTargetFound(@NonNull View targetView, @NonNull State state,
11729                 @NonNull Action action);
11730 
11731         /**
11732          * Holds information about a smooth scroll request by a {@link SmoothScroller}.
11733          */
11734         public static class Action {
11735 
11736             public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
11737 
11738             private int mDx;
11739 
11740             private int mDy;
11741 
11742             private int mDuration;
11743 
11744             private int mJumpToPosition = NO_POSITION;
11745 
11746             private Interpolator mInterpolator;
11747 
11748             private boolean mChanged = false;
11749 
11750             // we track this variable to inform custom implementer if they are updating the action
11751             // in every animation callback
11752             private int mConsecutiveUpdates = 0;
11753 
11754             /**
11755              * @param dx Pixels to scroll horizontally
11756              * @param dy Pixels to scroll vertically
11757              */
Action(@x int dx, @Px int dy)11758             public Action(@Px int dx, @Px int dy) {
11759                 this(dx, dy, UNDEFINED_DURATION, null);
11760             }
11761 
11762             /**
11763              * @param dx       Pixels to scroll horizontally
11764              * @param dy       Pixels to scroll vertically
11765              * @param duration Duration of the animation in milliseconds
11766              */
Action(@x int dx, @Px int dy, int duration)11767             public Action(@Px int dx, @Px int dy, int duration) {
11768                 this(dx, dy, duration, null);
11769             }
11770 
11771             /**
11772              * @param dx           Pixels to scroll horizontally
11773              * @param dy           Pixels to scroll vertically
11774              * @param duration     Duration of the animation in milliseconds
11775              * @param interpolator Interpolator to be used when calculating scroll position in each
11776              *                     animation step
11777              */
Action(@x int dx, @Px int dy, int duration, @Nullable Interpolator interpolator)11778             public Action(@Px int dx, @Px int dy, int duration,
11779                     @Nullable Interpolator interpolator) {
11780                 mDx = dx;
11781                 mDy = dy;
11782                 mDuration = duration;
11783                 mInterpolator = interpolator;
11784             }
11785 
11786             /**
11787              * Instead of specifying pixels to scroll, use the target position to jump using
11788              * {@link RecyclerView#scrollToPosition(int)}.
11789              * <p>
11790              * You may prefer using this method if scroll target is really far away and you prefer
11791              * to jump to a location and smooth scroll afterwards.
11792              * <p>
11793              * Note that calling this method takes priority over other update methods such as
11794              * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
11795              * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
11796              * {@link #jumpTo(int)}, the other changes will not be considered for this animation
11797              * frame.
11798              *
11799              * @param targetPosition The target item position to scroll to using instant scrolling.
11800              */
jumpTo(int targetPosition)11801             public void jumpTo(int targetPosition) {
11802                 mJumpToPosition = targetPosition;
11803             }
11804 
hasJumpTarget()11805             boolean hasJumpTarget() {
11806                 return mJumpToPosition >= 0;
11807             }
11808 
runIfNecessary(RecyclerView recyclerView)11809             void runIfNecessary(RecyclerView recyclerView) {
11810                 if (mJumpToPosition >= 0) {
11811                     final int position = mJumpToPosition;
11812                     mJumpToPosition = NO_POSITION;
11813                     recyclerView.jumpToPositionForSmoothScroller(position);
11814                     mChanged = false;
11815                     return;
11816                 }
11817                 if (mChanged) {
11818                     validate();
11819                     if (mInterpolator == null) {
11820                         if (mDuration == UNDEFINED_DURATION) {
11821                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
11822                         } else {
11823                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
11824                         }
11825                     } else {
11826                         recyclerView.mViewFlinger.smoothScrollBy(
11827                                 mDx, mDy, mDuration, mInterpolator);
11828                     }
11829                     mConsecutiveUpdates++;
11830                     if (mConsecutiveUpdates > 10) {
11831                         // A new action is being set in every animation step. This looks like a bad
11832                         // implementation. Inform developer.
11833                         Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
11834                                 + " you are not changing it unless necessary");
11835                     }
11836                     mChanged = false;
11837                 } else {
11838                     mConsecutiveUpdates = 0;
11839                 }
11840             }
11841 
validate()11842             private void validate() {
11843                 if (mInterpolator != null && mDuration < 1) {
11844                     throw new IllegalStateException("If you provide an interpolator, you must"
11845                             + " set a positive duration");
11846                 } else if (mDuration < 1) {
11847                     throw new IllegalStateException("Scroll duration must be a positive number");
11848                 }
11849             }
11850 
11851             @Px
getDx()11852             public int getDx() {
11853                 return mDx;
11854             }
11855 
setDx(@x int dx)11856             public void setDx(@Px int dx) {
11857                 mChanged = true;
11858                 mDx = dx;
11859             }
11860 
11861             @Px
getDy()11862             public int getDy() {
11863                 return mDy;
11864             }
11865 
setDy(@x int dy)11866             public void setDy(@Px int dy) {
11867                 mChanged = true;
11868                 mDy = dy;
11869             }
11870 
getDuration()11871             public int getDuration() {
11872                 return mDuration;
11873             }
11874 
setDuration(int duration)11875             public void setDuration(int duration) {
11876                 mChanged = true;
11877                 mDuration = duration;
11878             }
11879 
11880             @Nullable
getInterpolator()11881             public Interpolator getInterpolator() {
11882                 return mInterpolator;
11883             }
11884 
11885             /**
11886              * Sets the interpolator to calculate scroll steps
11887              * @param interpolator The interpolator to use. If you specify an interpolator, you must
11888              *                     also set the duration.
11889              * @see #setDuration(int)
11890              */
setInterpolator(@ullable Interpolator interpolator)11891             public void setInterpolator(@Nullable Interpolator interpolator) {
11892                 mChanged = true;
11893                 mInterpolator = interpolator;
11894             }
11895 
11896             /**
11897              * Updates the action with given parameters.
11898              * @param dx Pixels to scroll horizontally
11899              * @param dy Pixels to scroll vertically
11900              * @param duration Duration of the animation in milliseconds
11901              * @param interpolator Interpolator to be used when calculating scroll position in each
11902              *                     animation step
11903              */
update(@x int dx, @Px int dy, int duration, @Nullable Interpolator interpolator)11904             public void update(@Px int dx, @Px int dy, int duration,
11905                     @Nullable Interpolator interpolator) {
11906                 mDx = dx;
11907                 mDy = dy;
11908                 mDuration = duration;
11909                 mInterpolator = interpolator;
11910                 mChanged = true;
11911             }
11912         }
11913 
11914         /**
11915          * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
11916          * to provide a hint to a {@link SmoothScroller} about the location of the target position.
11917          */
11918         public interface ScrollVectorProvider {
11919             /**
11920              * Should calculate the vector that points to the direction where the target position
11921              * can be found.
11922              * <p>
11923              * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
11924              * the target position.
11925              * <p>
11926              * The magnitude of the vector is not important. It is always normalized before being
11927              * used by the {@link LinearSmoothScroller}.
11928              * <p>
11929              * LayoutManager should not check whether the position exists in the adapter or not.
11930              *
11931              * @param targetPosition the target position to which the returned vector should point
11932              *
11933              * @return the scroll vector for a given position.
11934              */
11935             @Nullable
computeScrollVectorForPosition(int targetPosition)11936             PointF computeScrollVectorForPosition(int targetPosition);
11937         }
11938     }
11939 
11940     static class AdapterDataObservable extends Observable<AdapterDataObserver> {
hasObservers()11941         public boolean hasObservers() {
11942             return !mObservers.isEmpty();
11943         }
11944 
notifyChanged()11945         public void notifyChanged() {
11946             // since onChanged() is implemented by the app, it could do anything, including
11947             // removing itself from {@link mObservers} - and that could cause problems if
11948             // an iterator is used on the ArrayList {@link mObservers}.
11949             // to avoid such problems, just march thru the list in the reverse order.
11950             for (int i = mObservers.size() - 1; i >= 0; i--) {
11951                 mObservers.get(i).onChanged();
11952             }
11953         }
11954 
notifyItemRangeChanged(int positionStart, int itemCount)11955         public void notifyItemRangeChanged(int positionStart, int itemCount) {
11956             notifyItemRangeChanged(positionStart, itemCount, null);
11957         }
11958 
notifyItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload)11959         public void notifyItemRangeChanged(int positionStart, int itemCount,
11960                 @Nullable Object payload) {
11961             // since onItemRangeChanged() is implemented by the app, it could do anything, including
11962             // removing itself from {@link mObservers} - and that could cause problems if
11963             // an iterator is used on the ArrayList {@link mObservers}.
11964             // to avoid such problems, just march thru the list in the reverse order.
11965             for (int i = mObservers.size() - 1; i >= 0; i--) {
11966                 mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
11967             }
11968         }
11969 
notifyItemRangeInserted(int positionStart, int itemCount)11970         public void notifyItemRangeInserted(int positionStart, int itemCount) {
11971             // since onItemRangeInserted() is implemented by the app, it could do anything,
11972             // including removing itself from {@link mObservers} - and that could cause problems if
11973             // an iterator is used on the ArrayList {@link mObservers}.
11974             // to avoid such problems, just march thru the list in the reverse order.
11975             for (int i = mObservers.size() - 1; i >= 0; i--) {
11976                 mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
11977             }
11978         }
11979 
notifyItemRangeRemoved(int positionStart, int itemCount)11980         public void notifyItemRangeRemoved(int positionStart, int itemCount) {
11981             // since onItemRangeRemoved() is implemented by the app, it could do anything, including
11982             // removing itself from {@link mObservers} - and that could cause problems if
11983             // an iterator is used on the ArrayList {@link mObservers}.
11984             // to avoid such problems, just march thru the list in the reverse order.
11985             for (int i = mObservers.size() - 1; i >= 0; i--) {
11986                 mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
11987             }
11988         }
11989 
notifyItemMoved(int fromPosition, int toPosition)11990         public void notifyItemMoved(int fromPosition, int toPosition) {
11991             for (int i = mObservers.size() - 1; i >= 0; i--) {
11992                 mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
11993             }
11994         }
11995     }
11996 
11997     /**
11998      * This is public so that the CREATOR can be accessed on cold launch.
11999      * @hide
12000      */
12001     @RestrictTo(LIBRARY_GROUP)
12002     public static class SavedState extends AbsSavedState {
12003 
12004         Parcelable mLayoutState;
12005 
12006         /**
12007          * called by CREATOR
12008          */
SavedState(Parcel in, ClassLoader loader)12009         SavedState(Parcel in, ClassLoader loader) {
12010             super(in, loader);
12011             mLayoutState = in.readParcelable(
12012                     loader != null ? loader : LayoutManager.class.getClassLoader());
12013         }
12014 
12015         /**
12016          * Called by onSaveInstanceState
12017          */
SavedState(Parcelable superState)12018         SavedState(Parcelable superState) {
12019             super(superState);
12020         }
12021 
12022         @Override
writeToParcel(Parcel dest, int flags)12023         public void writeToParcel(Parcel dest, int flags) {
12024             super.writeToParcel(dest, flags);
12025             dest.writeParcelable(mLayoutState, 0);
12026         }
12027 
copyFrom(SavedState other)12028         void copyFrom(SavedState other) {
12029             mLayoutState = other.mLayoutState;
12030         }
12031 
12032         public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
12033             @Override
12034             public SavedState createFromParcel(Parcel in, ClassLoader loader) {
12035                 return new SavedState(in, loader);
12036             }
12037 
12038             @Override
12039             public SavedState createFromParcel(Parcel in) {
12040                 return new SavedState(in, null);
12041             }
12042 
12043             @Override
12044             public SavedState[] newArray(int size) {
12045                 return new SavedState[size];
12046             }
12047         };
12048     }
12049 
12050     /**
12051      * <p>Contains useful information about the current RecyclerView state like target scroll
12052      * position or view focus. State object can also keep arbitrary data, identified by resource
12053      * ids.</p>
12054      * <p>Often times, RecyclerView components will need to pass information between each other.
12055      * To provide a well defined data bus between components, RecyclerView passes the same State
12056      * object to component callbacks and these components can use it to exchange data.</p>
12057      * <p>If you implement custom components, you can use State's put/get/remove methods to pass
12058      * data between your components without needing to manage their lifecycles.</p>
12059      */
12060     public static class State {
12061         static final int STEP_START = 1;
12062         static final int STEP_LAYOUT = 1 << 1;
12063         static final int STEP_ANIMATIONS = 1 << 2;
12064 
assertLayoutStep(int accepted)12065         void assertLayoutStep(int accepted) {
12066             if ((accepted & mLayoutStep) == 0) {
12067                 throw new IllegalStateException("Layout state should be one of "
12068                         + Integer.toBinaryString(accepted) + " but it is "
12069                         + Integer.toBinaryString(mLayoutStep));
12070             }
12071         }
12072 
12073 
12074         /** Owned by SmoothScroller */
12075         private int mTargetPosition = RecyclerView.NO_POSITION;
12076 
12077         private SparseArray<Object> mData;
12078 
12079         ////////////////////////////////////////////////////////////////////////////////////////////
12080         // Fields below are carried from one layout pass to the next
12081         ////////////////////////////////////////////////////////////////////////////////////////////
12082 
12083         /**
12084          * Number of items adapter had in the previous layout.
12085          */
12086         int mPreviousLayoutItemCount = 0;
12087 
12088         /**
12089          * Number of items that were NOT laid out but has been deleted from the adapter after the
12090          * previous layout.
12091          */
12092         int mDeletedInvisibleItemCountSincePreviousLayout = 0;
12093 
12094         ////////////////////////////////////////////////////////////////////////////////////////////
12095         // Fields below must be updated or cleared before they are used (generally before a pass)
12096         ////////////////////////////////////////////////////////////////////////////////////////////
12097 
12098         @IntDef(flag = true, value = {
12099                 STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
12100         })
12101         @Retention(RetentionPolicy.SOURCE)
12102         @interface LayoutState {}
12103 
12104         @LayoutState
12105         int mLayoutStep = STEP_START;
12106 
12107         /**
12108          * Number of items adapter has.
12109          */
12110         int mItemCount = 0;
12111 
12112         boolean mStructureChanged = false;
12113 
12114         /**
12115          * True if the associated {@link RecyclerView} is in the pre-layout step where it is having
12116          * its {@link LayoutManager} layout items where they will be at the beginning of a set of
12117          * predictive item animations.
12118          */
12119         boolean mInPreLayout = false;
12120 
12121         boolean mTrackOldChangeHolders = false;
12122 
12123         boolean mIsMeasuring = false;
12124 
12125         ////////////////////////////////////////////////////////////////////////////////////////////
12126         // Fields below are always reset outside of the pass (or passes) that use them
12127         ////////////////////////////////////////////////////////////////////////////////////////////
12128 
12129         boolean mRunSimpleAnimations = false;
12130 
12131         boolean mRunPredictiveAnimations = false;
12132 
12133         /**
12134          * This data is saved before a layout calculation happens. After the layout is finished,
12135          * if the previously focused view has been replaced with another view for the same item, we
12136          * move the focus to the new item automatically.
12137          */
12138         int mFocusedItemPosition;
12139         long mFocusedItemId;
12140         // when a sub child has focus, record its id and see if we can directly request focus on
12141         // that one instead
12142         int mFocusedSubChildId;
12143 
12144         int mRemainingScrollHorizontal;
12145         int mRemainingScrollVertical;
12146 
12147         ////////////////////////////////////////////////////////////////////////////////////////////
12148 
reset()12149         State reset() {
12150             mTargetPosition = RecyclerView.NO_POSITION;
12151             if (mData != null) {
12152                 mData.clear();
12153             }
12154             mItemCount = 0;
12155             mStructureChanged = false;
12156             mIsMeasuring = false;
12157             return this;
12158         }
12159 
12160         /**
12161          * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
12162          * prior to any layout passes.
12163          *
12164          * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
12165          * that Recycler#getViewForPosition() can function safely.</p>
12166          */
prepareForNestedPrefetch(Adapter adapter)12167         void prepareForNestedPrefetch(Adapter adapter) {
12168             mLayoutStep = STEP_START;
12169             mItemCount = adapter.getItemCount();
12170             mInPreLayout = false;
12171             mTrackOldChangeHolders = false;
12172             mIsMeasuring = false;
12173         }
12174 
12175         /**
12176          * Returns true if the RecyclerView is currently measuring the layout. This value is
12177          * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
12178          * has non-exact measurement specs.
12179          * <p>
12180          * Note that if the LayoutManager supports predictive animations and it is calculating the
12181          * pre-layout step, this value will be {@code false} even if the RecyclerView is in
12182          * {@code onMeasure} call. This is because pre-layout means the previous state of the
12183          * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
12184          * LayoutManager is always guaranteed to receive another call to
12185          * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
12186          *
12187          * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
12188          */
isMeasuring()12189         public boolean isMeasuring() {
12190             return mIsMeasuring;
12191         }
12192 
12193         /**
12194          * Returns true if the {@link RecyclerView} is in the pre-layout step where it is having its
12195          * {@link LayoutManager} layout items where they will be at the beginning of a set of
12196          * predictive item animations.
12197          */
isPreLayout()12198         public boolean isPreLayout() {
12199             return mInPreLayout;
12200         }
12201 
12202         /**
12203          * Returns whether RecyclerView will run predictive animations in this layout pass
12204          * or not.
12205          *
12206          * @return true if RecyclerView is calculating predictive animations to be run at the end
12207          *         of the layout pass.
12208          */
willRunPredictiveAnimations()12209         public boolean willRunPredictiveAnimations() {
12210             return mRunPredictiveAnimations;
12211         }
12212 
12213         /**
12214          * Returns whether RecyclerView will run simple animations in this layout pass
12215          * or not.
12216          *
12217          * @return true if RecyclerView is calculating simple animations to be run at the end of
12218          *         the layout pass.
12219          */
willRunSimpleAnimations()12220         public boolean willRunSimpleAnimations() {
12221             return mRunSimpleAnimations;
12222         }
12223 
12224         /**
12225          * Removes the mapping from the specified id, if there was any.
12226          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
12227          *                   preserve cross functionality and avoid conflicts.
12228          */
remove(int resourceId)12229         public void remove(int resourceId) {
12230             if (mData == null) {
12231                 return;
12232             }
12233             mData.remove(resourceId);
12234         }
12235 
12236         /**
12237          * Gets the Object mapped from the specified id, or <code>null</code>
12238          * if no such data exists.
12239          *
12240          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
12241          *                   to
12242          *                   preserve cross functionality and avoid conflicts.
12243          */
12244         @SuppressWarnings("TypeParameterUnusedInFormals")
get(int resourceId)12245         public <T> T get(int resourceId) {
12246             if (mData == null) {
12247                 return null;
12248             }
12249             return (T) mData.get(resourceId);
12250         }
12251 
12252         /**
12253          * Adds a mapping from the specified id to the specified value, replacing the previous
12254          * mapping from the specified key if there was one.
12255          *
12256          * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
12257          *                   preserve cross functionality and avoid conflicts.
12258          * @param data       The data you want to associate with the resourceId.
12259          */
put(int resourceId, Object data)12260         public void put(int resourceId, Object data) {
12261             if (mData == null) {
12262                 mData = new SparseArray<Object>();
12263             }
12264             mData.put(resourceId, data);
12265         }
12266 
12267         /**
12268          * If scroll is triggered to make a certain item visible, this value will return the
12269          * adapter index of that item.
12270          * @return Adapter index of the target item or
12271          * {@link RecyclerView#NO_POSITION} if there is no target
12272          * position.
12273          */
getTargetScrollPosition()12274         public int getTargetScrollPosition() {
12275             return mTargetPosition;
12276         }
12277 
12278         /**
12279          * Returns if current scroll has a target position.
12280          * @return true if scroll is being triggered to make a certain position visible
12281          * @see #getTargetScrollPosition()
12282          */
hasTargetScrollPosition()12283         public boolean hasTargetScrollPosition() {
12284             return mTargetPosition != RecyclerView.NO_POSITION;
12285         }
12286 
12287         /**
12288          * @return true if the structure of the data set has changed since the last call to
12289          *         onLayoutChildren, false otherwise
12290          */
didStructureChange()12291         public boolean didStructureChange() {
12292             return mStructureChanged;
12293         }
12294 
12295         /**
12296          * Returns the total number of items that can be laid out. Note that this number is not
12297          * necessarily equal to the number of items in the adapter, so you should always use this
12298          * number for your position calculations and never access the adapter directly.
12299          * <p>
12300          * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
12301          * data changes on existing Views. These calculations are used to decide which animations
12302          * should be run.
12303          * <p>
12304          * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
12305          * present the correct state to LayoutManager in pre-layout pass.
12306          * <p>
12307          * For example, a newly added item is not included in pre-layout item count because
12308          * pre-layout reflects the contents of the adapter before the item is added. Behind the
12309          * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
12310          * LayoutManager does not know about the new item's existence in pre-layout. The item will
12311          * be available in second layout pass and will be included in the item count. Similar
12312          * adjustments are made for moved and removed items as well.
12313          * <p>
12314          * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
12315          *
12316          * @return The number of items currently available
12317          * @see LayoutManager#getItemCount()
12318          */
getItemCount()12319         public int getItemCount() {
12320             return mInPreLayout
12321                     ? (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout)
12322                     : mItemCount;
12323         }
12324 
12325         /**
12326          * Returns remaining horizontal scroll distance of an ongoing scroll animation(fling/
12327          * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
12328          * other than {@link #SCROLL_STATE_SETTLING}.
12329          *
12330          * @return Remaining horizontal scroll distance
12331          */
getRemainingScrollHorizontal()12332         public int getRemainingScrollHorizontal() {
12333             return mRemainingScrollHorizontal;
12334         }
12335 
12336         /**
12337          * Returns remaining vertical scroll distance of an ongoing scroll animation(fling/
12338          * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
12339          * other than {@link #SCROLL_STATE_SETTLING}.
12340          *
12341          * @return Remaining vertical scroll distance
12342          */
getRemainingScrollVertical()12343         public int getRemainingScrollVertical() {
12344             return mRemainingScrollVertical;
12345         }
12346 
12347         @Override
toString()12348         public String toString() {
12349             return "State{"
12350                     + "mTargetPosition=" + mTargetPosition
12351                     + ", mData=" + mData
12352                     + ", mItemCount=" + mItemCount
12353                     + ", mIsMeasuring=" + mIsMeasuring
12354                     + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
12355                     + ", mDeletedInvisibleItemCountSincePreviousLayout="
12356                     + mDeletedInvisibleItemCountSincePreviousLayout
12357                     + ", mStructureChanged=" + mStructureChanged
12358                     + ", mInPreLayout=" + mInPreLayout
12359                     + ", mRunSimpleAnimations=" + mRunSimpleAnimations
12360                     + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
12361                     + '}';
12362         }
12363     }
12364 
12365     /**
12366      * This class defines the behavior of fling if the developer wishes to handle it.
12367      * <p>
12368      * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
12369      *
12370      * @see #setOnFlingListener(OnFlingListener)
12371      */
12372     public abstract static class OnFlingListener {
12373 
12374         /**
12375          * Override this to handle a fling given the velocities in both x and y directions.
12376          * Note that this method will only be called if the associated {@link LayoutManager}
12377          * supports scrolling and the fling is not handled by nested scrolls first.
12378          *
12379          * @param velocityX the fling velocity on the X axis
12380          * @param velocityY the fling velocity on the Y axis
12381          *
12382          * @return true if the fling was handled, false otherwise.
12383          */
onFling(int velocityX, int velocityY)12384         public abstract boolean onFling(int velocityX, int velocityY);
12385     }
12386 
12387     /**
12388      * Internal listener that manages items after animations finish. This is how items are
12389      * retained (not recycled) during animations, but allowed to be recycled afterwards.
12390      * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
12391      * method on the animator's listener when it is done animating any item.
12392      */
12393     private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
12394 
ItemAnimatorRestoreListener()12395         ItemAnimatorRestoreListener() {
12396         }
12397 
12398         @Override
onAnimationFinished(ViewHolder item)12399         public void onAnimationFinished(ViewHolder item) {
12400             item.setIsRecyclable(true);
12401             if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
12402                 item.mShadowedHolder = null;
12403             }
12404             // always null this because an OldViewHolder can never become NewViewHolder w/o being
12405             // recycled.
12406             item.mShadowingHolder = null;
12407             if (!item.shouldBeKeptAsChild()) {
12408                 if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
12409                     removeDetachedView(item.itemView, false);
12410                 }
12411             }
12412         }
12413     }
12414 
12415     /**
12416      * This class defines the animations that take place on items as changes are made
12417      * to the adapter.
12418      *
12419      * Subclasses of ItemAnimator can be used to implement custom animations for actions on
12420      * ViewHolder items. The RecyclerView will manage retaining these items while they
12421      * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
12422      * when a ViewHolder's animation is finished. In other words, there must be a matching
12423      * {@link #dispatchAnimationFinished(ViewHolder)} call for each
12424      * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
12425      * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12426      * animateChange()}
12427      * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
12428      * and
12429      * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12430      * animateDisappearance()} call.
12431      *
12432      * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
12433      *
12434      * @see #setItemAnimator(ItemAnimator)
12435      */
12436     @SuppressWarnings("UnusedParameters")
12437     public abstract static class ItemAnimator {
12438 
12439         /**
12440          * The Item represented by this ViewHolder is updated.
12441          * <p>
12442          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12443          */
12444         public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
12445 
12446         /**
12447          * The Item represented by this ViewHolder is removed from the adapter.
12448          * <p>
12449          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12450          */
12451         public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
12452 
12453         /**
12454          * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
12455          * represented by this ViewHolder is invalid.
12456          * <p>
12457          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12458          */
12459         public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
12460 
12461         /**
12462          * The position of the Item represented by this ViewHolder has been changed. This flag is
12463          * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
12464          * any adapter change that may have a side effect on this item. (e.g. The item before this
12465          * one has been removed from the Adapter).
12466          * <p>
12467          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12468          */
12469         public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
12470 
12471         /**
12472          * This ViewHolder was not laid out but has been added to the layout in pre-layout state
12473          * by the {@link LayoutManager}. This means that the item was already in the Adapter but
12474          * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
12475          * to add new items in pre-layout to specify their virtual location when they are invisible
12476          * (e.g. to specify the item should <i>animate in</i> from below the visible area).
12477          * <p>
12478          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12479          */
12480         public static final int FLAG_APPEARED_IN_PRE_LAYOUT =
12481                 ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
12482 
12483         /**
12484          * The set of flags that might be passed to
12485          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12486          */
12487         @IntDef(flag = true, value = {
12488                 FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
12489                 FLAG_APPEARED_IN_PRE_LAYOUT
12490         })
12491         @Retention(RetentionPolicy.SOURCE)
12492         public @interface AdapterChanges {}
12493         private ItemAnimatorListener mListener = null;
12494         private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
12495                 new ArrayList<ItemAnimatorFinishedListener>();
12496 
12497         private long mAddDuration = 120;
12498         private long mRemoveDuration = 120;
12499         private long mMoveDuration = 250;
12500         private long mChangeDuration = 250;
12501 
12502         /**
12503          * Gets the current duration for which all move animations will run.
12504          *
12505          * @return The current move duration
12506          */
getMoveDuration()12507         public long getMoveDuration() {
12508             return mMoveDuration;
12509         }
12510 
12511         /**
12512          * Sets the duration for which all move animations will run.
12513          *
12514          * @param moveDuration The move duration
12515          */
setMoveDuration(long moveDuration)12516         public void setMoveDuration(long moveDuration) {
12517             mMoveDuration = moveDuration;
12518         }
12519 
12520         /**
12521          * Gets the current duration for which all add animations will run.
12522          *
12523          * @return The current add duration
12524          */
getAddDuration()12525         public long getAddDuration() {
12526             return mAddDuration;
12527         }
12528 
12529         /**
12530          * Sets the duration for which all add animations will run.
12531          *
12532          * @param addDuration The add duration
12533          */
setAddDuration(long addDuration)12534         public void setAddDuration(long addDuration) {
12535             mAddDuration = addDuration;
12536         }
12537 
12538         /**
12539          * Gets the current duration for which all remove animations will run.
12540          *
12541          * @return The current remove duration
12542          */
getRemoveDuration()12543         public long getRemoveDuration() {
12544             return mRemoveDuration;
12545         }
12546 
12547         /**
12548          * Sets the duration for which all remove animations will run.
12549          *
12550          * @param removeDuration The remove duration
12551          */
setRemoveDuration(long removeDuration)12552         public void setRemoveDuration(long removeDuration) {
12553             mRemoveDuration = removeDuration;
12554         }
12555 
12556         /**
12557          * Gets the current duration for which all change animations will run.
12558          *
12559          * @return The current change duration
12560          */
getChangeDuration()12561         public long getChangeDuration() {
12562             return mChangeDuration;
12563         }
12564 
12565         /**
12566          * Sets the duration for which all change animations will run.
12567          *
12568          * @param changeDuration The change duration
12569          */
setChangeDuration(long changeDuration)12570         public void setChangeDuration(long changeDuration) {
12571             mChangeDuration = changeDuration;
12572         }
12573 
12574         /**
12575          * Internal only:
12576          * Sets the listener that must be called when the animator is finished
12577          * animating the item (or immediately if no animation happens). This is set
12578          * internally and is not intended to be set by external code.
12579          *
12580          * @param listener The listener that must be called.
12581          */
setListener(ItemAnimatorListener listener)12582         void setListener(ItemAnimatorListener listener) {
12583             mListener = listener;
12584         }
12585 
12586         /**
12587          * Called by the RecyclerView before the layout begins. Item animator should record
12588          * necessary information about the View before it is potentially rebound, moved or removed.
12589          * <p>
12590          * The data returned from this method will be passed to the related <code>animate**</code>
12591          * methods.
12592          * <p>
12593          * Note that this method may be called after pre-layout phase if LayoutManager adds new
12594          * Views to the layout in pre-layout pass.
12595          * <p>
12596          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
12597          * the View and the adapter change flags.
12598          *
12599          * @param state       The current State of RecyclerView which includes some useful data
12600          *                    about the layout that will be calculated.
12601          * @param viewHolder  The ViewHolder whose information should be recorded.
12602          * @param changeFlags Additional information about what changes happened in the Adapter
12603          *                    about the Item represented by this ViewHolder. For instance, if
12604          *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
12605          * @param payloads    The payload list that was previously passed to
12606          *                    {@link Adapter#notifyItemChanged(int, Object)} or
12607          *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
12608          *
12609          * @return An ItemHolderInfo instance that preserves necessary information about the
12610          * ViewHolder. This object will be passed back to related <code>animate**</code> methods
12611          * after layout is complete.
12612          *
12613          * @see #recordPostLayoutInformation(State, ViewHolder)
12614          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12615          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12616          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12617          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12618          */
recordPreLayoutInformation(@onNull State state, @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags, @NonNull List<Object> payloads)12619         public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
12620                 @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
12621                 @NonNull List<Object> payloads) {
12622             return obtainHolderInfo().setFrom(viewHolder);
12623         }
12624 
12625         /**
12626          * Called by the RecyclerView after the layout is complete. Item animator should record
12627          * necessary information about the View's final state.
12628          * <p>
12629          * The data returned from this method will be passed to the related <code>animate**</code>
12630          * methods.
12631          * <p>
12632          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
12633          * the View.
12634          *
12635          * @param state      The current State of RecyclerView which includes some useful data about
12636          *                   the layout that will be calculated.
12637          * @param viewHolder The ViewHolder whose information should be recorded.
12638          *
12639          * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
12640          * This object will be passed back to related <code>animate**</code> methods when
12641          * RecyclerView decides how items should be animated.
12642          *
12643          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12644          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12645          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12646          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12647          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12648          */
recordPostLayoutInformation(@onNull State state, @NonNull ViewHolder viewHolder)12649         public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
12650                 @NonNull ViewHolder viewHolder) {
12651             return obtainHolderInfo().setFrom(viewHolder);
12652         }
12653 
12654         /**
12655          * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
12656          * <p>
12657          * This means that the View was a child of the LayoutManager when layout started but has
12658          * been removed by the LayoutManager. It might have been removed from the adapter or simply
12659          * become invisible due to other factors. You can distinguish these two cases by checking
12660          * the change flags that were passed to
12661          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12662          * <p>
12663          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
12664          * animation callback method which will be called by the RecyclerView depends on the
12665          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
12666          * LayoutManager's decision whether to layout the changed version of a disappearing
12667          * ViewHolder or not. RecyclerView will call
12668          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12669          * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
12670          * returns {@code false} from
12671          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
12672          * LayoutManager lays out a new disappearing view that holds the updated information.
12673          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
12674          * <p>
12675          * If LayoutManager supports predictive animations, it might provide a target disappear
12676          * location for the View by laying it out in that location. When that happens,
12677          * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
12678          * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
12679          * <p>
12680          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12681          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12682          * decides not to animate the view).
12683          *
12684          * @param viewHolder    The ViewHolder which should be animated
12685          * @param preLayoutInfo The information that was returned from
12686          *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12687          * @param postLayoutInfo The information that was returned from
12688          *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
12689          *                       null if the LayoutManager did not layout the item.
12690          *
12691          * @return true if a later call to {@link #runPendingAnimations()} is requested,
12692          * false otherwise.
12693          */
animateDisappearance(@onNull ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo)12694         public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
12695                 @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
12696 
12697         /**
12698          * Called by the RecyclerView when a ViewHolder is added to the layout.
12699          * <p>
12700          * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
12701          * but has  been added by the LayoutManager. It might be newly added to the adapter or
12702          * simply become visible due to other factors.
12703          * <p>
12704          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12705          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12706          * decides not to animate the view).
12707          *
12708          * @param viewHolder     The ViewHolder which should be animated
12709          * @param preLayoutInfo  The information that was returned from
12710          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12711          *                       Might be null if Item was just added to the adapter or
12712          *                       LayoutManager does not support predictive animations or it could
12713          *                       not predict that this ViewHolder will become visible.
12714          * @param postLayoutInfo The information that was returned from {@link
12715          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12716          *
12717          * @return true if a later call to {@link #runPendingAnimations()} is requested,
12718          * false otherwise.
12719          */
animateAppearance(@onNull ViewHolder viewHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)12720         public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
12721                 @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12722 
12723         /**
12724          * Called by the RecyclerView when a ViewHolder is present in both before and after the
12725          * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
12726          * for it or a {@link Adapter#notifyDataSetChanged()} call.
12727          * <p>
12728          * This ViewHolder still represents the same data that it was representing when the layout
12729          * started but its position / size may be changed by the LayoutManager.
12730          * <p>
12731          * If the Item's layout position didn't change, RecyclerView still calls this method because
12732          * it does not track this information (or does not necessarily know that an animation is
12733          * not required). Your ItemAnimator should handle this case and if there is nothing to
12734          * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
12735          * <code>false</code>.
12736          * <p>
12737          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12738          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12739          * decides not to animate the view).
12740          *
12741          * @param viewHolder     The ViewHolder which should be animated
12742          * @param preLayoutInfo  The information that was returned from
12743          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12744          * @param postLayoutInfo The information that was returned from {@link
12745          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12746          *
12747          * @return true if a later call to {@link #runPendingAnimations()} is requested,
12748          * false otherwise.
12749          */
animatePersistence(@onNull ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)12750         public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
12751                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12752 
12753         /**
12754          * Called by the RecyclerView when an adapter item is present both before and after the
12755          * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
12756          * for it. This method may also be called when
12757          * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
12758          * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
12759          * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
12760          * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
12761          * called for the new ViewHolder and the old one will be recycled.
12762          * <p>
12763          * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
12764          * a good possibility that item contents didn't really change but it is rebound from the
12765          * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
12766          * screen didn't change and your animator should handle this case as well and avoid creating
12767          * unnecessary animations.
12768          * <p>
12769          * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
12770          * previous presentation of the item as-is and supply a new ViewHolder for the updated
12771          * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
12772          * This is useful if you don't know the contents of the Item and would like
12773          * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
12774          * <p>
12775          * When you are writing a custom item animator for your layout, it might be more performant
12776          * and elegant to re-use the same ViewHolder and animate the content changes manually.
12777          * <p>
12778          * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
12779          * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
12780          * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
12781          * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
12782          * which represent the same Item. In that case, only the new ViewHolder is visible
12783          * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
12784          * <p>
12785          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
12786          * ViewHolder when their animation is complete
12787          * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
12788          * animate the view).
12789          * <p>
12790          *  If oldHolder and newHolder are the same instance, you should call
12791          * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
12792          * <p>
12793          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
12794          * animation callback method which will be called by the RecyclerView depends on the
12795          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
12796          * LayoutManager's decision whether to layout the changed version of a disappearing
12797          * ViewHolder or not. RecyclerView will call
12798          * {@code animateChange} instead of
12799          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12800          * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
12801          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
12802          * LayoutManager lays out a new disappearing view that holds the updated information.
12803          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
12804          *
12805          * @param oldHolder     The ViewHolder before the layout is started, might be the same
12806          *                      instance with newHolder.
12807          * @param newHolder     The ViewHolder after the layout is finished, might be the same
12808          *                      instance with oldHolder.
12809          * @param preLayoutInfo  The information that was returned from
12810          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12811          * @param postLayoutInfo The information that was returned from {@link
12812          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12813          *
12814          * @return true if a later call to {@link #runPendingAnimations()} is requested,
12815          * false otherwise.
12816          */
animateChange(@onNull ViewHolder oldHolder, @NonNull ViewHolder newHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)12817         public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
12818                 @NonNull ViewHolder newHolder,
12819                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12820 
buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder)12821         @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
12822             int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
12823             if (viewHolder.isInvalid()) {
12824                 return FLAG_INVALIDATED;
12825             }
12826             if ((flags & FLAG_INVALIDATED) == 0) {
12827                 final int oldPos = viewHolder.getOldPosition();
12828                 final int pos = viewHolder.getAdapterPosition();
12829                 if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
12830                     flags |= FLAG_MOVED;
12831                 }
12832             }
12833             return flags;
12834         }
12835 
12836         /**
12837          * Called when there are pending animations waiting to be started. This state
12838          * is governed by the return values from
12839          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12840          * animateAppearance()},
12841          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12842          * animateChange()}
12843          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12844          * animatePersistence()}, and
12845          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12846          * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
12847          * called later to start the associated animations. runPendingAnimations() will be scheduled
12848          * to be run on the next frame.
12849          */
runPendingAnimations()12850         public abstract void runPendingAnimations();
12851 
12852         /**
12853          * Method called when an animation on a view should be ended immediately.
12854          * This could happen when other events, like scrolling, occur, so that
12855          * animating views can be quickly put into their proper end locations.
12856          * Implementations should ensure that any animations running on the item
12857          * are canceled and affected properties are set to their end values.
12858          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
12859          * animation since the animations are effectively done when this method is called.
12860          *
12861          * @param item The item for which an animation should be stopped.
12862          */
endAnimation(@onNull ViewHolder item)12863         public abstract void endAnimation(@NonNull ViewHolder item);
12864 
12865         /**
12866          * Method called when all item animations should be ended immediately.
12867          * This could happen when other events, like scrolling, occur, so that
12868          * animating views can be quickly put into their proper end locations.
12869          * Implementations should ensure that any animations running on any items
12870          * are canceled and affected properties are set to their end values.
12871          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
12872          * animation since the animations are effectively done when this method is called.
12873          */
endAnimations()12874         public abstract void endAnimations();
12875 
12876         /**
12877          * Method which returns whether there are any item animations currently running.
12878          * This method can be used to determine whether to delay other actions until
12879          * animations end.
12880          *
12881          * @return true if there are any item animations currently running, false otherwise.
12882          */
isRunning()12883         public abstract boolean isRunning();
12884 
12885         /**
12886          * Method to be called by subclasses when an animation is finished.
12887          * <p>
12888          * For each call RecyclerView makes to
12889          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12890          * animateAppearance()},
12891          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12892          * animatePersistence()}, or
12893          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12894          * animateDisappearance()}, there
12895          * should
12896          * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
12897          * <p>
12898          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12899          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
12900          * and <code>newHolder</code>  (if they are not the same instance).
12901          *
12902          * @param viewHolder The ViewHolder whose animation is finished.
12903          * @see #onAnimationFinished(ViewHolder)
12904          */
dispatchAnimationFinished(@onNull ViewHolder viewHolder)12905         public final void dispatchAnimationFinished(@NonNull ViewHolder viewHolder) {
12906             onAnimationFinished(viewHolder);
12907             if (mListener != null) {
12908                 mListener.onAnimationFinished(viewHolder);
12909             }
12910         }
12911 
12912         /**
12913          * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
12914          * ItemAnimator.
12915          *
12916          * @param viewHolder The ViewHolder whose animation is finished. There might still be other
12917          *                   animations running on this ViewHolder.
12918          * @see #dispatchAnimationFinished(ViewHolder)
12919          */
onAnimationFinished(@onNull ViewHolder viewHolder)12920         public void onAnimationFinished(@NonNull ViewHolder viewHolder) {
12921         }
12922 
12923         /**
12924          * Method to be called by subclasses when an animation is started.
12925          * <p>
12926          * For each call RecyclerView makes to
12927          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12928          * animateAppearance()},
12929          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12930          * animatePersistence()}, or
12931          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12932          * animateDisappearance()}, there should be a matching
12933          * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
12934          * <p>
12935          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12936          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
12937          * and <code>newHolder</code> (if they are not the same instance).
12938          * <p>
12939          * If your ItemAnimator decides not to animate a ViewHolder, it should call
12940          * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
12941          * {@link #dispatchAnimationStarted(ViewHolder)}.
12942          *
12943          * @param viewHolder The ViewHolder whose animation is starting.
12944          * @see #onAnimationStarted(ViewHolder)
12945          */
dispatchAnimationStarted(@onNull ViewHolder viewHolder)12946         public final void dispatchAnimationStarted(@NonNull ViewHolder viewHolder) {
12947             onAnimationStarted(viewHolder);
12948         }
12949 
12950         /**
12951          * Called when a new animation is started on the given ViewHolder.
12952          *
12953          * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
12954          *                   might already be animating and this might be another animation.
12955          * @see #dispatchAnimationStarted(ViewHolder)
12956          */
onAnimationStarted(@onNull ViewHolder viewHolder)12957         public void onAnimationStarted(@NonNull ViewHolder viewHolder) {
12958 
12959         }
12960 
12961         /**
12962          * Like {@link #isRunning()}, this method returns whether there are any item
12963          * animations currently running. Additionally, the listener passed in will be called
12964          * when there are no item animations running, either immediately (before the method
12965          * returns) if no animations are currently running, or when the currently running
12966          * animations are {@link #dispatchAnimationsFinished() finished}.
12967          *
12968          * <p>Note that the listener is transient - it is either called immediately and not
12969          * stored at all, or stored only until it is called when running animations
12970          * are finished sometime later.</p>
12971          *
12972          * @param listener A listener to be called immediately if no animations are running
12973          * or later when currently-running animations have finished. A null listener is
12974          * equivalent to calling {@link #isRunning()}.
12975          * @return true if there are any item animations currently running, false otherwise.
12976          */
isRunning(@ullable ItemAnimatorFinishedListener listener)12977         public final boolean isRunning(@Nullable ItemAnimatorFinishedListener listener) {
12978             boolean running = isRunning();
12979             if (listener != null) {
12980                 if (!running) {
12981                     listener.onAnimationsFinished();
12982                 } else {
12983                     mFinishedListeners.add(listener);
12984                 }
12985             }
12986             return running;
12987         }
12988 
12989         /**
12990          * When an item is changed, ItemAnimator can decide whether it wants to re-use
12991          * the same ViewHolder for animations or RecyclerView should create a copy of the
12992          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12993          * <p>
12994          * Note that this method will only be called if the {@link ViewHolder} still has the same
12995          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12996          * both {@link ViewHolder}s in the
12997          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12998          * <p>
12999          * If your application is using change payloads, you can override
13000          * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
13001          *
13002          * @param viewHolder The ViewHolder which represents the changed item's old content.
13003          *
13004          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
13005          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
13006          *         ItemAnimator to animate. Default implementation returns <code>true</code>.
13007          *
13008          * @see #canReuseUpdatedViewHolder(ViewHolder, List)
13009          */
canReuseUpdatedViewHolder(@onNull ViewHolder viewHolder)13010         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
13011             return true;
13012         }
13013 
13014         /**
13015          * When an item is changed, ItemAnimator can decide whether it wants to re-use
13016          * the same ViewHolder for animations or RecyclerView should create a copy of the
13017          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
13018          * <p>
13019          * Note that this method will only be called if the {@link ViewHolder} still has the same
13020          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
13021          * both {@link ViewHolder}s in the
13022          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
13023          *
13024          * @param viewHolder The ViewHolder which represents the changed item's old content.
13025          * @param payloads A non-null list of merged payloads that were sent with change
13026          *                 notifications. Can be empty if the adapter is invalidated via
13027          *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
13028          *                 payloads will be passed into
13029          *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
13030          *                 method <b>if</b> this method returns <code>true</code>.
13031          *
13032          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
13033          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
13034          *         ItemAnimator to animate. Default implementation calls
13035          *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
13036          *
13037          * @see #canReuseUpdatedViewHolder(ViewHolder)
13038          */
canReuseUpdatedViewHolder(@onNull ViewHolder viewHolder, @NonNull List<Object> payloads)13039         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
13040                 @NonNull List<Object> payloads) {
13041             return canReuseUpdatedViewHolder(viewHolder);
13042         }
13043 
13044         /**
13045          * This method should be called by ItemAnimator implementations to notify
13046          * any listeners that all pending and active item animations are finished.
13047          */
dispatchAnimationsFinished()13048         public final void dispatchAnimationsFinished() {
13049             final int count = mFinishedListeners.size();
13050             for (int i = 0; i < count; ++i) {
13051                 mFinishedListeners.get(i).onAnimationsFinished();
13052             }
13053             mFinishedListeners.clear();
13054         }
13055 
13056         /**
13057          * Returns a new {@link ItemHolderInfo} which will be used to store information about the
13058          * ViewHolder. This information will later be passed into <code>animate**</code> methods.
13059          * <p>
13060          * You can override this method if you want to extend {@link ItemHolderInfo} and provide
13061          * your own instances.
13062          *
13063          * @return A new {@link ItemHolderInfo}.
13064          */
13065         @NonNull
obtainHolderInfo()13066         public ItemHolderInfo obtainHolderInfo() {
13067             return new ItemHolderInfo();
13068         }
13069 
13070         /**
13071          * The interface to be implemented by listeners to animation events from this
13072          * ItemAnimator. This is used internally and is not intended for developers to
13073          * create directly.
13074          */
13075         interface ItemAnimatorListener {
onAnimationFinished(@onNull ViewHolder item)13076             void onAnimationFinished(@NonNull ViewHolder item);
13077         }
13078 
13079         /**
13080          * This interface is used to inform listeners when all pending or running animations
13081          * in an ItemAnimator are finished. This can be used, for example, to delay an action
13082          * in a data set until currently-running animations are complete.
13083          *
13084          * @see #isRunning(ItemAnimatorFinishedListener)
13085          */
13086         public interface ItemAnimatorFinishedListener {
13087             /**
13088              * Notifies when all pending or running animations in an ItemAnimator are finished.
13089              */
onAnimationsFinished()13090             void onAnimationsFinished();
13091         }
13092 
13093         /**
13094          * A simple data structure that holds information about an item's bounds.
13095          * This information is used in calculating item animations. Default implementation of
13096          * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
13097          * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
13098          * structure. You can extend this class if you would like to keep more information about
13099          * the Views.
13100          * <p>
13101          * If you want to provide your own implementation but still use `super` methods to record
13102          * basic information, you can override {@link #obtainHolderInfo()} to provide your own
13103          * instances.
13104          */
13105         public static class ItemHolderInfo {
13106 
13107             /**
13108              * The left edge of the View (excluding decorations)
13109              */
13110             public int left;
13111 
13112             /**
13113              * The top edge of the View (excluding decorations)
13114              */
13115             public int top;
13116 
13117             /**
13118              * The right edge of the View (excluding decorations)
13119              */
13120             public int right;
13121 
13122             /**
13123              * The bottom edge of the View (excluding decorations)
13124              */
13125             public int bottom;
13126 
13127             /**
13128              * The change flags that were passed to
13129              * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
13130              */
13131             @AdapterChanges
13132             public int changeFlags;
13133 
ItemHolderInfo()13134             public ItemHolderInfo() {
13135             }
13136 
13137             /**
13138              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
13139              * the given ViewHolder. Clears all {@link #changeFlags}.
13140              *
13141              * @param holder The ViewHolder whose bounds should be copied.
13142              * @return This {@link ItemHolderInfo}
13143              */
13144             @NonNull
setFrom(@onNull RecyclerView.ViewHolder holder)13145             public ItemHolderInfo setFrom(@NonNull RecyclerView.ViewHolder holder) {
13146                 return setFrom(holder, 0);
13147             }
13148 
13149             /**
13150              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
13151              * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
13152              *
13153              * @param holder The ViewHolder whose bounds should be copied.
13154              * @param flags  The adapter change flags that were passed into
13155              *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
13156              *               List)}.
13157              * @return This {@link ItemHolderInfo}
13158              */
13159             @NonNull
setFrom(@onNull RecyclerView.ViewHolder holder, @AdapterChanges int flags)13160             public ItemHolderInfo setFrom(@NonNull RecyclerView.ViewHolder holder,
13161                     @AdapterChanges int flags) {
13162                 final View view = holder.itemView;
13163                 this.left = view.getLeft();
13164                 this.top = view.getTop();
13165                 this.right = view.getRight();
13166                 this.bottom = view.getBottom();
13167                 return this;
13168             }
13169         }
13170     }
13171 
13172     @Override
getChildDrawingOrder(int childCount, int i)13173     protected int getChildDrawingOrder(int childCount, int i) {
13174         if (mChildDrawingOrderCallback == null) {
13175             return super.getChildDrawingOrder(childCount, i);
13176         } else {
13177             return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
13178         }
13179     }
13180 
13181     /**
13182      * A callback interface that can be used to alter the drawing order of RecyclerView children.
13183      * <p>
13184      * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
13185      * that applies to that method also applies to this callback. For example, changing the drawing
13186      * order of two views will not have any effect if their elevation values are different since
13187      * elevation overrides the result of this callback.
13188      */
13189     public interface ChildDrawingOrderCallback {
13190         /**
13191          * Returns the index of the child to draw for this iteration. Override this
13192          * if you want to change the drawing order of children. By default, it
13193          * returns i.
13194          *
13195          * @param i The current iteration.
13196          * @return The index of the child to draw this iteration.
13197          *
13198          * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
13199          */
onGetChildDrawingOrder(int childCount, int i)13200         int onGetChildDrawingOrder(int childCount, int i);
13201     }
13202 
getScrollingChildHelper()13203     private NestedScrollingChildHelper getScrollingChildHelper() {
13204         if (mScrollingChildHelper == null) {
13205             mScrollingChildHelper = new NestedScrollingChildHelper(this);
13206         }
13207         return mScrollingChildHelper;
13208     }
13209 }
13210