1 /*
2  * Copyright (C) 2013 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 android.support.v7.widget;
19 
20 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
21 import static android.support.v4.view.ViewCompat.TYPE_NON_TOUCH;
22 import static android.support.v4.view.ViewCompat.TYPE_TOUCH;
23 
24 import android.content.Context;
25 import android.content.res.Resources;
26 import android.content.res.TypedArray;
27 import android.database.Observable;
28 import android.graphics.Canvas;
29 import android.graphics.Matrix;
30 import android.graphics.PointF;
31 import android.graphics.Rect;
32 import android.graphics.RectF;
33 import android.graphics.drawable.Drawable;
34 import android.graphics.drawable.StateListDrawable;
35 import android.os.Build;
36 import android.os.Bundle;
37 import android.os.Parcel;
38 import android.os.Parcelable;
39 import android.os.SystemClock;
40 import android.support.annotation.CallSuper;
41 import android.support.annotation.IntDef;
42 import android.support.annotation.NonNull;
43 import android.support.annotation.Nullable;
44 import android.support.annotation.RestrictTo;
45 import android.support.annotation.VisibleForTesting;
46 import android.support.v4.os.TraceCompat;
47 import android.support.v4.view.AbsSavedState;
48 import android.support.v4.view.InputDeviceCompat;
49 import android.support.v4.view.MotionEventCompat;
50 import android.support.v4.view.NestedScrollingChild2;
51 import android.support.v4.view.NestedScrollingChildHelper;
52 import android.support.v4.view.ScrollingView;
53 import android.support.v4.view.ViewCompat;
54 import android.support.v4.view.ViewConfigurationCompat;
55 import android.support.v4.view.accessibility.AccessibilityEventCompat;
56 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
57 import android.support.v4.widget.EdgeEffectCompat;
58 import android.support.v7.recyclerview.R;
59 import android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
60 import android.util.AttributeSet;
61 import android.util.Log;
62 import android.util.SparseArray;
63 import android.view.Display;
64 import android.view.FocusFinder;
65 import android.view.InputDevice;
66 import android.view.MotionEvent;
67 import android.view.VelocityTracker;
68 import android.view.View;
69 import android.view.ViewConfiguration;
70 import android.view.ViewGroup;
71 import android.view.ViewParent;
72 import android.view.accessibility.AccessibilityEvent;
73 import android.view.accessibility.AccessibilityManager;
74 import android.view.animation.Interpolator;
75 import android.widget.EdgeEffect;
76 import android.widget.OverScroller;
77 
78 import java.lang.annotation.Retention;
79 import java.lang.annotation.RetentionPolicy;
80 import java.lang.ref.WeakReference;
81 import java.lang.reflect.Constructor;
82 import java.lang.reflect.InvocationTargetException;
83 import java.util.ArrayList;
84 import java.util.Collections;
85 import java.util.List;
86 
87 /**
88  * A flexible view for providing a limited window into a large data set.
89  *
90  * <h3>Glossary of terms:</h3>
91  *
92  * <ul>
93  *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
94  *     that represent items in a data set.</li>
95  *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
96  *     <li><em>Index:</em> The index of an attached child view as used in a call to
97  *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
98  *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
99  *     to a <em>position</em> within the adapter.</li>
100  *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
101  *     position may be placed in a cache for later reuse to display the same type of data again
102  *     later. This can drastically improve performance by skipping initial layout inflation
103  *     or construction.</li>
104  *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
105  *     state during layout. Scrap views may be reused without becoming fully detached
106  *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
107  *     by the adapter if the view was considered <em>dirty</em>.</li>
108  *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
109  *     being displayed.</li>
110  * </ul>
111  *
112  * <h4>Positions in RecyclerView:</h4>
113  * <p>
114  * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
115  * {@link LayoutManager} to be able to detect data set changes in batches during a layout
116  * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
117  * It also helps with performance because all view bindings happen at the same time and unnecessary
118  * bindings are avoided.
119  * <p>
120  * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
121  * <ul>
122  *     <li>layout position: Position of an item in the latest layout calculation. This is the
123  *     position from the LayoutManager's perspective.</li>
124  *     <li>adapter position: Position of an item in the adapter. This is the position from
125  *     the Adapter's perspective.</li>
126  * </ul>
127  * <p>
128  * These two positions are the same except the time between dispatching <code>adapter.notify*
129  * </code> events and calculating the updated layout.
130  * <p>
131  * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
132  * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
133  * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
134  * last layout calculation. You can rely on these positions to be consistent with what user is
135  * currently seeing on the screen. For example, if you have a list of items on the screen and user
136  * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
137  * is seeing.
138  * <p>
139  * The other set of position related methods are in the form of
140  * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
141  * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
142  * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
143  * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
144  * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
145  * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
146  * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
147  * <code>null</code> results from these methods.
148  * <p>
149  * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
150  * writing an {@link Adapter}, you probably want to use adapter positions.
151  *
152  * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_layoutManager
153  */
154 public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild2 {
155 
156     static final String TAG = "RecyclerView";
157 
158     static final boolean DEBUG = false;
159 
160     static final boolean VERBOSE_TRACING = false;
161 
162     private static final int[]  NESTED_SCROLLING_ATTRS =
163             {16843830 /* android.R.attr.nestedScrollingEnabled */};
164 
165     private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
166 
167     /**
168      * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
169      * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
170      * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
171      * recursively traverses itemView and invalidates display list for each ViewGroup that matches
172      * this criteria.
173      */
174     static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
175             || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
176     /**
177      * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
178      * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
179      * 0 when mode is unspecified.
180      */
181     static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
182 
183     static final boolean POST_UPDATES_ON_ANIMATION = Build.VERSION.SDK_INT >= 16;
184 
185     /**
186      * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
187      * RenderThread but before the next frame begins. We schedule prefetch work in this window.
188      */
189     private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
190 
191     /**
192      * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
193      * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
194      */
195     private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
196 
197     /**
198      * on API 15-, a focused child can still be considered a focused child of RV even after
199      * it's being removed or its focusable flag is set to false. This is because when this focused
200      * child is detached, the reference to this child is not removed in clearFocus. API 16 and above
201      * properly handle this case by calling ensureInputFocusOnFirstFocusable or rootViewRequestFocus
202      * to request focus on a new child, which will clear the focus on the old (detached) child as a
203      * side-effect.
204      */
205     private static final boolean IGNORE_DETACHED_FOCUSED_CHILD = Build.VERSION.SDK_INT <= 15;
206 
207     static final boolean DISPATCH_TEMP_DETACH = false;
208     public static final int HORIZONTAL = 0;
209     public static final int VERTICAL = 1;
210 
211     public static final int NO_POSITION = -1;
212     public static final long NO_ID = -1;
213     public static final int INVALID_TYPE = -1;
214 
215     /**
216      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
217      * that the RecyclerView should use the standard touch slop for smooth,
218      * continuous scrolling.
219      */
220     public static final int TOUCH_SLOP_DEFAULT = 0;
221 
222     /**
223      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
224      * that the RecyclerView should use the standard touch slop for scrolling
225      * widgets that snap to a page or other coarse-grained barrier.
226      */
227     public static final int TOUCH_SLOP_PAGING = 1;
228 
229     static final int MAX_SCROLL_DURATION = 2000;
230 
231     /**
232      * RecyclerView is calculating a scroll.
233      * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
234      * it. Try to avoid using EditText, focusable views or handle them with care.
235      */
236     static final String TRACE_SCROLL_TAG = "RV Scroll";
237 
238     /**
239      * OnLayout has been called by the View system.
240      * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
241      * update themselves directly. This will cause a full re-layout but when it happens via the
242      * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
243      */
244     private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
245 
246     /**
247      * NotifyDataSetChanged or equal has been called.
248      * If this is taking a long time, try sending granular notify adapter changes instead of just
249      * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
250      * might help.
251      */
252     private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
253 
254     /**
255      * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
256      * If this is taking a long time, you may have dispatched too many Adapter updates causing too
257      * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
258      * methods.
259      */
260     private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
261 
262     /**
263      * RecyclerView is rebinding a View.
264      * If this is taking a lot of time, consider optimizing your layout or make sure you are not
265      * doing extra operations in onBindViewHolder call.
266      */
267     static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
268 
269     /**
270      * RecyclerView is attempting to pre-populate off screen views.
271      */
272     static final String TRACE_PREFETCH_TAG = "RV Prefetch";
273 
274     /**
275      * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
276      * RecyclerView.
277      */
278     static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
279 
280     /**
281      * RecyclerView is creating a new View.
282      * If too many of these present in Systrace:
283      * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
284      * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
285      * > Adapter#onFailedToRecycleView(ViewHolder)})
286      *
287      * - There might be too many item view types.
288      * > Try merging them
289      *
290      * - There might be too many itemChange animations and not enough space in RecyclerPool.
291      * >Try increasing your pool size and item cache size.
292      */
293     static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
294     private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
295             new Class[]{Context.class, AttributeSet.class, int.class, int.class};
296 
297     private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
298 
299     final Recycler mRecycler = new Recycler();
300 
301     private SavedState mPendingSavedState;
302 
303     /**
304      * Handles adapter updates
305      */
306     AdapterHelper mAdapterHelper;
307 
308     /**
309      * Handles abstraction between LayoutManager children and RecyclerView children
310      */
311     ChildHelper mChildHelper;
312 
313     /**
314      * Keeps data about views to be used for animations
315      */
316     final ViewInfoStore mViewInfoStore = new ViewInfoStore();
317 
318     /**
319      * Prior to L, there is no way to query this variable which is why we override the setter and
320      * track it here.
321      */
322     boolean mClipToPadding;
323 
324     /**
325      * Note: this Runnable is only ever posted if:
326      * 1) We've been through first layout
327      * 2) We know we have a fixed size (mHasFixedSize)
328      * 3) We're attached
329      */
330     final Runnable mUpdateChildViewsRunnable = new Runnable() {
331         @Override
332         public void run() {
333             if (!mFirstLayoutComplete || isLayoutRequested()) {
334                 // a layout request will happen, we should not do layout here.
335                 return;
336             }
337             if (!mIsAttached) {
338                 requestLayout();
339                 // if we are not attached yet, mark us as requiring layout and skip
340                 return;
341             }
342             if (mLayoutFrozen) {
343                 mLayoutRequestEaten = true;
344                 return; //we'll process updates when ice age ends.
345             }
346             consumePendingUpdateOperations();
347         }
348     };
349 
350     final Rect mTempRect = new Rect();
351     private final Rect mTempRect2 = new Rect();
352     final RectF mTempRectF = new RectF();
353     Adapter mAdapter;
354     @VisibleForTesting LayoutManager mLayout;
355     RecyclerListener mRecyclerListener;
356     final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
357     private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
358             new ArrayList<>();
359     private OnItemTouchListener mActiveOnItemTouchListener;
360     boolean mIsAttached;
361     boolean mHasFixedSize;
362     boolean mEnableFastScroller;
363     @VisibleForTesting boolean mFirstLayoutComplete;
364 
365     // Counting lock to control whether we should ignore requestLayout calls from children or not.
366     private int mEatRequestLayout = 0;
367 
368     boolean mLayoutRequestEaten;
369     boolean mLayoutFrozen;
370     private boolean mIgnoreMotionEventTillDown;
371 
372     // binary OR of change events that were eaten during a layout or scroll.
373     private int mEatenAccessibilityChangeFlags;
374     boolean mAdapterUpdateDuringMeasure;
375 
376     private final AccessibilityManager mAccessibilityManager;
377     private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
378 
379     /**
380      * Set to true when an adapter data set changed notification is received.
381      * In that case, we cannot run any animations since we don't know what happened until layout.
382      *
383      * Attached items are invalid until next layout, at which point layout will animate/replace
384      * items as necessary, building up content from the (effectively) new adapter from scratch.
385      *
386      * Cached items must be discarded when setting this to true, so that the cache may be freely
387      * used by prefetching until the next layout occurs.
388      *
389      * @see #setDataSetChangedAfterLayout()
390      */
391     boolean mDataSetHasChangedAfterLayout = false;
392 
393     /**
394      * This variable is incremented during a dispatchLayout and/or scroll.
395      * Some methods should not be called during these periods (e.g. adapter data change).
396      * Doing so will create hard to find bugs so we better check it and throw an exception.
397      *
398      * @see #assertInLayoutOrScroll(String)
399      * @see #assertNotInLayoutOrScroll(String)
400      */
401     private int mLayoutOrScrollCounter = 0;
402 
403     /**
404      * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
405      * (for API compatibility).
406      * <p>
407      * It is a bad practice for a developer to update the data in a scroll callback since it is
408      * potentially called during a layout.
409      */
410     private int mDispatchScrollCounter = 0;
411 
412     private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
413 
414     ItemAnimator mItemAnimator = new DefaultItemAnimator();
415 
416     private static final int INVALID_POINTER = -1;
417 
418     /**
419      * The RecyclerView is not currently scrolling.
420      * @see #getScrollState()
421      */
422     public static final int SCROLL_STATE_IDLE = 0;
423 
424     /**
425      * The RecyclerView is currently being dragged by outside input such as user touch input.
426      * @see #getScrollState()
427      */
428     public static final int SCROLL_STATE_DRAGGING = 1;
429 
430     /**
431      * The RecyclerView is currently animating to a final position while not under
432      * outside control.
433      * @see #getScrollState()
434      */
435     public static final int SCROLL_STATE_SETTLING = 2;
436 
437     static final long FOREVER_NS = Long.MAX_VALUE;
438 
439     // Touch/scrolling handling
440 
441     private int mScrollState = SCROLL_STATE_IDLE;
442     private int mScrollPointerId = INVALID_POINTER;
443     private VelocityTracker mVelocityTracker;
444     private int mInitialTouchX;
445     private int mInitialTouchY;
446     private int mLastTouchX;
447     private int mLastTouchY;
448     private int mTouchSlop;
449     private OnFlingListener mOnFlingListener;
450     private final int mMinFlingVelocity;
451     private final int mMaxFlingVelocity;
452 
453     // This value is used when handling rotary encoder generic motion events.
454     private float mScaledHorizontalScrollFactor = Float.MIN_VALUE;
455     private float mScaledVerticalScrollFactor = Float.MIN_VALUE;
456 
457     private boolean mPreserveFocusAfterLayout = true;
458 
459     final ViewFlinger mViewFlinger = new ViewFlinger();
460 
461     GapWorker mGapWorker;
462     GapWorker.LayoutPrefetchRegistryImpl mPrefetchRegistry =
463             ALLOW_THREAD_GAP_WORK ? new GapWorker.LayoutPrefetchRegistryImpl() : null;
464 
465     final State mState = new State();
466 
467     private OnScrollListener mScrollListener;
468     private List<OnScrollListener> mScrollListeners;
469 
470     // For use in item animations
471     boolean mItemsAddedOrRemoved = false;
472     boolean mItemsChanged = false;
473     private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
474             new ItemAnimatorRestoreListener();
475     boolean mPostedAnimatorRunner = false;
476     RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
477     private ChildDrawingOrderCallback mChildDrawingOrderCallback;
478 
479     // simple array to keep min and max child position during a layout calculation
480     // preserved not to create a new one in each layout pass
481     private final int[] mMinMaxLayoutPositions = new int[2];
482 
483     private NestedScrollingChildHelper mScrollingChildHelper;
484     private final int[] mScrollOffset = new int[2];
485     private final int[] mScrollConsumed = new int[2];
486     private final int[] mNestedOffsets = new int[2];
487 
488     /**
489      * These are views that had their a11y importance changed during a layout. We defer these events
490      * until the end of the layout because a11y service may make sync calls back to the RV while
491      * the View's state is undefined.
492      */
493     @VisibleForTesting
494     final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList<>();
495 
496     private Runnable mItemAnimatorRunner = new Runnable() {
497         @Override
498         public void run() {
499             if (mItemAnimator != null) {
500                 mItemAnimator.runPendingAnimations();
501             }
502             mPostedAnimatorRunner = false;
503         }
504     };
505 
506     static final Interpolator sQuinticInterpolator = new Interpolator() {
507         @Override
508         public float getInterpolation(float t) {
509             t -= 1.0f;
510             return t * t * t * t * t + 1.0f;
511         }
512     };
513 
514     /**
515      * The callback to convert view info diffs into animations.
516      */
517     private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
518             new ViewInfoStore.ProcessCallback() {
519                 @Override
520                 public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
521                         @Nullable ItemHolderInfo postInfo) {
522                     mRecycler.unscrapView(viewHolder);
523                     animateDisappearance(viewHolder, info, postInfo);
524                 }
525                 @Override
526                 public void processAppeared(ViewHolder viewHolder,
527                         ItemHolderInfo preInfo, ItemHolderInfo info) {
528                     animateAppearance(viewHolder, preInfo, info);
529                 }
530 
531                 @Override
532                 public void processPersistent(ViewHolder viewHolder,
533                         @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
534                     viewHolder.setIsRecyclable(false);
535                     if (mDataSetHasChangedAfterLayout) {
536                         // since it was rebound, use change instead as we'll be mapping them from
537                         // stable ids. If stable ids were false, we would not be running any
538                         // animations
539                         if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo,
540                                 postInfo)) {
541                             postAnimationRunner();
542                         }
543                     } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
544                         postAnimationRunner();
545                     }
546                 }
547                 @Override
548                 public void unused(ViewHolder viewHolder) {
549                     mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
550                 }
551             };
552 
RecyclerView(Context context)553     public RecyclerView(Context context) {
554         this(context, null);
555     }
556 
RecyclerView(Context context, @Nullable AttributeSet attrs)557     public RecyclerView(Context context, @Nullable AttributeSet attrs) {
558         this(context, attrs, 0);
559     }
560 
RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle)561     public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
562         super(context, attrs, defStyle);
563         if (attrs != null) {
564             TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
565             mClipToPadding = a.getBoolean(0, true);
566             a.recycle();
567         } else {
568             mClipToPadding = true;
569         }
570         setScrollContainer(true);
571         setFocusableInTouchMode(true);
572 
573         final ViewConfiguration vc = ViewConfiguration.get(context);
574         mTouchSlop = vc.getScaledTouchSlop();
575         mScaledHorizontalScrollFactor =
576                 ViewConfigurationCompat.getScaledHorizontalScrollFactor(vc, context);
577         mScaledVerticalScrollFactor =
578                 ViewConfigurationCompat.getScaledVerticalScrollFactor(vc, context);
579         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
580         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
581         setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
582 
583         mItemAnimator.setListener(mItemAnimatorListener);
584         initAdapterManager();
585         initChildrenHelper();
586         // If not explicitly specified this view is important for accessibility.
587         if (ViewCompat.getImportantForAccessibility(this)
588                 == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
589             ViewCompat.setImportantForAccessibility(this,
590                     ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
591         }
592         mAccessibilityManager = (AccessibilityManager) getContext()
593                 .getSystemService(Context.ACCESSIBILITY_SERVICE);
594         setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
595         // Create the layoutManager if specified.
596 
597         boolean nestedScrollingEnabled = true;
598 
599         if (attrs != null) {
600             int defStyleRes = 0;
601             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
602                     defStyle, defStyleRes);
603             String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
604             int descendantFocusability = a.getInt(
605                     R.styleable.RecyclerView_android_descendantFocusability, -1);
606             if (descendantFocusability == -1) {
607                 setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
608             }
609             mEnableFastScroller = a.getBoolean(R.styleable.RecyclerView_fastScrollEnabled, false);
610             if (mEnableFastScroller) {
611                 StateListDrawable verticalThumbDrawable = (StateListDrawable) a
612                         .getDrawable(R.styleable.RecyclerView_fastScrollVerticalThumbDrawable);
613                 Drawable verticalTrackDrawable = a
614                         .getDrawable(R.styleable.RecyclerView_fastScrollVerticalTrackDrawable);
615                 StateListDrawable horizontalThumbDrawable = (StateListDrawable) a
616                         .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalThumbDrawable);
617                 Drawable horizontalTrackDrawable = a
618                         .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalTrackDrawable);
619                 initFastScroller(verticalThumbDrawable, verticalTrackDrawable,
620                         horizontalThumbDrawable, horizontalTrackDrawable);
621             }
622             a.recycle();
623             createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
624 
625             if (Build.VERSION.SDK_INT >= 21) {
626                 a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
627                         defStyle, defStyleRes);
628                 nestedScrollingEnabled = a.getBoolean(0, true);
629                 a.recycle();
630             }
631         } else {
632             setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
633         }
634 
635         // Re-set whether nested scrolling is enabled so that it is set on all API levels
636         setNestedScrollingEnabled(nestedScrollingEnabled);
637     }
638 
639     @Override
toString()640     public String toString() {
641         return super.toString()
642                 + ", adapter:" + mAdapter
643                 + ", layout:" + mLayout
644                 + ", context:" + getContext();
645     }
646 
647     /**
648      * Label appended to all public exception strings, used to help find which RV in an app is
649      * hitting an exception.
650      */
exceptionLabel()651     String exceptionLabel() {
652         return " " + this;
653     }
654 
655     /**
656      * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
657      * @return An instance of AccessibilityDelegateCompat used by RecyclerView
658      */
getCompatAccessibilityDelegate()659     public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
660         return mAccessibilityDelegate;
661     }
662 
663     /**
664      * Sets the accessibility delegate compatibility implementation used by RecyclerView.
665      * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
666      */
setAccessibilityDelegateCompat( RecyclerViewAccessibilityDelegate accessibilityDelegate)667     public void setAccessibilityDelegateCompat(
668             RecyclerViewAccessibilityDelegate accessibilityDelegate) {
669         mAccessibilityDelegate = accessibilityDelegate;
670         ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
671     }
672 
673     /**
674      * Instantiate and set a LayoutManager, if specified in the attributes.
675      */
createLayoutManager(Context context, String className, AttributeSet attrs, int defStyleAttr, int defStyleRes)676     private void createLayoutManager(Context context, String className, AttributeSet attrs,
677             int defStyleAttr, int defStyleRes) {
678         if (className != null) {
679             className = className.trim();
680             if (className.length() != 0) {  // Can't use isEmpty since it was added in API 9.
681                 className = getFullClassName(context, className);
682                 try {
683                     ClassLoader classLoader;
684                     if (isInEditMode()) {
685                         // Stupid layoutlib cannot handle simple class loaders.
686                         classLoader = this.getClass().getClassLoader();
687                     } else {
688                         classLoader = context.getClassLoader();
689                     }
690                     Class<? extends LayoutManager> layoutManagerClass =
691                             classLoader.loadClass(className).asSubclass(LayoutManager.class);
692                     Constructor<? extends LayoutManager> constructor;
693                     Object[] constructorArgs = null;
694                     try {
695                         constructor = layoutManagerClass
696                                 .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
697                         constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
698                     } catch (NoSuchMethodException e) {
699                         try {
700                             constructor = layoutManagerClass.getConstructor();
701                         } catch (NoSuchMethodException e1) {
702                             e1.initCause(e);
703                             throw new IllegalStateException(attrs.getPositionDescription()
704                                     + ": Error creating LayoutManager " + className, e1);
705                         }
706                     }
707                     constructor.setAccessible(true);
708                     setLayoutManager(constructor.newInstance(constructorArgs));
709                 } catch (ClassNotFoundException e) {
710                     throw new IllegalStateException(attrs.getPositionDescription()
711                             + ": Unable to find LayoutManager " + className, e);
712                 } catch (InvocationTargetException e) {
713                     throw new IllegalStateException(attrs.getPositionDescription()
714                             + ": Could not instantiate the LayoutManager: " + className, e);
715                 } catch (InstantiationException e) {
716                     throw new IllegalStateException(attrs.getPositionDescription()
717                             + ": Could not instantiate the LayoutManager: " + className, e);
718                 } catch (IllegalAccessException e) {
719                     throw new IllegalStateException(attrs.getPositionDescription()
720                             + ": Cannot access non-public constructor " + className, e);
721                 } catch (ClassCastException e) {
722                     throw new IllegalStateException(attrs.getPositionDescription()
723                             + ": Class is not a LayoutManager " + className, e);
724                 }
725             }
726         }
727     }
728 
getFullClassName(Context context, String className)729     private String getFullClassName(Context context, String className) {
730         if (className.charAt(0) == '.') {
731             return context.getPackageName() + className;
732         }
733         if (className.contains(".")) {
734             return className;
735         }
736         return RecyclerView.class.getPackage().getName() + '.' + className;
737     }
738 
initChildrenHelper()739     private void initChildrenHelper() {
740         mChildHelper = new ChildHelper(new ChildHelper.Callback() {
741             @Override
742             public int getChildCount() {
743                 return RecyclerView.this.getChildCount();
744             }
745 
746             @Override
747             public void addView(View child, int index) {
748                 if (VERBOSE_TRACING) {
749                     TraceCompat.beginSection("RV addView");
750                 }
751                 RecyclerView.this.addView(child, index);
752                 if (VERBOSE_TRACING) {
753                     TraceCompat.endSection();
754                 }
755                 dispatchChildAttached(child);
756             }
757 
758             @Override
759             public int indexOfChild(View view) {
760                 return RecyclerView.this.indexOfChild(view);
761             }
762 
763             @Override
764             public void removeViewAt(int index) {
765                 final View child = RecyclerView.this.getChildAt(index);
766                 if (child != null) {
767                     dispatchChildDetached(child);
768 
769                     // Clear any android.view.animation.Animation that may prevent the item from
770                     // detaching when being removed. If a child is re-added before the
771                     // lazy detach occurs, it will receive invalid attach/detach sequencing.
772                     child.clearAnimation();
773                 }
774                 if (VERBOSE_TRACING) {
775                     TraceCompat.beginSection("RV removeViewAt");
776                 }
777                 RecyclerView.this.removeViewAt(index);
778                 if (VERBOSE_TRACING) {
779                     TraceCompat.endSection();
780                 }
781             }
782 
783             @Override
784             public View getChildAt(int offset) {
785                 return RecyclerView.this.getChildAt(offset);
786             }
787 
788             @Override
789             public void removeAllViews() {
790                 final int count = getChildCount();
791                 for (int i = 0; i < count; i++) {
792                     View child = getChildAt(i);
793                     dispatchChildDetached(child);
794 
795                     // Clear any android.view.animation.Animation that may prevent the item from
796                     // detaching when being removed. If a child is re-added before the
797                     // lazy detach occurs, it will receive invalid attach/detach sequencing.
798                     child.clearAnimation();
799                 }
800                 RecyclerView.this.removeAllViews();
801             }
802 
803             @Override
804             public ViewHolder getChildViewHolder(View view) {
805                 return getChildViewHolderInt(view);
806             }
807 
808             @Override
809             public void attachViewToParent(View child, int index,
810                     ViewGroup.LayoutParams layoutParams) {
811                 final ViewHolder vh = getChildViewHolderInt(child);
812                 if (vh != null) {
813                     if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
814                         throw new IllegalArgumentException("Called attach on a child which is not"
815                                 + " detached: " + vh + exceptionLabel());
816                     }
817                     if (DEBUG) {
818                         Log.d(TAG, "reAttach " + vh);
819                     }
820                     vh.clearTmpDetachFlag();
821                 }
822                 RecyclerView.this.attachViewToParent(child, index, layoutParams);
823             }
824 
825             @Override
826             public void detachViewFromParent(int offset) {
827                 final View view = getChildAt(offset);
828                 if (view != null) {
829                     final ViewHolder vh = getChildViewHolderInt(view);
830                     if (vh != null) {
831                         if (vh.isTmpDetached() && !vh.shouldIgnore()) {
832                             throw new IllegalArgumentException("called detach on an already"
833                                     + " detached child " + vh + exceptionLabel());
834                         }
835                         if (DEBUG) {
836                             Log.d(TAG, "tmpDetach " + vh);
837                         }
838                         vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
839                     }
840                 }
841                 RecyclerView.this.detachViewFromParent(offset);
842             }
843 
844             @Override
845             public void onEnteredHiddenState(View child) {
846                 final ViewHolder vh = getChildViewHolderInt(child);
847                 if (vh != null) {
848                     vh.onEnteredHiddenState(RecyclerView.this);
849                 }
850             }
851 
852             @Override
853             public void onLeftHiddenState(View child) {
854                 final ViewHolder vh = getChildViewHolderInt(child);
855                 if (vh != null) {
856                     vh.onLeftHiddenState(RecyclerView.this);
857                 }
858             }
859         });
860     }
861 
initAdapterManager()862     void initAdapterManager() {
863         mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
864             @Override
865             public ViewHolder findViewHolder(int position) {
866                 final ViewHolder vh = findViewHolderForPosition(position, true);
867                 if (vh == null) {
868                     return null;
869                 }
870                 // ensure it is not hidden because for adapter helper, the only thing matter is that
871                 // LM thinks view is a child.
872                 if (mChildHelper.isHidden(vh.itemView)) {
873                     if (DEBUG) {
874                         Log.d(TAG, "assuming view holder cannot be find because it is hidden");
875                     }
876                     return null;
877                 }
878                 return vh;
879             }
880 
881             @Override
882             public void offsetPositionsForRemovingInvisible(int start, int count) {
883                 offsetPositionRecordsForRemove(start, count, true);
884                 mItemsAddedOrRemoved = true;
885                 mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
886             }
887 
888             @Override
889             public void offsetPositionsForRemovingLaidOutOrNewView(
890                     int positionStart, int itemCount) {
891                 offsetPositionRecordsForRemove(positionStart, itemCount, false);
892                 mItemsAddedOrRemoved = true;
893             }
894 
895 
896             @Override
897             public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
898                 viewRangeUpdate(positionStart, itemCount, payload);
899                 mItemsChanged = true;
900             }
901 
902             @Override
903             public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
904                 dispatchUpdate(op);
905             }
906 
907             void dispatchUpdate(AdapterHelper.UpdateOp op) {
908                 switch (op.cmd) {
909                     case AdapterHelper.UpdateOp.ADD:
910                         mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
911                         break;
912                     case AdapterHelper.UpdateOp.REMOVE:
913                         mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
914                         break;
915                     case AdapterHelper.UpdateOp.UPDATE:
916                         mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
917                                 op.payload);
918                         break;
919                     case AdapterHelper.UpdateOp.MOVE:
920                         mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
921                         break;
922                 }
923             }
924 
925             @Override
926             public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
927                 dispatchUpdate(op);
928             }
929 
930             @Override
931             public void offsetPositionsForAdd(int positionStart, int itemCount) {
932                 offsetPositionRecordsForInsert(positionStart, itemCount);
933                 mItemsAddedOrRemoved = true;
934             }
935 
936             @Override
937             public void offsetPositionsForMove(int from, int to) {
938                 offsetPositionRecordsForMove(from, to);
939                 // should we create mItemsMoved ?
940                 mItemsAddedOrRemoved = true;
941             }
942         });
943     }
944 
945     /**
946      * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
947      * size is not affected by the adapter contents. RecyclerView can still change its size based
948      * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
949      * size of its children or contents of its adapter (except the number of items in the adapter).
950      * <p>
951      * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
952      * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
953      *
954      * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
955      */
setHasFixedSize(boolean hasFixedSize)956     public void setHasFixedSize(boolean hasFixedSize) {
957         mHasFixedSize = hasFixedSize;
958     }
959 
960     /**
961      * @return true if the app has specified that changes in adapter content cannot change
962      * the size of the RecyclerView itself.
963      */
hasFixedSize()964     public boolean hasFixedSize() {
965         return mHasFixedSize;
966     }
967 
968     @Override
setClipToPadding(boolean clipToPadding)969     public void setClipToPadding(boolean clipToPadding) {
970         if (clipToPadding != mClipToPadding) {
971             invalidateGlows();
972         }
973         mClipToPadding = clipToPadding;
974         super.setClipToPadding(clipToPadding);
975         if (mFirstLayoutComplete) {
976             requestLayout();
977         }
978     }
979 
980     /**
981      * Returns whether this RecyclerView will clip its children to its padding, and resize (but
982      * not clip) any EdgeEffect to the padded region, if padding is present.
983      * <p>
984      * By default, children are clipped to the padding of their parent
985      * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
986      *
987      * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
988      *         clip) any EdgeEffect to the padded region, false otherwise.
989      *
990      * @attr name android:clipToPadding
991      */
992     @Override
getClipToPadding()993     public boolean getClipToPadding() {
994         return mClipToPadding;
995     }
996 
997     /**
998      * Configure the scrolling touch slop for a specific use case.
999      *
1000      * Set up the RecyclerView's scrolling motion threshold based on common usages.
1001      * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
1002      *
1003      * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
1004      *                     the intended usage of this RecyclerView
1005      */
setScrollingTouchSlop(int slopConstant)1006     public void setScrollingTouchSlop(int slopConstant) {
1007         final ViewConfiguration vc = ViewConfiguration.get(getContext());
1008         switch (slopConstant) {
1009             default:
1010                 Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
1011                         + slopConstant + "; using default value");
1012                 // fall-through
1013             case TOUCH_SLOP_DEFAULT:
1014                 mTouchSlop = vc.getScaledTouchSlop();
1015                 break;
1016 
1017             case TOUCH_SLOP_PAGING:
1018                 mTouchSlop = vc.getScaledPagingTouchSlop();
1019                 break;
1020         }
1021     }
1022 
1023     /**
1024      * Swaps the current adapter with the provided one. It is similar to
1025      * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
1026      * {@link ViewHolder} and does not clear the RecycledViewPool.
1027      * <p>
1028      * Note that it still calls onAdapterChanged callbacks.
1029      *
1030      * @param adapter The new adapter to set, or null to set no adapter.
1031      * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
1032      *                                      Views. If adapters have stable ids and/or you want to
1033      *                                      animate the disappearing views, you may prefer to set
1034      *                                      this to false.
1035      * @see #setAdapter(Adapter)
1036      */
swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews)1037     public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
1038         // bail out if layout is frozen
1039         setLayoutFrozen(false);
1040         setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
1041         setDataSetChangedAfterLayout();
1042         requestLayout();
1043     }
1044     /**
1045      * Set a new adapter to provide child views on demand.
1046      * <p>
1047      * When adapter is changed, all existing views are recycled back to the pool. If the pool has
1048      * only one adapter, it will be cleared.
1049      *
1050      * @param adapter The new adapter to set, or null to set no adapter.
1051      * @see #swapAdapter(Adapter, boolean)
1052      */
setAdapter(Adapter adapter)1053     public void setAdapter(Adapter adapter) {
1054         // bail out if layout is frozen
1055         setLayoutFrozen(false);
1056         setAdapterInternal(adapter, false, true);
1057         requestLayout();
1058     }
1059 
1060     /**
1061      * Removes and recycles all views - both those currently attached, and those in the Recycler.
1062      */
removeAndRecycleViews()1063     void removeAndRecycleViews() {
1064         // end all running animations
1065         if (mItemAnimator != null) {
1066             mItemAnimator.endAnimations();
1067         }
1068         // Since animations are ended, mLayout.children should be equal to
1069         // recyclerView.children. This may not be true if item animator's end does not work as
1070         // expected. (e.g. not release children instantly). It is safer to use mLayout's child
1071         // count.
1072         if (mLayout != null) {
1073             mLayout.removeAndRecycleAllViews(mRecycler);
1074             mLayout.removeAndRecycleScrapInt(mRecycler);
1075         }
1076         // we should clear it here before adapters are swapped to ensure correct callbacks.
1077         mRecycler.clear();
1078     }
1079 
1080     /**
1081      * Replaces the current adapter with the new one and triggers listeners.
1082      * @param adapter The new adapter
1083      * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
1084      *                               item types with the current adapter (helps us avoid cache
1085      *                               invalidation).
1086      * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
1087      *                               compatibleWithPrevious is false, this parameter is ignored.
1088      */
setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews)1089     private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
1090             boolean removeAndRecycleViews) {
1091         if (mAdapter != null) {
1092             mAdapter.unregisterAdapterDataObserver(mObserver);
1093             mAdapter.onDetachedFromRecyclerView(this);
1094         }
1095         if (!compatibleWithPrevious || removeAndRecycleViews) {
1096             removeAndRecycleViews();
1097         }
1098         mAdapterHelper.reset();
1099         final Adapter oldAdapter = mAdapter;
1100         mAdapter = adapter;
1101         if (adapter != null) {
1102             adapter.registerAdapterDataObserver(mObserver);
1103             adapter.onAttachedToRecyclerView(this);
1104         }
1105         if (mLayout != null) {
1106             mLayout.onAdapterChanged(oldAdapter, mAdapter);
1107         }
1108         mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
1109         mState.mStructureChanged = true;
1110         markKnownViewsInvalid();
1111     }
1112 
1113     /**
1114      * Retrieves the previously set adapter or null if no adapter is set.
1115      *
1116      * @return The previously set adapter
1117      * @see #setAdapter(Adapter)
1118      */
getAdapter()1119     public Adapter getAdapter() {
1120         return mAdapter;
1121     }
1122 
1123     /**
1124      * Register a listener that will be notified whenever a child view is recycled.
1125      *
1126      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1127      * that a child view is no longer needed. If an application associates expensive
1128      * or heavyweight data with item views, this may be a good place to release
1129      * or free those resources.</p>
1130      *
1131      * @param listener Listener to register, or null to clear
1132      */
setRecyclerListener(RecyclerListener listener)1133     public void setRecyclerListener(RecyclerListener listener) {
1134         mRecyclerListener = listener;
1135     }
1136 
1137     /**
1138      * <p>Return the offset of the RecyclerView's text baseline from the its top
1139      * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
1140      * this method returns -1.</p>
1141      *
1142      * @return the offset of the baseline within the RecyclerView's bounds or -1
1143      *         if baseline alignment is not supported
1144      */
1145     @Override
getBaseline()1146     public int getBaseline() {
1147         if (mLayout != null) {
1148             return mLayout.getBaseline();
1149         } else {
1150             return super.getBaseline();
1151         }
1152     }
1153 
1154     /**
1155      * Register a listener that will be notified whenever a child view is attached to or detached
1156      * from RecyclerView.
1157      *
1158      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1159      * that a child view is no longer needed. If an application associates expensive
1160      * or heavyweight data with item views, this may be a good place to release
1161      * or free those resources.</p>
1162      *
1163      * @param listener Listener to register
1164      */
addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener)1165     public void addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1166         if (mOnChildAttachStateListeners == null) {
1167             mOnChildAttachStateListeners = new ArrayList<>();
1168         }
1169         mOnChildAttachStateListeners.add(listener);
1170     }
1171 
1172     /**
1173      * Removes the provided listener from child attached state listeners list.
1174      *
1175      * @param listener Listener to unregister
1176      */
removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener)1177     public void removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1178         if (mOnChildAttachStateListeners == null) {
1179             return;
1180         }
1181         mOnChildAttachStateListeners.remove(listener);
1182     }
1183 
1184     /**
1185      * Removes all listeners that were added via
1186      * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
1187      */
clearOnChildAttachStateChangeListeners()1188     public void clearOnChildAttachStateChangeListeners() {
1189         if (mOnChildAttachStateListeners != null) {
1190             mOnChildAttachStateListeners.clear();
1191         }
1192     }
1193 
1194     /**
1195      * Set the {@link LayoutManager} that this RecyclerView will use.
1196      *
1197      * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
1198      * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
1199      * layout arrangements for child views. These arrangements are controlled by the
1200      * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
1201      *
1202      * <p>Several default strategies are provided for common uses such as lists and grids.</p>
1203      *
1204      * @param layout LayoutManager to use
1205      */
setLayoutManager(LayoutManager layout)1206     public void setLayoutManager(LayoutManager layout) {
1207         if (layout == mLayout) {
1208             return;
1209         }
1210         stopScroll();
1211         // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
1212         // chance that LayoutManagers will re-use views.
1213         if (mLayout != null) {
1214             // end all running animations
1215             if (mItemAnimator != null) {
1216                 mItemAnimator.endAnimations();
1217             }
1218             mLayout.removeAndRecycleAllViews(mRecycler);
1219             mLayout.removeAndRecycleScrapInt(mRecycler);
1220             mRecycler.clear();
1221 
1222             if (mIsAttached) {
1223                 mLayout.dispatchDetachedFromWindow(this, mRecycler);
1224             }
1225             mLayout.setRecyclerView(null);
1226             mLayout = null;
1227         } else {
1228             mRecycler.clear();
1229         }
1230         // this is just a defensive measure for faulty item animators.
1231         mChildHelper.removeAllViewsUnfiltered();
1232         mLayout = layout;
1233         if (layout != null) {
1234             if (layout.mRecyclerView != null) {
1235                 throw new IllegalArgumentException("LayoutManager " + layout
1236                         + " is already attached to a RecyclerView:"
1237                         + layout.mRecyclerView.exceptionLabel());
1238             }
1239             mLayout.setRecyclerView(this);
1240             if (mIsAttached) {
1241                 mLayout.dispatchAttachedToWindow(this);
1242             }
1243         }
1244         mRecycler.updateViewCacheSize();
1245         requestLayout();
1246     }
1247 
1248     /**
1249      * Set a {@link OnFlingListener} for this {@link RecyclerView}.
1250      * <p>
1251      * If the {@link OnFlingListener} is set then it will receive
1252      * calls to {@link #fling(int,int)} and will be able to intercept them.
1253      *
1254      * @param onFlingListener The {@link OnFlingListener} instance.
1255      */
setOnFlingListener(@ullable OnFlingListener onFlingListener)1256     public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
1257         mOnFlingListener = onFlingListener;
1258     }
1259 
1260     /**
1261      * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
1262      *
1263      * @return The {@link OnFlingListener} instance currently set (can be null).
1264      */
1265     @Nullable
getOnFlingListener()1266     public OnFlingListener getOnFlingListener() {
1267         return mOnFlingListener;
1268     }
1269 
1270     @Override
onSaveInstanceState()1271     protected Parcelable onSaveInstanceState() {
1272         SavedState state = new SavedState(super.onSaveInstanceState());
1273         if (mPendingSavedState != null) {
1274             state.copyFrom(mPendingSavedState);
1275         } else if (mLayout != null) {
1276             state.mLayoutState = mLayout.onSaveInstanceState();
1277         } else {
1278             state.mLayoutState = null;
1279         }
1280 
1281         return state;
1282     }
1283 
1284     @Override
onRestoreInstanceState(Parcelable state)1285     protected void onRestoreInstanceState(Parcelable state) {
1286         if (!(state instanceof SavedState)) {
1287             super.onRestoreInstanceState(state);
1288             return;
1289         }
1290 
1291         mPendingSavedState = (SavedState) state;
1292         super.onRestoreInstanceState(mPendingSavedState.getSuperState());
1293         if (mLayout != null && mPendingSavedState.mLayoutState != null) {
1294             mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
1295         }
1296     }
1297 
1298     /**
1299      * Override to prevent freezing of any views created by the adapter.
1300      */
1301     @Override
dispatchSaveInstanceState(SparseArray<Parcelable> container)1302     protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1303         dispatchFreezeSelfOnly(container);
1304     }
1305 
1306     /**
1307      * Override to prevent thawing of any views created by the adapter.
1308      */
1309     @Override
dispatchRestoreInstanceState(SparseArray<Parcelable> container)1310     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1311         dispatchThawSelfOnly(container);
1312     }
1313 
1314     /**
1315      * Adds a view to the animatingViews list.
1316      * mAnimatingViews holds the child views that are currently being kept around
1317      * purely for the purpose of being animated out of view. They are drawn as a regular
1318      * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
1319      * as they are managed separately from the regular child views.
1320      * @param viewHolder The ViewHolder to be removed
1321      */
addAnimatingView(ViewHolder viewHolder)1322     private void addAnimatingView(ViewHolder viewHolder) {
1323         final View view = viewHolder.itemView;
1324         final boolean alreadyParented = view.getParent() == this;
1325         mRecycler.unscrapView(getChildViewHolder(view));
1326         if (viewHolder.isTmpDetached()) {
1327             // re-attach
1328             mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
1329         } else if (!alreadyParented) {
1330             mChildHelper.addView(view, true);
1331         } else {
1332             mChildHelper.hide(view);
1333         }
1334     }
1335 
1336     /**
1337      * Removes a view from the animatingViews list.
1338      * @param view The view to be removed
1339      * @see #addAnimatingView(RecyclerView.ViewHolder)
1340      * @return true if an animating view is removed
1341      */
removeAnimatingView(View view)1342     boolean removeAnimatingView(View view) {
1343         eatRequestLayout();
1344         final boolean removed = mChildHelper.removeViewIfHidden(view);
1345         if (removed) {
1346             final ViewHolder viewHolder = getChildViewHolderInt(view);
1347             mRecycler.unscrapView(viewHolder);
1348             mRecycler.recycleViewHolderInternal(viewHolder);
1349             if (DEBUG) {
1350                 Log.d(TAG, "after removing animated view: " + view + ", " + this);
1351             }
1352         }
1353         // only clear request eaten flag if we removed the view.
1354         resumeRequestLayout(!removed);
1355         return removed;
1356     }
1357 
1358     /**
1359      * Return the {@link LayoutManager} currently responsible for
1360      * layout policy for this RecyclerView.
1361      *
1362      * @return The currently bound LayoutManager
1363      */
getLayoutManager()1364     public LayoutManager getLayoutManager() {
1365         return mLayout;
1366     }
1367 
1368     /**
1369      * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
1370      * if no pool is set for this view a new one will be created. See
1371      * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
1372      *
1373      * @return The pool used to store recycled item views for reuse.
1374      * @see #setRecycledViewPool(RecycledViewPool)
1375      */
getRecycledViewPool()1376     public RecycledViewPool getRecycledViewPool() {
1377         return mRecycler.getRecycledViewPool();
1378     }
1379 
1380     /**
1381      * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
1382      * This can be useful if you have multiple RecyclerViews with adapters that use the same
1383      * view types, for example if you have several data sets with the same kinds of item views
1384      * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
1385      *
1386      * @param pool Pool to set. If this parameter is null a new pool will be created and used.
1387      */
setRecycledViewPool(RecycledViewPool pool)1388     public void setRecycledViewPool(RecycledViewPool pool) {
1389         mRecycler.setRecycledViewPool(pool);
1390     }
1391 
1392     /**
1393      * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
1394      *
1395      * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
1396      *
1397      * @see ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)
1398      */
setViewCacheExtension(ViewCacheExtension extension)1399     public void setViewCacheExtension(ViewCacheExtension extension) {
1400         mRecycler.setViewCacheExtension(extension);
1401     }
1402 
1403     /**
1404      * Set the number of offscreen views to retain before adding them to the potentially shared
1405      * {@link #getRecycledViewPool() recycled view pool}.
1406      *
1407      * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
1408      * a LayoutManager to reuse those views unmodified without needing to return to the adapter
1409      * to rebind them.</p>
1410      *
1411      * @param size Number of views to cache offscreen before returning them to the general
1412      *             recycled view pool
1413      */
setItemViewCacheSize(int size)1414     public void setItemViewCacheSize(int size) {
1415         mRecycler.setViewCacheSize(size);
1416     }
1417 
1418     /**
1419      * Return the current scrolling state of the RecyclerView.
1420      *
1421      * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
1422      * {@link #SCROLL_STATE_SETTLING}
1423      */
getScrollState()1424     public int getScrollState() {
1425         return mScrollState;
1426     }
1427 
setScrollState(int state)1428     void setScrollState(int state) {
1429         if (state == mScrollState) {
1430             return;
1431         }
1432         if (DEBUG) {
1433             Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
1434                     new Exception());
1435         }
1436         mScrollState = state;
1437         if (state != SCROLL_STATE_SETTLING) {
1438             stopScrollersInternal();
1439         }
1440         dispatchOnScrollStateChanged(state);
1441     }
1442 
1443     /**
1444      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1445      * affect both measurement and drawing of individual item views.
1446      *
1447      * <p>Item decorations are ordered. Decorations placed earlier in the list will
1448      * be run/queried/drawn first for their effects on item views. Padding added to views
1449      * will be nested; a padding added by an earlier decoration will mean further
1450      * item decorations in the list will be asked to draw/pad within the previous decoration's
1451      * given area.</p>
1452      *
1453      * @param decor Decoration to add
1454      * @param index Position in the decoration chain to insert this decoration at. If this value
1455      *              is negative the decoration will be added at the end.
1456      */
addItemDecoration(ItemDecoration decor, int index)1457     public void addItemDecoration(ItemDecoration decor, int index) {
1458         if (mLayout != null) {
1459             mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
1460                     + " layout");
1461         }
1462         if (mItemDecorations.isEmpty()) {
1463             setWillNotDraw(false);
1464         }
1465         if (index < 0) {
1466             mItemDecorations.add(decor);
1467         } else {
1468             mItemDecorations.add(index, decor);
1469         }
1470         markItemDecorInsetsDirty();
1471         requestLayout();
1472     }
1473 
1474     /**
1475      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1476      * affect both measurement and drawing of individual item views.
1477      *
1478      * <p>Item decorations are ordered. Decorations placed earlier in the list will
1479      * be run/queried/drawn first for their effects on item views. Padding added to views
1480      * will be nested; a padding added by an earlier decoration will mean further
1481      * item decorations in the list will be asked to draw/pad within the previous decoration's
1482      * given area.</p>
1483      *
1484      * @param decor Decoration to add
1485      */
addItemDecoration(ItemDecoration decor)1486     public void addItemDecoration(ItemDecoration decor) {
1487         addItemDecoration(decor, -1);
1488     }
1489 
1490     /**
1491      * Returns an {@link ItemDecoration} previously added to this RecyclerView.
1492      *
1493      * @param index The index position of the desired ItemDecoration.
1494      * @return the ItemDecoration at index position, or null if invalid index.
1495      */
getItemDecorationAt(int index)1496     public ItemDecoration getItemDecorationAt(int index) {
1497         if (index < 0 || index >= mItemDecorations.size()) {
1498             return null;
1499         }
1500 
1501         return mItemDecorations.get(index);
1502     }
1503 
1504     /**
1505      * Remove an {@link ItemDecoration} from this RecyclerView.
1506      *
1507      * <p>The given decoration will no longer impact the measurement and drawing of
1508      * item views.</p>
1509      *
1510      * @param decor Decoration to remove
1511      * @see #addItemDecoration(ItemDecoration)
1512      */
removeItemDecoration(ItemDecoration decor)1513     public void removeItemDecoration(ItemDecoration decor) {
1514         if (mLayout != null) {
1515             mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
1516                     + " layout");
1517         }
1518         mItemDecorations.remove(decor);
1519         if (mItemDecorations.isEmpty()) {
1520             setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
1521         }
1522         markItemDecorInsetsDirty();
1523         requestLayout();
1524     }
1525 
1526     /**
1527      * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
1528      * <p>
1529      * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
1530      * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
1531      * true if childDrawingOrderCallback is not null, false otherwise.
1532      * <p>
1533      * Note that child drawing order may be overridden by View's elevation.
1534      *
1535      * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
1536      *                                  system.
1537      */
setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback)1538     public void setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback) {
1539         if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
1540             return;
1541         }
1542         mChildDrawingOrderCallback = childDrawingOrderCallback;
1543         setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
1544     }
1545 
1546     /**
1547      * Set a listener that will be notified of any changes in scroll state or position.
1548      *
1549      * @param listener Listener to set or null to clear
1550      *
1551      * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
1552      *             {@link #removeOnScrollListener(OnScrollListener)}
1553      */
1554     @Deprecated
setOnScrollListener(OnScrollListener listener)1555     public void setOnScrollListener(OnScrollListener listener) {
1556         mScrollListener = listener;
1557     }
1558 
1559     /**
1560      * Add a listener that will be notified of any changes in scroll state or position.
1561      *
1562      * <p>Components that add a listener should take care to remove it when finished.
1563      * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
1564      * to remove all attached listeners.</p>
1565      *
1566      * @param listener listener to set or null to clear
1567      */
addOnScrollListener(OnScrollListener listener)1568     public void addOnScrollListener(OnScrollListener listener) {
1569         if (mScrollListeners == null) {
1570             mScrollListeners = new ArrayList<>();
1571         }
1572         mScrollListeners.add(listener);
1573     }
1574 
1575     /**
1576      * Remove a listener that was notified of any changes in scroll state or position.
1577      *
1578      * @param listener listener to set or null to clear
1579      */
removeOnScrollListener(OnScrollListener listener)1580     public void removeOnScrollListener(OnScrollListener listener) {
1581         if (mScrollListeners != null) {
1582             mScrollListeners.remove(listener);
1583         }
1584     }
1585 
1586     /**
1587      * Remove all secondary listener that were notified of any changes in scroll state or position.
1588      */
clearOnScrollListeners()1589     public void clearOnScrollListeners() {
1590         if (mScrollListeners != null) {
1591             mScrollListeners.clear();
1592         }
1593     }
1594 
1595     /**
1596      * Convenience method to scroll to a certain position.
1597      *
1598      * RecyclerView does not implement scrolling logic, rather forwards the call to
1599      * {@link android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
1600      * @param position Scroll to this adapter position
1601      * @see android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)
1602      */
scrollToPosition(int position)1603     public void scrollToPosition(int position) {
1604         if (mLayoutFrozen) {
1605             return;
1606         }
1607         stopScroll();
1608         if (mLayout == null) {
1609             Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
1610                     + "Call setLayoutManager with a non-null argument.");
1611             return;
1612         }
1613         mLayout.scrollToPosition(position);
1614         awakenScrollBars();
1615     }
1616 
jumpToPositionForSmoothScroller(int position)1617     void jumpToPositionForSmoothScroller(int position) {
1618         if (mLayout == null) {
1619             return;
1620         }
1621         mLayout.scrollToPosition(position);
1622         awakenScrollBars();
1623     }
1624 
1625     /**
1626      * Starts a smooth scroll to an adapter position.
1627      * <p>
1628      * To support smooth scrolling, you must override
1629      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
1630      * {@link SmoothScroller}.
1631      * <p>
1632      * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
1633      * provide a custom smooth scroll logic, override
1634      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
1635      * LayoutManager.
1636      *
1637      * @param position The adapter position to scroll to
1638      * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
1639      */
smoothScrollToPosition(int position)1640     public void smoothScrollToPosition(int position) {
1641         if (mLayoutFrozen) {
1642             return;
1643         }
1644         if (mLayout == null) {
1645             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
1646                     + "Call setLayoutManager with a non-null argument.");
1647             return;
1648         }
1649         mLayout.smoothScrollToPosition(this, mState, position);
1650     }
1651 
1652     @Override
scrollTo(int x, int y)1653     public void scrollTo(int x, int y) {
1654         Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
1655                 + "Use scrollToPosition instead");
1656     }
1657 
1658     @Override
scrollBy(int x, int y)1659     public void scrollBy(int x, int y) {
1660         if (mLayout == null) {
1661             Log.e(TAG, "Cannot scroll without a LayoutManager set. "
1662                     + "Call setLayoutManager with a non-null argument.");
1663             return;
1664         }
1665         if (mLayoutFrozen) {
1666             return;
1667         }
1668         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1669         final boolean canScrollVertical = mLayout.canScrollVertically();
1670         if (canScrollHorizontal || canScrollVertical) {
1671             scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
1672         }
1673     }
1674 
1675     /**
1676      * Helper method reflect data changes to the state.
1677      * <p>
1678      * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
1679      * but data actually changed.
1680      * <p>
1681      * This method consumes all deferred changes to avoid that case.
1682      */
consumePendingUpdateOperations()1683     void consumePendingUpdateOperations() {
1684         if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
1685             TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1686             dispatchLayout();
1687             TraceCompat.endSection();
1688             return;
1689         }
1690         if (!mAdapterHelper.hasPendingUpdates()) {
1691             return;
1692         }
1693 
1694         // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
1695         // of the visible items is affected and if not, just ignore the change.
1696         if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
1697                 .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
1698                         | AdapterHelper.UpdateOp.MOVE)) {
1699             TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
1700             eatRequestLayout();
1701             onEnterLayoutOrScroll();
1702             mAdapterHelper.preProcess();
1703             if (!mLayoutRequestEaten) {
1704                 if (hasUpdatedView()) {
1705                     dispatchLayout();
1706                 } else {
1707                     // no need to layout, clean state
1708                     mAdapterHelper.consumePostponedUpdates();
1709                 }
1710             }
1711             resumeRequestLayout(true);
1712             onExitLayoutOrScroll();
1713             TraceCompat.endSection();
1714         } else if (mAdapterHelper.hasPendingUpdates()) {
1715             TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1716             dispatchLayout();
1717             TraceCompat.endSection();
1718         }
1719     }
1720 
1721     /**
1722      * @return True if an existing view holder needs to be updated
1723      */
hasUpdatedView()1724     private boolean hasUpdatedView() {
1725         final int childCount = mChildHelper.getChildCount();
1726         for (int i = 0; i < childCount; i++) {
1727             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
1728             if (holder == null || holder.shouldIgnore()) {
1729                 continue;
1730             }
1731             if (holder.isUpdated()) {
1732                 return true;
1733             }
1734         }
1735         return false;
1736     }
1737 
1738     /**
1739      * Does not perform bounds checking. Used by internal methods that have already validated input.
1740      * <p>
1741      * It also reports any unused scroll request to the related EdgeEffect.
1742      *
1743      * @param x The amount of horizontal scroll request
1744      * @param y The amount of vertical scroll request
1745      * @param ev The originating MotionEvent, or null if not from a touch event.
1746      *
1747      * @return Whether any scroll was consumed in either direction.
1748      */
scrollByInternal(int x, int y, MotionEvent ev)1749     boolean scrollByInternal(int x, int y, MotionEvent ev) {
1750         int unconsumedX = 0, unconsumedY = 0;
1751         int consumedX = 0, consumedY = 0;
1752 
1753         consumePendingUpdateOperations();
1754         if (mAdapter != null) {
1755             eatRequestLayout();
1756             onEnterLayoutOrScroll();
1757             TraceCompat.beginSection(TRACE_SCROLL_TAG);
1758             fillRemainingScrollValues(mState);
1759             if (x != 0) {
1760                 consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
1761                 unconsumedX = x - consumedX;
1762             }
1763             if (y != 0) {
1764                 consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
1765                 unconsumedY = y - consumedY;
1766             }
1767             TraceCompat.endSection();
1768             repositionShadowingViews();
1769             onExitLayoutOrScroll();
1770             resumeRequestLayout(false);
1771         }
1772         if (!mItemDecorations.isEmpty()) {
1773             invalidate();
1774         }
1775 
1776         if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset,
1777                 TYPE_TOUCH)) {
1778             // Update the last touch co-ords, taking any scroll offset into account
1779             mLastTouchX -= mScrollOffset[0];
1780             mLastTouchY -= mScrollOffset[1];
1781             if (ev != null) {
1782                 ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
1783             }
1784             mNestedOffsets[0] += mScrollOffset[0];
1785             mNestedOffsets[1] += mScrollOffset[1];
1786         } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
1787             if (ev != null && !MotionEventCompat.isFromSource(ev, InputDevice.SOURCE_MOUSE)) {
1788                 pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
1789             }
1790             considerReleasingGlowsOnScroll(x, y);
1791         }
1792         if (consumedX != 0 || consumedY != 0) {
1793             dispatchOnScrolled(consumedX, consumedY);
1794         }
1795         if (!awakenScrollBars()) {
1796             invalidate();
1797         }
1798         return consumedX != 0 || consumedY != 0;
1799     }
1800 
1801     /**
1802      * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
1803      * range. This value is used to compute the length of the thumb within the scrollbar's track.
1804      * </p>
1805      *
1806      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1807      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
1808      *
1809      * <p>Default implementation returns 0.</p>
1810      *
1811      * <p>If you want to support scroll bars, override
1812      * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
1813      * LayoutManager. </p>
1814      *
1815      * @return The horizontal offset of the scrollbar's thumb
1816      * @see android.support.v7.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset
1817      * (RecyclerView.State)
1818      */
1819     @Override
computeHorizontalScrollOffset()1820     public int computeHorizontalScrollOffset() {
1821         if (mLayout == null) {
1822             return 0;
1823         }
1824         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
1825     }
1826 
1827     /**
1828      * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
1829      * horizontal range. This value is used to compute the length of the thumb within the
1830      * scrollbar's track.</p>
1831      *
1832      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1833      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
1834      *
1835      * <p>Default implementation returns 0.</p>
1836      *
1837      * <p>If you want to support scroll bars, override
1838      * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
1839      * LayoutManager.</p>
1840      *
1841      * @return The horizontal extent of the scrollbar's thumb
1842      * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
1843      */
1844     @Override
computeHorizontalScrollExtent()1845     public int computeHorizontalScrollExtent() {
1846         if (mLayout == null) {
1847             return 0;
1848         }
1849         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
1850     }
1851 
1852     /**
1853      * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
1854      *
1855      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1856      * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
1857      *
1858      * <p>Default implementation returns 0.</p>
1859      *
1860      * <p>If you want to support scroll bars, override
1861      * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
1862      * LayoutManager.</p>
1863      *
1864      * @return The total horizontal range represented by the vertical scrollbar
1865      * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
1866      */
1867     @Override
computeHorizontalScrollRange()1868     public int computeHorizontalScrollRange() {
1869         if (mLayout == null) {
1870             return 0;
1871         }
1872         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
1873     }
1874 
1875     /**
1876      * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
1877      * This value is used to compute the length of the thumb within the scrollbar's track. </p>
1878      *
1879      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1880      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
1881      *
1882      * <p>Default implementation returns 0.</p>
1883      *
1884      * <p>If you want to support scroll bars, override
1885      * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
1886      * LayoutManager.</p>
1887      *
1888      * @return The vertical offset of the scrollbar's thumb
1889      * @see android.support.v7.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset
1890      * (RecyclerView.State)
1891      */
1892     @Override
computeVerticalScrollOffset()1893     public int computeVerticalScrollOffset() {
1894         if (mLayout == null) {
1895             return 0;
1896         }
1897         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
1898     }
1899 
1900     /**
1901      * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
1902      * This value is used to compute the length of the thumb within the scrollbar's track.</p>
1903      *
1904      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1905      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
1906      *
1907      * <p>Default implementation returns 0.</p>
1908      *
1909      * <p>If you want to support scroll bars, override
1910      * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
1911      * LayoutManager.</p>
1912      *
1913      * @return The vertical extent of the scrollbar's thumb
1914      * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
1915      */
1916     @Override
computeVerticalScrollExtent()1917     public int computeVerticalScrollExtent() {
1918         if (mLayout == null) {
1919             return 0;
1920         }
1921         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
1922     }
1923 
1924     /**
1925      * <p>Compute the vertical range that the vertical scrollbar represents.</p>
1926      *
1927      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1928      * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
1929      *
1930      * <p>Default implementation returns 0.</p>
1931      *
1932      * <p>If you want to support scroll bars, override
1933      * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
1934      * LayoutManager.</p>
1935      *
1936      * @return The total vertical range represented by the vertical scrollbar
1937      * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
1938      */
1939     @Override
computeVerticalScrollRange()1940     public int computeVerticalScrollRange() {
1941         if (mLayout == null) {
1942             return 0;
1943         }
1944         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
1945     }
1946 
1947 
eatRequestLayout()1948     void eatRequestLayout() {
1949         mEatRequestLayout++;
1950         if (mEatRequestLayout == 1 && !mLayoutFrozen) {
1951             mLayoutRequestEaten = false;
1952         }
1953     }
1954 
resumeRequestLayout(boolean performLayoutChildren)1955     void resumeRequestLayout(boolean performLayoutChildren) {
1956         if (mEatRequestLayout < 1) {
1957             //noinspection PointlessBooleanExpression
1958             if (DEBUG) {
1959                 throw new IllegalStateException("invalid eat request layout count"
1960                         + exceptionLabel());
1961             }
1962             mEatRequestLayout = 1;
1963         }
1964         if (!performLayoutChildren) {
1965             // Reset the layout request eaten counter.
1966             // This is necessary since eatRequest calls can be nested in which case the other
1967             // call will override the inner one.
1968             // for instance:
1969             // eat layout for process adapter updates
1970             //   eat layout for dispatchLayout
1971             //     a bunch of req layout calls arrive
1972 
1973             mLayoutRequestEaten = false;
1974         }
1975         if (mEatRequestLayout == 1) {
1976             // when layout is frozen we should delay dispatchLayout()
1977             if (performLayoutChildren && mLayoutRequestEaten && !mLayoutFrozen
1978                     && mLayout != null && mAdapter != null) {
1979                 dispatchLayout();
1980             }
1981             if (!mLayoutFrozen) {
1982                 mLayoutRequestEaten = false;
1983             }
1984         }
1985         mEatRequestLayout--;
1986     }
1987 
1988     /**
1989      * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
1990      * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
1991      * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
1992      * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
1993      * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
1994      * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
1995      * called.
1996      *
1997      * <p>
1998      * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
1999      * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
2000      * RecyclerView, State, int)}.
2001      * <p>
2002      * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
2003      * stop frozen.
2004      * <p>
2005      * Note: Running ItemAnimator is not stopped automatically,  it's caller's
2006      * responsibility to call ItemAnimator.end().
2007      *
2008      * @param frozen   true to freeze layout and scroll, false to re-enable.
2009      */
setLayoutFrozen(boolean frozen)2010     public void setLayoutFrozen(boolean frozen) {
2011         if (frozen != mLayoutFrozen) {
2012             assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
2013             if (!frozen) {
2014                 mLayoutFrozen = false;
2015                 if (mLayoutRequestEaten && mLayout != null && mAdapter != null) {
2016                     requestLayout();
2017                 }
2018                 mLayoutRequestEaten = false;
2019             } else {
2020                 final long now = SystemClock.uptimeMillis();
2021                 MotionEvent cancelEvent = MotionEvent.obtain(now, now,
2022                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2023                 onTouchEvent(cancelEvent);
2024                 mLayoutFrozen = true;
2025                 mIgnoreMotionEventTillDown = true;
2026                 stopScroll();
2027             }
2028         }
2029     }
2030 
2031     /**
2032      * Returns true if layout and scroll are frozen.
2033      *
2034      * @return true if layout and scroll are frozen
2035      * @see #setLayoutFrozen(boolean)
2036      */
isLayoutFrozen()2037     public boolean isLayoutFrozen() {
2038         return mLayoutFrozen;
2039     }
2040 
2041     /**
2042      * Animate a scroll by the given amount of pixels along either axis.
2043      *
2044      * @param dx Pixels to scroll horizontally
2045      * @param dy Pixels to scroll vertically
2046      */
smoothScrollBy(int dx, int dy)2047     public void smoothScrollBy(int dx, int dy) {
2048         smoothScrollBy(dx, dy, null);
2049     }
2050 
2051     /**
2052      * Animate a scroll by the given amount of pixels along either axis.
2053      *
2054      * @param dx Pixels to scroll horizontally
2055      * @param dy Pixels to scroll vertically
2056      * @param interpolator {@link Interpolator} to be used for scrolling. If it is
2057      *                     {@code null}, RecyclerView is going to use the default interpolator.
2058      */
smoothScrollBy(int dx, int dy, Interpolator interpolator)2059     public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
2060         if (mLayout == null) {
2061             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
2062                     + "Call setLayoutManager with a non-null argument.");
2063             return;
2064         }
2065         if (mLayoutFrozen) {
2066             return;
2067         }
2068         if (!mLayout.canScrollHorizontally()) {
2069             dx = 0;
2070         }
2071         if (!mLayout.canScrollVertically()) {
2072             dy = 0;
2073         }
2074         if (dx != 0 || dy != 0) {
2075             mViewFlinger.smoothScrollBy(dx, dy, interpolator);
2076         }
2077     }
2078 
2079     /**
2080      * Begin a standard fling with an initial velocity along each axis in pixels per second.
2081      * If the velocity given is below the system-defined minimum this method will return false
2082      * and no fling will occur.
2083      *
2084      * @param velocityX Initial horizontal velocity in pixels per second
2085      * @param velocityY Initial vertical velocity in pixels per second
2086      * @return true if the fling was started, false if the velocity was too low to fling or
2087      * LayoutManager does not support scrolling in the axis fling is issued.
2088      *
2089      * @see LayoutManager#canScrollVertically()
2090      * @see LayoutManager#canScrollHorizontally()
2091      */
fling(int velocityX, int velocityY)2092     public boolean fling(int velocityX, int velocityY) {
2093         if (mLayout == null) {
2094             Log.e(TAG, "Cannot fling without a LayoutManager set. "
2095                     + "Call setLayoutManager with a non-null argument.");
2096             return false;
2097         }
2098         if (mLayoutFrozen) {
2099             return false;
2100         }
2101 
2102         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
2103         final boolean canScrollVertical = mLayout.canScrollVertically();
2104 
2105         if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
2106             velocityX = 0;
2107         }
2108         if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
2109             velocityY = 0;
2110         }
2111         if (velocityX == 0 && velocityY == 0) {
2112             // If we don't have any velocity, return false
2113             return false;
2114         }
2115 
2116         if (!dispatchNestedPreFling(velocityX, velocityY)) {
2117             final boolean canScroll = canScrollHorizontal || canScrollVertical;
2118             dispatchNestedFling(velocityX, velocityY, canScroll);
2119 
2120             if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
2121                 return true;
2122             }
2123 
2124             if (canScroll) {
2125                 int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2126                 if (canScrollHorizontal) {
2127                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2128                 }
2129                 if (canScrollVertical) {
2130                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2131                 }
2132                 startNestedScroll(nestedScrollAxis, TYPE_NON_TOUCH);
2133 
2134                 velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
2135                 velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
2136                 mViewFlinger.fling(velocityX, velocityY);
2137                 return true;
2138             }
2139         }
2140         return false;
2141     }
2142 
2143     /**
2144      * Stop any current scroll in progress, such as one started by
2145      * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
2146      */
stopScroll()2147     public void stopScroll() {
2148         setScrollState(SCROLL_STATE_IDLE);
2149         stopScrollersInternal();
2150     }
2151 
2152     /**
2153      * Similar to {@link #stopScroll()} but does not set the state.
2154      */
stopScrollersInternal()2155     private void stopScrollersInternal() {
2156         mViewFlinger.stop();
2157         if (mLayout != null) {
2158             mLayout.stopSmoothScroller();
2159         }
2160     }
2161 
2162     /**
2163      * Returns the minimum velocity to start a fling.
2164      *
2165      * @return The minimum velocity to start a fling
2166      */
getMinFlingVelocity()2167     public int getMinFlingVelocity() {
2168         return mMinFlingVelocity;
2169     }
2170 
2171 
2172     /**
2173      * Returns the maximum fling velocity used by this RecyclerView.
2174      *
2175      * @return The maximum fling velocity used by this RecyclerView.
2176      */
getMaxFlingVelocity()2177     public int getMaxFlingVelocity() {
2178         return mMaxFlingVelocity;
2179     }
2180 
2181     /**
2182      * Apply a pull to relevant overscroll glow effects
2183      */
pullGlows(float x, float overscrollX, float y, float overscrollY)2184     private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
2185         boolean invalidate = false;
2186         if (overscrollX < 0) {
2187             ensureLeftGlow();
2188             EdgeEffectCompat.onPull(mLeftGlow, -overscrollX / getWidth(), 1f - y  / getHeight());
2189             invalidate = true;
2190         } else if (overscrollX > 0) {
2191             ensureRightGlow();
2192             EdgeEffectCompat.onPull(mRightGlow, overscrollX / getWidth(), y / getHeight());
2193             invalidate = true;
2194         }
2195 
2196         if (overscrollY < 0) {
2197             ensureTopGlow();
2198             EdgeEffectCompat.onPull(mTopGlow, -overscrollY / getHeight(), x / getWidth());
2199             invalidate = true;
2200         } else if (overscrollY > 0) {
2201             ensureBottomGlow();
2202             EdgeEffectCompat.onPull(mBottomGlow, overscrollY / getHeight(), 1f - x / getWidth());
2203             invalidate = true;
2204         }
2205 
2206         if (invalidate || overscrollX != 0 || overscrollY != 0) {
2207             ViewCompat.postInvalidateOnAnimation(this);
2208         }
2209     }
2210 
releaseGlows()2211     private void releaseGlows() {
2212         boolean needsInvalidate = false;
2213         if (mLeftGlow != null) {
2214             mLeftGlow.onRelease();
2215             needsInvalidate = mLeftGlow.isFinished();
2216         }
2217         if (mTopGlow != null) {
2218             mTopGlow.onRelease();
2219             needsInvalidate |= mTopGlow.isFinished();
2220         }
2221         if (mRightGlow != null) {
2222             mRightGlow.onRelease();
2223             needsInvalidate |= mRightGlow.isFinished();
2224         }
2225         if (mBottomGlow != null) {
2226             mBottomGlow.onRelease();
2227             needsInvalidate |= mBottomGlow.isFinished();
2228         }
2229         if (needsInvalidate) {
2230             ViewCompat.postInvalidateOnAnimation(this);
2231         }
2232     }
2233 
considerReleasingGlowsOnScroll(int dx, int dy)2234     void considerReleasingGlowsOnScroll(int dx, int dy) {
2235         boolean needsInvalidate = false;
2236         if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
2237             mLeftGlow.onRelease();
2238             needsInvalidate = mLeftGlow.isFinished();
2239         }
2240         if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
2241             mRightGlow.onRelease();
2242             needsInvalidate |= mRightGlow.isFinished();
2243         }
2244         if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
2245             mTopGlow.onRelease();
2246             needsInvalidate |= mTopGlow.isFinished();
2247         }
2248         if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
2249             mBottomGlow.onRelease();
2250             needsInvalidate |= mBottomGlow.isFinished();
2251         }
2252         if (needsInvalidate) {
2253             ViewCompat.postInvalidateOnAnimation(this);
2254         }
2255     }
2256 
absorbGlows(int velocityX, int velocityY)2257     void absorbGlows(int velocityX, int velocityY) {
2258         if (velocityX < 0) {
2259             ensureLeftGlow();
2260             mLeftGlow.onAbsorb(-velocityX);
2261         } else if (velocityX > 0) {
2262             ensureRightGlow();
2263             mRightGlow.onAbsorb(velocityX);
2264         }
2265 
2266         if (velocityY < 0) {
2267             ensureTopGlow();
2268             mTopGlow.onAbsorb(-velocityY);
2269         } else if (velocityY > 0) {
2270             ensureBottomGlow();
2271             mBottomGlow.onAbsorb(velocityY);
2272         }
2273 
2274         if (velocityX != 0 || velocityY != 0) {
2275             ViewCompat.postInvalidateOnAnimation(this);
2276         }
2277     }
2278 
ensureLeftGlow()2279     void ensureLeftGlow() {
2280         if (mLeftGlow != null) {
2281             return;
2282         }
2283         mLeftGlow = new EdgeEffect(getContext());
2284         if (mClipToPadding) {
2285             mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2286                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2287         } else {
2288             mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2289         }
2290     }
2291 
ensureRightGlow()2292     void ensureRightGlow() {
2293         if (mRightGlow != null) {
2294             return;
2295         }
2296         mRightGlow = new EdgeEffect(getContext());
2297         if (mClipToPadding) {
2298             mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2299                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2300         } else {
2301             mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2302         }
2303     }
2304 
ensureTopGlow()2305     void ensureTopGlow() {
2306         if (mTopGlow != null) {
2307             return;
2308         }
2309         mTopGlow = new EdgeEffect(getContext());
2310         if (mClipToPadding) {
2311             mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2312                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2313         } else {
2314             mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2315         }
2316 
2317     }
2318 
ensureBottomGlow()2319     void ensureBottomGlow() {
2320         if (mBottomGlow != null) {
2321             return;
2322         }
2323         mBottomGlow = new EdgeEffect(getContext());
2324         if (mClipToPadding) {
2325             mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2326                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2327         } else {
2328             mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2329         }
2330     }
2331 
invalidateGlows()2332     void invalidateGlows() {
2333         mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
2334     }
2335 
2336     /**
2337      * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
2338      * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
2339      * that differs from other ViewGroups.
2340      * <p>
2341      * It first does a focus search within the RecyclerView. If this search finds a View that is in
2342      * the focus direction with respect to the currently focused View, RecyclerView returns that
2343      * child as the next focus target. When it cannot find such child, it calls
2344      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
2345      * in the focus search direction. If LayoutManager adds a View that matches the
2346      * focus search criteria, it will be returned as the focus search result. Otherwise,
2347      * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
2348      * <p>
2349      * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
2350      * is not in the focus direction is still valid focus target which may not be the desired
2351      * behavior if the Adapter has more children in the focus direction. To handle this case,
2352      * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
2353      * focus search in that direction. If there are no Views to gain focus, it will call
2354      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
2355      * focus search with the original (relative) direction. This allows RecyclerView to provide
2356      * better candidates to the focus search while still allowing the view system to take focus from
2357      * the RecyclerView and give it to a more suitable child if such child exists.
2358      *
2359      * @param focused The view that currently has focus
2360      * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
2361      * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
2362      * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
2363      *
2364      * @return A new View that can be the next focus after the focused View
2365      */
2366     @Override
focusSearch(View focused, int direction)2367     public View focusSearch(View focused, int direction) {
2368         View result = mLayout.onInterceptFocusSearch(focused, direction);
2369         if (result != null) {
2370             return result;
2371         }
2372         final boolean canRunFocusFailure = mAdapter != null && mLayout != null
2373                 && !isComputingLayout() && !mLayoutFrozen;
2374 
2375         final FocusFinder ff = FocusFinder.getInstance();
2376         if (canRunFocusFailure
2377                 && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
2378             // convert direction to absolute direction and see if we have a view there and if not
2379             // tell LayoutManager to add if it can.
2380             boolean needsFocusFailureLayout = false;
2381             if (mLayout.canScrollVertically()) {
2382                 final int absDir =
2383                         direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
2384                 final View found = ff.findNextFocus(this, focused, absDir);
2385                 needsFocusFailureLayout = found == null;
2386                 if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2387                     // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2388                     direction = absDir;
2389                 }
2390             }
2391             if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
2392                 boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
2393                 final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
2394                         ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2395                 final View found = ff.findNextFocus(this, focused, absDir);
2396                 needsFocusFailureLayout = found == null;
2397                 if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2398                     // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2399                     direction = absDir;
2400                 }
2401             }
2402             if (needsFocusFailureLayout) {
2403                 consumePendingUpdateOperations();
2404                 final View focusedItemView = findContainingItemView(focused);
2405                 if (focusedItemView == null) {
2406                     // panic, focused view is not a child anymore, cannot call super.
2407                     return null;
2408                 }
2409                 eatRequestLayout();
2410                 mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2411                 resumeRequestLayout(false);
2412             }
2413             result = ff.findNextFocus(this, focused, direction);
2414         } else {
2415             result = ff.findNextFocus(this, focused, direction);
2416             if (result == null && canRunFocusFailure) {
2417                 consumePendingUpdateOperations();
2418                 final View focusedItemView = findContainingItemView(focused);
2419                 if (focusedItemView == null) {
2420                     // panic, focused view is not a child anymore, cannot call super.
2421                     return null;
2422                 }
2423                 eatRequestLayout();
2424                 result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2425                 resumeRequestLayout(false);
2426             }
2427         }
2428         if (result != null && !result.hasFocusable()) {
2429             if (getFocusedChild() == null) {
2430                 // Scrolling to this unfocusable view is not meaningful since there is no currently
2431                 // focused view which RV needs to keep visible.
2432                 return super.focusSearch(focused, direction);
2433             }
2434             // If the next view returned by onFocusSearchFailed in layout manager has no focusable
2435             // views, we still scroll to that view in order to make it visible on the screen.
2436             // If it's focusable, framework already calls RV's requestChildFocus which handles
2437             // bringing this newly focused item onto the screen.
2438             requestChildOnScreen(result, null);
2439             return focused;
2440         }
2441         return isPreferredNextFocus(focused, result, direction)
2442                 ? result : super.focusSearch(focused, direction);
2443     }
2444 
2445     /**
2446      * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
2447      * assign it as the next focus View instead of letting view hierarchy decide.
2448      * A good candidate means a View that is aligned in the focus direction wrt the focused View
2449      * and is not the RecyclerView itself.
2450      * When this method returns false, RecyclerView will let the parent make the decision so the
2451      * same View may still get the focus as a result of that search.
2452      */
isPreferredNextFocus(View focused, View next, int direction)2453     private boolean isPreferredNextFocus(View focused, View next, int direction) {
2454         if (next == null || next == this) {
2455             return false;
2456         }
2457         if (focused == null) {
2458             return true;
2459         }
2460 
2461         if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
2462             final boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
2463             final int absHorizontal = (direction == View.FOCUS_FORWARD) ^ rtl
2464                     ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2465             if (isPreferredNextFocusAbsolute(focused, next, absHorizontal)) {
2466                 return true;
2467             }
2468             if (direction == View.FOCUS_FORWARD) {
2469                 return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_DOWN);
2470             } else {
2471                 return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_UP);
2472             }
2473         } else {
2474             return isPreferredNextFocusAbsolute(focused, next, direction);
2475         }
2476 
2477     }
2478 
2479     /**
2480      * Logic taken from FocusSearch#isCandidate
2481      */
isPreferredNextFocusAbsolute(View focused, View next, int direction)2482     private boolean isPreferredNextFocusAbsolute(View focused, View next, int direction) {
2483         mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2484         mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
2485         offsetDescendantRectToMyCoords(focused, mTempRect);
2486         offsetDescendantRectToMyCoords(next, mTempRect2);
2487         switch (direction) {
2488             case View.FOCUS_LEFT:
2489                 return (mTempRect.right > mTempRect2.right
2490                         || mTempRect.left >= mTempRect2.right)
2491                         && mTempRect.left > mTempRect2.left;
2492             case View.FOCUS_RIGHT:
2493                 return (mTempRect.left < mTempRect2.left
2494                         || mTempRect.right <= mTempRect2.left)
2495                         && mTempRect.right < mTempRect2.right;
2496             case View.FOCUS_UP:
2497                 return (mTempRect.bottom > mTempRect2.bottom
2498                         || mTempRect.top >= mTempRect2.bottom)
2499                         && mTempRect.top > mTempRect2.top;
2500             case View.FOCUS_DOWN:
2501                 return (mTempRect.top < mTempRect2.top
2502                         || mTempRect.bottom <= mTempRect2.top)
2503                         && mTempRect.bottom < mTempRect2.bottom;
2504         }
2505         throw new IllegalArgumentException("direction must be absolute. received:"
2506                 + direction + exceptionLabel());
2507     }
2508 
2509     @Override
requestChildFocus(View child, View focused)2510     public void requestChildFocus(View child, View focused) {
2511         if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
2512             requestChildOnScreen(child, focused);
2513         }
2514         super.requestChildFocus(child, focused);
2515     }
2516 
2517     /**
2518      * Requests that the given child of the RecyclerView be positioned onto the screen. This method
2519      * can be called for both unfocusable and focusable child views. For unfocusable child views,
2520      * the {@param focused} parameter passed is null, whereas for a focusable child, this parameter
2521      * indicates the actual descendant view within this child view that holds the focus.
2522      * @param child The child view of this RecyclerView that wants to come onto the screen.
2523      * @param focused The descendant view that actually has the focus if child is focusable, null
2524      *                otherwise.
2525      */
requestChildOnScreen(@onNull View child, @Nullable View focused)2526     private void requestChildOnScreen(@NonNull View child, @Nullable View focused) {
2527         View rectView = (focused != null) ? focused : child;
2528         mTempRect.set(0, 0, rectView.getWidth(), rectView.getHeight());
2529 
2530         // get item decor offsets w/o refreshing. If they are invalid, there will be another
2531         // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
2532         // View in viewport.
2533         final ViewGroup.LayoutParams focusedLayoutParams = rectView.getLayoutParams();
2534         if (focusedLayoutParams instanceof LayoutParams) {
2535             // if focused child has item decors, use them. Otherwise, ignore.
2536             final LayoutParams lp = (LayoutParams) focusedLayoutParams;
2537             if (!lp.mInsetsDirty) {
2538                 final Rect insets = lp.mDecorInsets;
2539                 mTempRect.left -= insets.left;
2540                 mTempRect.right += insets.right;
2541                 mTempRect.top -= insets.top;
2542                 mTempRect.bottom += insets.bottom;
2543             }
2544         }
2545 
2546         if (focused != null) {
2547             offsetDescendantRectToMyCoords(focused, mTempRect);
2548             offsetRectIntoDescendantCoords(child, mTempRect);
2549         }
2550         mLayout.requestChildRectangleOnScreen(this, child, mTempRect, !mFirstLayoutComplete,
2551                 (focused == null));
2552     }
2553 
2554     @Override
requestChildRectangleOnScreen(View child, Rect rect, boolean immediate)2555     public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
2556         return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
2557     }
2558 
2559     @Override
addFocusables(ArrayList<View> views, int direction, int focusableMode)2560     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
2561         if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
2562             super.addFocusables(views, direction, focusableMode);
2563         }
2564     }
2565 
2566     @Override
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)2567     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
2568         if (isComputingLayout()) {
2569             // if we are in the middle of a layout calculation, don't let any child take focus.
2570             // RV will handle it after layout calculation is finished.
2571             return false;
2572         }
2573         return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
2574     }
2575 
2576     @Override
onAttachedToWindow()2577     protected void onAttachedToWindow() {
2578         super.onAttachedToWindow();
2579         mLayoutOrScrollCounter = 0;
2580         mIsAttached = true;
2581         mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
2582         if (mLayout != null) {
2583             mLayout.dispatchAttachedToWindow(this);
2584         }
2585         mPostedAnimatorRunner = false;
2586 
2587         if (ALLOW_THREAD_GAP_WORK) {
2588             // Register with gap worker
2589             mGapWorker = GapWorker.sGapWorker.get();
2590             if (mGapWorker == null) {
2591                 mGapWorker = new GapWorker();
2592 
2593                 // break 60 fps assumption if data from display appears valid
2594                 // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
2595                 Display display = ViewCompat.getDisplay(this);
2596                 float refreshRate = 60.0f;
2597                 if (!isInEditMode() && display != null) {
2598                     float displayRefreshRate = display.getRefreshRate();
2599                     if (displayRefreshRate >= 30.0f) {
2600                         refreshRate = displayRefreshRate;
2601                     }
2602                 }
2603                 mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
2604                 GapWorker.sGapWorker.set(mGapWorker);
2605             }
2606             mGapWorker.add(this);
2607         }
2608     }
2609 
2610     @Override
onDetachedFromWindow()2611     protected void onDetachedFromWindow() {
2612         super.onDetachedFromWindow();
2613         if (mItemAnimator != null) {
2614             mItemAnimator.endAnimations();
2615         }
2616         stopScroll();
2617         mIsAttached = false;
2618         if (mLayout != null) {
2619             mLayout.dispatchDetachedFromWindow(this, mRecycler);
2620         }
2621         mPendingAccessibilityImportanceChange.clear();
2622         removeCallbacks(mItemAnimatorRunner);
2623         mViewInfoStore.onDetach();
2624 
2625         if (ALLOW_THREAD_GAP_WORK) {
2626             // Unregister with gap worker
2627             mGapWorker.remove(this);
2628             mGapWorker = null;
2629         }
2630     }
2631 
2632     /**
2633      * Returns true if RecyclerView is attached to window.
2634      */
2635     @Override
isAttachedToWindow()2636     public boolean isAttachedToWindow() {
2637         return mIsAttached;
2638     }
2639 
2640     /**
2641      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2642      * {@link IllegalStateException} if it <b>is not</b>.
2643      *
2644      * @param message The message for the exception. Can be null.
2645      * @see #assertNotInLayoutOrScroll(String)
2646      */
assertInLayoutOrScroll(String message)2647     void assertInLayoutOrScroll(String message) {
2648         if (!isComputingLayout()) {
2649             if (message == null) {
2650                 throw new IllegalStateException("Cannot call this method unless RecyclerView is "
2651                         + "computing a layout or scrolling" + exceptionLabel());
2652             }
2653             throw new IllegalStateException(message + exceptionLabel());
2654 
2655         }
2656     }
2657 
2658     /**
2659      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2660      * {@link IllegalStateException} if it <b>is</b>.
2661      *
2662      * @param message The message for the exception. Can be null.
2663      * @see #assertInLayoutOrScroll(String)
2664      */
assertNotInLayoutOrScroll(String message)2665     void assertNotInLayoutOrScroll(String message) {
2666         if (isComputingLayout()) {
2667             if (message == null) {
2668                 throw new IllegalStateException("Cannot call this method while RecyclerView is "
2669                         + "computing a layout or scrolling" + exceptionLabel());
2670             }
2671             throw new IllegalStateException(message);
2672         }
2673         if (mDispatchScrollCounter > 0) {
2674             Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might"
2675                             + "be run during a measure & layout pass where you cannot change the"
2676                             + "RecyclerView data. Any method call that might change the structure"
2677                             + "of the RecyclerView or the adapter contents should be postponed to"
2678                             + "the next frame.",
2679                     new IllegalStateException("" + exceptionLabel()));
2680         }
2681     }
2682 
2683     /**
2684      * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
2685      * to child views or this view's standard scrolling behavior.
2686      *
2687      * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
2688      * returns true from
2689      * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
2690      * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
2691      * for each incoming MotionEvent until the end of the gesture.</p>
2692      *
2693      * @param listener Listener to add
2694      * @see SimpleOnItemTouchListener
2695      */
addOnItemTouchListener(OnItemTouchListener listener)2696     public void addOnItemTouchListener(OnItemTouchListener listener) {
2697         mOnItemTouchListeners.add(listener);
2698     }
2699 
2700     /**
2701      * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
2702      *
2703      * @param listener Listener to remove
2704      */
removeOnItemTouchListener(OnItemTouchListener listener)2705     public void removeOnItemTouchListener(OnItemTouchListener listener) {
2706         mOnItemTouchListeners.remove(listener);
2707         if (mActiveOnItemTouchListener == listener) {
2708             mActiveOnItemTouchListener = null;
2709         }
2710     }
2711 
dispatchOnItemTouchIntercept(MotionEvent e)2712     private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
2713         final int action = e.getAction();
2714         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
2715             mActiveOnItemTouchListener = null;
2716         }
2717 
2718         final int listenerCount = mOnItemTouchListeners.size();
2719         for (int i = 0; i < listenerCount; i++) {
2720             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2721             if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
2722                 mActiveOnItemTouchListener = listener;
2723                 return true;
2724             }
2725         }
2726         return false;
2727     }
2728 
dispatchOnItemTouch(MotionEvent e)2729     private boolean dispatchOnItemTouch(MotionEvent e) {
2730         final int action = e.getAction();
2731         if (mActiveOnItemTouchListener != null) {
2732             if (action == MotionEvent.ACTION_DOWN) {
2733                 // Stale state from a previous gesture, we're starting a new one. Clear it.
2734                 mActiveOnItemTouchListener = null;
2735             } else {
2736                 mActiveOnItemTouchListener.onTouchEvent(this, e);
2737                 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
2738                     // Clean up for the next gesture.
2739                     mActiveOnItemTouchListener = null;
2740                 }
2741                 return true;
2742             }
2743         }
2744 
2745         // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
2746         // as called from onInterceptTouchEvent; skip it.
2747         if (action != MotionEvent.ACTION_DOWN) {
2748             final int listenerCount = mOnItemTouchListeners.size();
2749             for (int i = 0; i < listenerCount; i++) {
2750                 final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2751                 if (listener.onInterceptTouchEvent(this, e)) {
2752                     mActiveOnItemTouchListener = listener;
2753                     return true;
2754                 }
2755             }
2756         }
2757         return false;
2758     }
2759 
2760     @Override
onInterceptTouchEvent(MotionEvent e)2761     public boolean onInterceptTouchEvent(MotionEvent e) {
2762         if (mLayoutFrozen) {
2763             // When layout is frozen,  RV does not intercept the motion event.
2764             // A child view e.g. a button may still get the click.
2765             return false;
2766         }
2767         if (dispatchOnItemTouchIntercept(e)) {
2768             cancelTouch();
2769             return true;
2770         }
2771 
2772         if (mLayout == null) {
2773             return false;
2774         }
2775 
2776         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2777         final boolean canScrollVertically = mLayout.canScrollVertically();
2778 
2779         if (mVelocityTracker == null) {
2780             mVelocityTracker = VelocityTracker.obtain();
2781         }
2782         mVelocityTracker.addMovement(e);
2783 
2784         final int action = e.getActionMasked();
2785         final int actionIndex = e.getActionIndex();
2786 
2787         switch (action) {
2788             case MotionEvent.ACTION_DOWN:
2789                 if (mIgnoreMotionEventTillDown) {
2790                     mIgnoreMotionEventTillDown = false;
2791                 }
2792                 mScrollPointerId = e.getPointerId(0);
2793                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2794                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2795 
2796                 if (mScrollState == SCROLL_STATE_SETTLING) {
2797                     getParent().requestDisallowInterceptTouchEvent(true);
2798                     setScrollState(SCROLL_STATE_DRAGGING);
2799                 }
2800 
2801                 // Clear the nested offsets
2802                 mNestedOffsets[0] = mNestedOffsets[1] = 0;
2803 
2804                 int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2805                 if (canScrollHorizontally) {
2806                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2807                 }
2808                 if (canScrollVertically) {
2809                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2810                 }
2811                 startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
2812                 break;
2813 
2814             case MotionEvent.ACTION_POINTER_DOWN:
2815                 mScrollPointerId = e.getPointerId(actionIndex);
2816                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2817                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2818                 break;
2819 
2820             case MotionEvent.ACTION_MOVE: {
2821                 final int index = e.findPointerIndex(mScrollPointerId);
2822                 if (index < 0) {
2823                     Log.e(TAG, "Error processing scroll; pointer index for id "
2824                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2825                     return false;
2826                 }
2827 
2828                 final int x = (int) (e.getX(index) + 0.5f);
2829                 final int y = (int) (e.getY(index) + 0.5f);
2830                 if (mScrollState != SCROLL_STATE_DRAGGING) {
2831                     final int dx = x - mInitialTouchX;
2832                     final int dy = y - mInitialTouchY;
2833                     boolean startScroll = false;
2834                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2835                         mLastTouchX = x;
2836                         startScroll = true;
2837                     }
2838                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2839                         mLastTouchY = y;
2840                         startScroll = true;
2841                     }
2842                     if (startScroll) {
2843                         setScrollState(SCROLL_STATE_DRAGGING);
2844                     }
2845                 }
2846             } break;
2847 
2848             case MotionEvent.ACTION_POINTER_UP: {
2849                 onPointerUp(e);
2850             } break;
2851 
2852             case MotionEvent.ACTION_UP: {
2853                 mVelocityTracker.clear();
2854                 stopNestedScroll(TYPE_TOUCH);
2855             } break;
2856 
2857             case MotionEvent.ACTION_CANCEL: {
2858                 cancelTouch();
2859             }
2860         }
2861         return mScrollState == SCROLL_STATE_DRAGGING;
2862     }
2863 
2864     @Override
requestDisallowInterceptTouchEvent(boolean disallowIntercept)2865     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2866         final int listenerCount = mOnItemTouchListeners.size();
2867         for (int i = 0; i < listenerCount; i++) {
2868             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2869             listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
2870         }
2871         super.requestDisallowInterceptTouchEvent(disallowIntercept);
2872     }
2873 
2874     @Override
onTouchEvent(MotionEvent e)2875     public boolean onTouchEvent(MotionEvent e) {
2876         if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
2877             return false;
2878         }
2879         if (dispatchOnItemTouch(e)) {
2880             cancelTouch();
2881             return true;
2882         }
2883 
2884         if (mLayout == null) {
2885             return false;
2886         }
2887 
2888         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2889         final boolean canScrollVertically = mLayout.canScrollVertically();
2890 
2891         if (mVelocityTracker == null) {
2892             mVelocityTracker = VelocityTracker.obtain();
2893         }
2894         boolean eventAddedToVelocityTracker = false;
2895 
2896         final MotionEvent vtev = MotionEvent.obtain(e);
2897         final int action = e.getActionMasked();
2898         final int actionIndex = e.getActionIndex();
2899 
2900         if (action == MotionEvent.ACTION_DOWN) {
2901             mNestedOffsets[0] = mNestedOffsets[1] = 0;
2902         }
2903         vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
2904 
2905         switch (action) {
2906             case MotionEvent.ACTION_DOWN: {
2907                 mScrollPointerId = e.getPointerId(0);
2908                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2909                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2910 
2911                 int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2912                 if (canScrollHorizontally) {
2913                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2914                 }
2915                 if (canScrollVertically) {
2916                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2917                 }
2918                 startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
2919             } break;
2920 
2921             case MotionEvent.ACTION_POINTER_DOWN: {
2922                 mScrollPointerId = e.getPointerId(actionIndex);
2923                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2924                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2925             } break;
2926 
2927             case MotionEvent.ACTION_MOVE: {
2928                 final int index = e.findPointerIndex(mScrollPointerId);
2929                 if (index < 0) {
2930                     Log.e(TAG, "Error processing scroll; pointer index for id "
2931                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2932                     return false;
2933                 }
2934 
2935                 final int x = (int) (e.getX(index) + 0.5f);
2936                 final int y = (int) (e.getY(index) + 0.5f);
2937                 int dx = mLastTouchX - x;
2938                 int dy = mLastTouchY - y;
2939 
2940                 if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset, TYPE_TOUCH)) {
2941                     dx -= mScrollConsumed[0];
2942                     dy -= mScrollConsumed[1];
2943                     vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
2944                     // Updated the nested offsets
2945                     mNestedOffsets[0] += mScrollOffset[0];
2946                     mNestedOffsets[1] += mScrollOffset[1];
2947                 }
2948 
2949                 if (mScrollState != SCROLL_STATE_DRAGGING) {
2950                     boolean startScroll = false;
2951                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2952                         if (dx > 0) {
2953                             dx -= mTouchSlop;
2954                         } else {
2955                             dx += mTouchSlop;
2956                         }
2957                         startScroll = true;
2958                     }
2959                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2960                         if (dy > 0) {
2961                             dy -= mTouchSlop;
2962                         } else {
2963                             dy += mTouchSlop;
2964                         }
2965                         startScroll = true;
2966                     }
2967                     if (startScroll) {
2968                         setScrollState(SCROLL_STATE_DRAGGING);
2969                     }
2970                 }
2971 
2972                 if (mScrollState == SCROLL_STATE_DRAGGING) {
2973                     mLastTouchX = x - mScrollOffset[0];
2974                     mLastTouchY = y - mScrollOffset[1];
2975 
2976                     if (scrollByInternal(
2977                             canScrollHorizontally ? dx : 0,
2978                             canScrollVertically ? dy : 0,
2979                             vtev)) {
2980                         getParent().requestDisallowInterceptTouchEvent(true);
2981                     }
2982                     if (mGapWorker != null && (dx != 0 || dy != 0)) {
2983                         mGapWorker.postFromTraversal(this, dx, dy);
2984                     }
2985                 }
2986             } break;
2987 
2988             case MotionEvent.ACTION_POINTER_UP: {
2989                 onPointerUp(e);
2990             } break;
2991 
2992             case MotionEvent.ACTION_UP: {
2993                 mVelocityTracker.addMovement(vtev);
2994                 eventAddedToVelocityTracker = true;
2995                 mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
2996                 final float xvel = canScrollHorizontally
2997                         ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
2998                 final float yvel = canScrollVertically
2999                         ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
3000                 if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
3001                     setScrollState(SCROLL_STATE_IDLE);
3002                 }
3003                 resetTouch();
3004             } break;
3005 
3006             case MotionEvent.ACTION_CANCEL: {
3007                 cancelTouch();
3008             } break;
3009         }
3010 
3011         if (!eventAddedToVelocityTracker) {
3012             mVelocityTracker.addMovement(vtev);
3013         }
3014         vtev.recycle();
3015 
3016         return true;
3017     }
3018 
resetTouch()3019     private void resetTouch() {
3020         if (mVelocityTracker != null) {
3021             mVelocityTracker.clear();
3022         }
3023         stopNestedScroll(TYPE_TOUCH);
3024         releaseGlows();
3025     }
3026 
cancelTouch()3027     private void cancelTouch() {
3028         resetTouch();
3029         setScrollState(SCROLL_STATE_IDLE);
3030     }
3031 
onPointerUp(MotionEvent e)3032     private void onPointerUp(MotionEvent e) {
3033         final int actionIndex = e.getActionIndex();
3034         if (e.getPointerId(actionIndex) == mScrollPointerId) {
3035             // Pick a new pointer to pick up the slack.
3036             final int newIndex = actionIndex == 0 ? 1 : 0;
3037             mScrollPointerId = e.getPointerId(newIndex);
3038             mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
3039             mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
3040         }
3041     }
3042 
3043     @Override
onGenericMotionEvent(MotionEvent event)3044     public boolean onGenericMotionEvent(MotionEvent event) {
3045         if (mLayout == null) {
3046             return false;
3047         }
3048         if (mLayoutFrozen) {
3049             return false;
3050         }
3051         if (event.getAction() == MotionEventCompat.ACTION_SCROLL) {
3052             final float vScroll, hScroll;
3053             if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
3054                 if (mLayout.canScrollVertically()) {
3055                     // Inverse the sign of the vertical scroll to align the scroll orientation
3056                     // with AbsListView.
3057                     vScroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
3058                 } else {
3059                     vScroll = 0f;
3060                 }
3061                 if (mLayout.canScrollHorizontally()) {
3062                     hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
3063                 } else {
3064                     hScroll = 0f;
3065                 }
3066             } else if ((event.getSource() & InputDeviceCompat.SOURCE_ROTARY_ENCODER) != 0) {
3067                 final float axisScroll = event.getAxisValue(MotionEventCompat.AXIS_SCROLL);
3068                 if (mLayout.canScrollVertically()) {
3069                     // Invert the sign of the vertical scroll to align the scroll orientation
3070                     // with AbsListView.
3071                     vScroll = -axisScroll;
3072                     hScroll = 0f;
3073                 } else if (mLayout.canScrollHorizontally()) {
3074                     vScroll = 0f;
3075                     hScroll = axisScroll;
3076                 } else {
3077                     vScroll = 0f;
3078                     hScroll = 0f;
3079                 }
3080             } else {
3081                 vScroll = 0f;
3082                 hScroll = 0f;
3083             }
3084 
3085             if (vScroll != 0 || hScroll != 0) {
3086                 scrollByInternal((int) (hScroll * mScaledHorizontalScrollFactor),
3087                         (int) (vScroll * mScaledVerticalScrollFactor), event);
3088             }
3089         }
3090         return false;
3091     }
3092 
3093     @Override
onMeasure(int widthSpec, int heightSpec)3094     protected void onMeasure(int widthSpec, int heightSpec) {
3095         if (mLayout == null) {
3096             defaultOnMeasure(widthSpec, heightSpec);
3097             return;
3098         }
3099         if (mLayout.mAutoMeasure) {
3100             final int widthMode = MeasureSpec.getMode(widthSpec);
3101             final int heightMode = MeasureSpec.getMode(heightSpec);
3102             final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
3103                     && heightMode == MeasureSpec.EXACTLY;
3104             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3105             if (skipMeasure || mAdapter == null) {
3106                 return;
3107             }
3108             if (mState.mLayoutStep == State.STEP_START) {
3109                 dispatchLayoutStep1();
3110             }
3111             // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
3112             // consistency
3113             mLayout.setMeasureSpecs(widthSpec, heightSpec);
3114             mState.mIsMeasuring = true;
3115             dispatchLayoutStep2();
3116 
3117             // now we can get the width and height from the children.
3118             mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
3119 
3120             // if RecyclerView has non-exact width and height and if there is at least one child
3121             // which also has non-exact width & height, we have to re-measure.
3122             if (mLayout.shouldMeasureTwice()) {
3123                 mLayout.setMeasureSpecs(
3124                         MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
3125                         MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
3126                 mState.mIsMeasuring = true;
3127                 dispatchLayoutStep2();
3128                 // now we can get the width and height from the children.
3129                 mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
3130             }
3131         } else {
3132             if (mHasFixedSize) {
3133                 mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3134                 return;
3135             }
3136             // custom onMeasure
3137             if (mAdapterUpdateDuringMeasure) {
3138                 eatRequestLayout();
3139                 onEnterLayoutOrScroll();
3140                 processAdapterUpdatesAndSetAnimationFlags();
3141                 onExitLayoutOrScroll();
3142 
3143                 if (mState.mRunPredictiveAnimations) {
3144                     mState.mInPreLayout = true;
3145                 } else {
3146                     // consume remaining updates to provide a consistent state with the layout pass.
3147                     mAdapterHelper.consumeUpdatesInOnePass();
3148                     mState.mInPreLayout = false;
3149                 }
3150                 mAdapterUpdateDuringMeasure = false;
3151                 resumeRequestLayout(false);
3152             }
3153 
3154             if (mAdapter != null) {
3155                 mState.mItemCount = mAdapter.getItemCount();
3156             } else {
3157                 mState.mItemCount = 0;
3158             }
3159             eatRequestLayout();
3160             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3161             resumeRequestLayout(false);
3162             mState.mInPreLayout = false; // clear
3163         }
3164     }
3165 
3166     /**
3167      * Used when onMeasure is called before layout manager is set
3168      */
defaultOnMeasure(int widthSpec, int heightSpec)3169     void defaultOnMeasure(int widthSpec, int heightSpec) {
3170         // calling LayoutManager here is not pretty but that API is already public and it is better
3171         // than creating another method since this is internal.
3172         final int width = LayoutManager.chooseSize(widthSpec,
3173                 getPaddingLeft() + getPaddingRight(),
3174                 ViewCompat.getMinimumWidth(this));
3175         final int height = LayoutManager.chooseSize(heightSpec,
3176                 getPaddingTop() + getPaddingBottom(),
3177                 ViewCompat.getMinimumHeight(this));
3178 
3179         setMeasuredDimension(width, height);
3180     }
3181 
3182     @Override
onSizeChanged(int w, int h, int oldw, int oldh)3183     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
3184         super.onSizeChanged(w, h, oldw, oldh);
3185         if (w != oldw || h != oldh) {
3186             invalidateGlows();
3187             // layout's w/h are updated during measure/layout steps.
3188         }
3189     }
3190 
3191     /**
3192      * Sets the {@link ItemAnimator} that will handle animations involving changes
3193      * to the items in this RecyclerView. By default, RecyclerView instantiates and
3194      * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
3195      * enabled for the RecyclerView depends on the ItemAnimator and whether
3196      * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
3197      * supports item animations}.
3198      *
3199      * @param animator The ItemAnimator being set. If null, no animations will occur
3200      * when changes occur to the items in this RecyclerView.
3201      */
setItemAnimator(ItemAnimator animator)3202     public void setItemAnimator(ItemAnimator animator) {
3203         if (mItemAnimator != null) {
3204             mItemAnimator.endAnimations();
3205             mItemAnimator.setListener(null);
3206         }
3207         mItemAnimator = animator;
3208         if (mItemAnimator != null) {
3209             mItemAnimator.setListener(mItemAnimatorListener);
3210         }
3211     }
3212 
onEnterLayoutOrScroll()3213     void onEnterLayoutOrScroll() {
3214         mLayoutOrScrollCounter++;
3215     }
3216 
onExitLayoutOrScroll()3217     void onExitLayoutOrScroll() {
3218         onExitLayoutOrScroll(true);
3219     }
3220 
onExitLayoutOrScroll(boolean enableChangeEvents)3221     void onExitLayoutOrScroll(boolean enableChangeEvents) {
3222         mLayoutOrScrollCounter--;
3223         if (mLayoutOrScrollCounter < 1) {
3224             if (DEBUG && mLayoutOrScrollCounter < 0) {
3225                 throw new IllegalStateException("layout or scroll counter cannot go below zero."
3226                         + "Some calls are not matching" + exceptionLabel());
3227             }
3228             mLayoutOrScrollCounter = 0;
3229             if (enableChangeEvents) {
3230                 dispatchContentChangedIfNecessary();
3231                 dispatchPendingImportantForAccessibilityChanges();
3232             }
3233         }
3234     }
3235 
isAccessibilityEnabled()3236     boolean isAccessibilityEnabled() {
3237         return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
3238     }
3239 
dispatchContentChangedIfNecessary()3240     private void dispatchContentChangedIfNecessary() {
3241         final int flags = mEatenAccessibilityChangeFlags;
3242         mEatenAccessibilityChangeFlags = 0;
3243         if (flags != 0 && isAccessibilityEnabled()) {
3244             final AccessibilityEvent event = AccessibilityEvent.obtain();
3245             event.setEventType(AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
3246             AccessibilityEventCompat.setContentChangeTypes(event, flags);
3247             sendAccessibilityEventUnchecked(event);
3248         }
3249     }
3250 
3251     /**
3252      * Returns whether RecyclerView is currently computing a layout.
3253      * <p>
3254      * If this method returns true, it means that RecyclerView is in a lockdown state and any
3255      * attempt to update adapter contents will result in an exception because adapter contents
3256      * cannot be changed while RecyclerView is trying to compute the layout.
3257      * <p>
3258      * It is very unlikely that your code will be running during this state as it is
3259      * called by the framework when a layout traversal happens or RecyclerView starts to scroll
3260      * in response to system events (touch, accessibility etc).
3261      * <p>
3262      * This case may happen if you have some custom logic to change adapter contents in
3263      * response to a View callback (e.g. focus change callback) which might be triggered during a
3264      * layout calculation. In these cases, you should just postpone the change using a Handler or a
3265      * similar mechanism.
3266      *
3267      * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
3268      *         otherwise
3269      */
isComputingLayout()3270     public boolean isComputingLayout() {
3271         return mLayoutOrScrollCounter > 0;
3272     }
3273 
3274     /**
3275      * Returns true if an accessibility event should not be dispatched now. This happens when an
3276      * accessibility request arrives while RecyclerView does not have a stable state which is very
3277      * hard to handle for a LayoutManager. Instead, this method records necessary information about
3278      * the event and dispatches a window change event after the critical section is finished.
3279      *
3280      * @return True if the accessibility event should be postponed.
3281      */
shouldDeferAccessibilityEvent(AccessibilityEvent event)3282     boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
3283         if (isComputingLayout()) {
3284             int type = 0;
3285             if (event != null) {
3286                 type = AccessibilityEventCompat.getContentChangeTypes(event);
3287             }
3288             if (type == 0) {
3289                 type = AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED;
3290             }
3291             mEatenAccessibilityChangeFlags |= type;
3292             return true;
3293         }
3294         return false;
3295     }
3296 
3297     @Override
sendAccessibilityEventUnchecked(AccessibilityEvent event)3298     public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
3299         if (shouldDeferAccessibilityEvent(event)) {
3300             return;
3301         }
3302         super.sendAccessibilityEventUnchecked(event);
3303     }
3304 
3305     /**
3306      * Gets the current ItemAnimator for this RecyclerView. A null return value
3307      * indicates that there is no animator and that item changes will happen without
3308      * any animations. By default, RecyclerView instantiates and
3309      * uses an instance of {@link DefaultItemAnimator}.
3310      *
3311      * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
3312      * when changes occur to the items in this RecyclerView.
3313      */
getItemAnimator()3314     public ItemAnimator getItemAnimator() {
3315         return mItemAnimator;
3316     }
3317 
3318     /**
3319      * Post a runnable to the next frame to run pending item animations. Only the first such
3320      * request will be posted, governed by the mPostedAnimatorRunner flag.
3321      */
postAnimationRunner()3322     void postAnimationRunner() {
3323         if (!mPostedAnimatorRunner && mIsAttached) {
3324             ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
3325             mPostedAnimatorRunner = true;
3326         }
3327     }
3328 
predictiveItemAnimationsEnabled()3329     private boolean predictiveItemAnimationsEnabled() {
3330         return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
3331     }
3332 
3333     /**
3334      * Consumes adapter updates and calculates which type of animations we want to run.
3335      * Called in onMeasure and dispatchLayout.
3336      * <p>
3337      * This method may process only the pre-layout state of updates or all of them.
3338      */
processAdapterUpdatesAndSetAnimationFlags()3339     private void processAdapterUpdatesAndSetAnimationFlags() {
3340         if (mDataSetHasChangedAfterLayout) {
3341             // Processing these items have no value since data set changed unexpectedly.
3342             // Instead, we just reset it.
3343             mAdapterHelper.reset();
3344             mLayout.onItemsChanged(this);
3345         }
3346         // simple animations are a subset of advanced animations (which will cause a
3347         // pre-layout step)
3348         // If layout supports predictive animations, pre-process to decide if we want to run them
3349         if (predictiveItemAnimationsEnabled()) {
3350             mAdapterHelper.preProcess();
3351         } else {
3352             mAdapterHelper.consumeUpdatesInOnePass();
3353         }
3354         boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
3355         mState.mRunSimpleAnimations = mFirstLayoutComplete
3356                 && mItemAnimator != null
3357                 && (mDataSetHasChangedAfterLayout
3358                 || animationTypeSupported
3359                 || mLayout.mRequestedSimpleAnimations)
3360                 && (!mDataSetHasChangedAfterLayout
3361                 || mAdapter.hasStableIds());
3362         mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
3363                 && animationTypeSupported
3364                 && !mDataSetHasChangedAfterLayout
3365                 && predictiveItemAnimationsEnabled();
3366     }
3367 
3368     /**
3369      * Wrapper around layoutChildren() that handles animating changes caused by layout.
3370      * Animations work on the assumption that there are five different kinds of items
3371      * in play:
3372      * PERSISTENT: items are visible before and after layout
3373      * REMOVED: items were visible before layout and were removed by the app
3374      * ADDED: items did not exist before layout and were added by the app
3375      * DISAPPEARING: items exist in the data set before/after, but changed from
3376      * visible to non-visible in the process of layout (they were moved off
3377      * screen as a side-effect of other changes)
3378      * APPEARING: items exist in the data set before/after, but changed from
3379      * non-visible to visible in the process of layout (they were moved on
3380      * screen as a side-effect of other changes)
3381      * The overall approach figures out what items exist before/after layout and
3382      * infers one of the five above states for each of the items. Then the animations
3383      * are set up accordingly:
3384      * PERSISTENT views are animated via
3385      * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3386      * DISAPPEARING views are animated via
3387      * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3388      * APPEARING views are animated via
3389      * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3390      * and changed views are animated via
3391      * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
3392      */
dispatchLayout()3393     void dispatchLayout() {
3394         if (mAdapter == null) {
3395             Log.e(TAG, "No adapter attached; skipping layout");
3396             // leave the state in START
3397             return;
3398         }
3399         if (mLayout == null) {
3400             Log.e(TAG, "No layout manager attached; skipping layout");
3401             // leave the state in START
3402             return;
3403         }
3404         mState.mIsMeasuring = false;
3405         if (mState.mLayoutStep == State.STEP_START) {
3406             dispatchLayoutStep1();
3407             mLayout.setExactMeasureSpecsFrom(this);
3408             dispatchLayoutStep2();
3409         } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
3410                 || mLayout.getHeight() != getHeight()) {
3411             // First 2 steps are done in onMeasure but looks like we have to run again due to
3412             // changed size.
3413             mLayout.setExactMeasureSpecsFrom(this);
3414             dispatchLayoutStep2();
3415         } else {
3416             // always make sure we sync them (to ensure mode is exact)
3417             mLayout.setExactMeasureSpecsFrom(this);
3418         }
3419         dispatchLayoutStep3();
3420     }
3421 
saveFocusInfo()3422     private void saveFocusInfo() {
3423         View child = null;
3424         if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
3425             child = getFocusedChild();
3426         }
3427 
3428         final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
3429         if (focusedVh == null) {
3430             resetFocusInfo();
3431         } else {
3432             mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
3433             // mFocusedItemPosition should hold the current adapter position of the previously
3434             // focused item. If the item is removed, we store the previous adapter position of the
3435             // removed item.
3436             mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION
3437                     : (focusedVh.isRemoved() ? focusedVh.mOldPosition
3438                             : focusedVh.getAdapterPosition());
3439             mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
3440         }
3441     }
3442 
resetFocusInfo()3443     private void resetFocusInfo() {
3444         mState.mFocusedItemId = NO_ID;
3445         mState.mFocusedItemPosition = NO_POSITION;
3446         mState.mFocusedSubChildId = View.NO_ID;
3447     }
3448 
3449     /**
3450      * Finds the best view candidate to request focus on using mFocusedItemPosition index of the
3451      * previously focused item. It first traverses the adapter forward to find a focusable candidate
3452      * and if no such candidate is found, it reverses the focus search direction for the items
3453      * before the mFocusedItemPosition'th index;
3454      * @return The best candidate to request focus on, or null if no such candidate exists. Null
3455      * indicates all the existing adapter items are unfocusable.
3456      */
3457     @Nullable
findNextViewToFocus()3458     private View findNextViewToFocus() {
3459         int startFocusSearchIndex = mState.mFocusedItemPosition != -1 ? mState.mFocusedItemPosition
3460                 : 0;
3461         ViewHolder nextFocus;
3462         final int itemCount = mState.getItemCount();
3463         for (int i = startFocusSearchIndex; i < itemCount; i++) {
3464             nextFocus = findViewHolderForAdapterPosition(i);
3465             if (nextFocus == null) {
3466                 break;
3467             }
3468             if (nextFocus.itemView.hasFocusable()) {
3469                 return nextFocus.itemView;
3470             }
3471         }
3472         final int limit = Math.min(itemCount, startFocusSearchIndex);
3473         for (int i = limit - 1; i >= 0; i--) {
3474             nextFocus = findViewHolderForAdapterPosition(i);
3475             if (nextFocus == null) {
3476                 return null;
3477             }
3478             if (nextFocus.itemView.hasFocusable()) {
3479                 return nextFocus.itemView;
3480             }
3481         }
3482         return null;
3483     }
3484 
recoverFocusFromState()3485     private void recoverFocusFromState() {
3486         if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()
3487                 || getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
3488                 || (getDescendantFocusability() == FOCUS_BEFORE_DESCENDANTS && isFocused())) {
3489             // No-op if either of these cases happens:
3490             // 1. RV has no focus, or 2. RV blocks focus to its children, or 3. RV takes focus
3491             // before its children and is focused (i.e. it already stole the focus away from its
3492             // descendants).
3493             return;
3494         }
3495         // only recover focus if RV itself has the focus or the focused view is hidden
3496         if (!isFocused()) {
3497             final View focusedChild = getFocusedChild();
3498             if (IGNORE_DETACHED_FOCUSED_CHILD
3499                     && (focusedChild.getParent() == null || !focusedChild.hasFocus())) {
3500                 // Special handling of API 15-. A focused child can be invalid because mFocus is not
3501                 // cleared when the child is detached (mParent = null),
3502                 // This happens because clearFocus on API 15- does not invalidate mFocus of its
3503                 // parent when this child is detached.
3504                 // For API 16+, this is not an issue because requestFocus takes care of clearing the
3505                 // prior detached focused child. For API 15- the problem happens in 2 cases because
3506                 // clearChild does not call clearChildFocus on RV: 1. setFocusable(false) is called
3507                 // for the current focused item which calls clearChild or 2. when the prior focused
3508                 // child is removed, removeDetachedView called in layout step 3 which calls
3509                 // clearChild. We should ignore this invalid focused child in all our calculations
3510                 // for the next view to receive focus, and apply the focus recovery logic instead.
3511                 if (mChildHelper.getChildCount() == 0) {
3512                     // No children left. Request focus on the RV itself since one of its children
3513                     // was holding focus previously.
3514                     requestFocus();
3515                     return;
3516                 }
3517             } else if (!mChildHelper.isHidden(focusedChild)) {
3518                 // If the currently focused child is hidden, apply the focus recovery logic.
3519                 // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
3520                 return;
3521             }
3522         }
3523         ViewHolder focusTarget = null;
3524         // RV first attempts to locate the previously focused item to request focus on using
3525         // mFocusedItemId. If such an item no longer exists, it then makes a best-effort attempt to
3526         // find the next best candidate to request focus on based on mFocusedItemPosition.
3527         if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
3528             focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
3529         }
3530         View viewToFocus = null;
3531         if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView)
3532                 || !focusTarget.itemView.hasFocusable()) {
3533             if (mChildHelper.getChildCount() > 0) {
3534                 // At this point, RV has focus and either of these conditions are true:
3535                 // 1. There's no previously focused item either because RV received focused before
3536                 // layout, or the previously focused item was removed, or RV doesn't have stable IDs
3537                 // 2. Previous focus child is hidden, or 3. Previous focused child is no longer
3538                 // focusable. In either of these cases, we make sure that RV still passes down the
3539                 // focus to one of its focusable children using a best-effort algorithm.
3540                 viewToFocus = findNextViewToFocus();
3541             }
3542         } else {
3543             // looks like the focused item has been replaced with another view that represents the
3544             // same item in the adapter. Request focus on that.
3545             viewToFocus = focusTarget.itemView;
3546         }
3547 
3548         if (viewToFocus != null) {
3549             if (mState.mFocusedSubChildId != NO_ID) {
3550                 View child = viewToFocus.findViewById(mState.mFocusedSubChildId);
3551                 if (child != null && child.isFocusable()) {
3552                     viewToFocus = child;
3553                 }
3554             }
3555             viewToFocus.requestFocus();
3556         }
3557     }
3558 
getDeepestFocusedViewWithId(View view)3559     private int getDeepestFocusedViewWithId(View view) {
3560         int lastKnownId = view.getId();
3561         while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
3562             view = ((ViewGroup) view).getFocusedChild();
3563             final int id = view.getId();
3564             if (id != View.NO_ID) {
3565                 lastKnownId = view.getId();
3566             }
3567         }
3568         return lastKnownId;
3569     }
3570 
fillRemainingScrollValues(State state)3571     final void fillRemainingScrollValues(State state) {
3572         if (getScrollState() == SCROLL_STATE_SETTLING) {
3573             final OverScroller scroller = mViewFlinger.mScroller;
3574             state.mRemainingScrollHorizontal = scroller.getFinalX() - scroller.getCurrX();
3575             state.mRemainingScrollVertical = scroller.getFinalY() - scroller.getCurrY();
3576         } else {
3577             state.mRemainingScrollHorizontal = 0;
3578             state.mRemainingScrollVertical = 0;
3579         }
3580     }
3581 
3582     /**
3583      * The first step of a layout where we;
3584      * - process adapter updates
3585      * - decide which animation should run
3586      * - save information about current views
3587      * - If necessary, run predictive layout and save its information
3588      */
dispatchLayoutStep1()3589     private void dispatchLayoutStep1() {
3590         mState.assertLayoutStep(State.STEP_START);
3591         fillRemainingScrollValues(mState);
3592         mState.mIsMeasuring = false;
3593         eatRequestLayout();
3594         mViewInfoStore.clear();
3595         onEnterLayoutOrScroll();
3596         processAdapterUpdatesAndSetAnimationFlags();
3597         saveFocusInfo();
3598         mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
3599         mItemsAddedOrRemoved = mItemsChanged = false;
3600         mState.mInPreLayout = mState.mRunPredictiveAnimations;
3601         mState.mItemCount = mAdapter.getItemCount();
3602         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3603 
3604         if (mState.mRunSimpleAnimations) {
3605             // Step 0: Find out where all non-removed items are, pre-layout
3606             int count = mChildHelper.getChildCount();
3607             for (int i = 0; i < count; ++i) {
3608                 final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3609                 if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
3610                     continue;
3611                 }
3612                 final ItemHolderInfo animationInfo = mItemAnimator
3613                         .recordPreLayoutInformation(mState, holder,
3614                                 ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
3615                                 holder.getUnmodifiedPayloads());
3616                 mViewInfoStore.addToPreLayout(holder, animationInfo);
3617                 if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
3618                         && !holder.shouldIgnore() && !holder.isInvalid()) {
3619                     long key = getChangedHolderKey(holder);
3620                     // This is NOT the only place where a ViewHolder is added to old change holders
3621                     // list. There is another case where:
3622                     //    * A VH is currently hidden but not deleted
3623                     //    * The hidden item is changed in the adapter
3624                     //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
3625                     // When this case is detected, RV will un-hide that view and add to the old
3626                     // change holders list.
3627                     mViewInfoStore.addToOldChangeHolders(key, holder);
3628                 }
3629             }
3630         }
3631         if (mState.mRunPredictiveAnimations) {
3632             // Step 1: run prelayout: This will use the old positions of items. The layout manager
3633             // is expected to layout everything, even removed items (though not to add removed
3634             // items back to the container). This gives the pre-layout position of APPEARING views
3635             // which come into existence as part of the real layout.
3636 
3637             // Save old positions so that LayoutManager can run its mapping logic.
3638             saveOldPositions();
3639             final boolean didStructureChange = mState.mStructureChanged;
3640             mState.mStructureChanged = false;
3641             // temporarily disable flag because we are asking for previous layout
3642             mLayout.onLayoutChildren(mRecycler, mState);
3643             mState.mStructureChanged = didStructureChange;
3644 
3645             for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
3646                 final View child = mChildHelper.getChildAt(i);
3647                 final ViewHolder viewHolder = getChildViewHolderInt(child);
3648                 if (viewHolder.shouldIgnore()) {
3649                     continue;
3650                 }
3651                 if (!mViewInfoStore.isInPreLayout(viewHolder)) {
3652                     int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
3653                     boolean wasHidden = viewHolder
3654                             .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3655                     if (!wasHidden) {
3656                         flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
3657                     }
3658                     final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
3659                             mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
3660                     if (wasHidden) {
3661                         recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
3662                     } else {
3663                         mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
3664                     }
3665                 }
3666             }
3667             // we don't process disappearing list because they may re-appear in post layout pass.
3668             clearOldPositions();
3669         } else {
3670             clearOldPositions();
3671         }
3672         onExitLayoutOrScroll();
3673         resumeRequestLayout(false);
3674         mState.mLayoutStep = State.STEP_LAYOUT;
3675     }
3676 
3677     /**
3678      * The second layout step where we do the actual layout of the views for the final state.
3679      * This step might be run multiple times if necessary (e.g. measure).
3680      */
dispatchLayoutStep2()3681     private void dispatchLayoutStep2() {
3682         eatRequestLayout();
3683         onEnterLayoutOrScroll();
3684         mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
3685         mAdapterHelper.consumeUpdatesInOnePass();
3686         mState.mItemCount = mAdapter.getItemCount();
3687         mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
3688 
3689         // Step 2: Run layout
3690         mState.mInPreLayout = false;
3691         mLayout.onLayoutChildren(mRecycler, mState);
3692 
3693         mState.mStructureChanged = false;
3694         mPendingSavedState = null;
3695 
3696         // onLayoutChildren may have caused client code to disable item animations; re-check
3697         mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
3698         mState.mLayoutStep = State.STEP_ANIMATIONS;
3699         onExitLayoutOrScroll();
3700         resumeRequestLayout(false);
3701     }
3702 
3703     /**
3704      * The final step of the layout where we save the information about views for animations,
3705      * trigger animations and do any necessary cleanup.
3706      */
dispatchLayoutStep3()3707     private void dispatchLayoutStep3() {
3708         mState.assertLayoutStep(State.STEP_ANIMATIONS);
3709         eatRequestLayout();
3710         onEnterLayoutOrScroll();
3711         mState.mLayoutStep = State.STEP_START;
3712         if (mState.mRunSimpleAnimations) {
3713             // Step 3: Find out where things are now, and process change animations.
3714             // traverse list in reverse because we may call animateChange in the loop which may
3715             // remove the target view holder.
3716             for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
3717                 ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3718                 if (holder.shouldIgnore()) {
3719                     continue;
3720                 }
3721                 long key = getChangedHolderKey(holder);
3722                 final ItemHolderInfo animationInfo = mItemAnimator
3723                         .recordPostLayoutInformation(mState, holder);
3724                 ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
3725                 if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
3726                     // run a change animation
3727 
3728                     // If an Item is CHANGED but the updated version is disappearing, it creates
3729                     // a conflicting case.
3730                     // Since a view that is marked as disappearing is likely to be going out of
3731                     // bounds, we run a change animation. Both views will be cleaned automatically
3732                     // once their animations finish.
3733                     // On the other hand, if it is the same view holder instance, we run a
3734                     // disappearing animation instead because we are not going to rebind the updated
3735                     // VH unless it is enforced by the layout manager.
3736                     final boolean oldDisappearing = mViewInfoStore.isDisappearing(
3737                             oldChangeViewHolder);
3738                     final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
3739                     if (oldDisappearing && oldChangeViewHolder == holder) {
3740                         // run disappear animation instead of change
3741                         mViewInfoStore.addToPostLayout(holder, animationInfo);
3742                     } else {
3743                         final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
3744                                 oldChangeViewHolder);
3745                         // we add and remove so that any post info is merged.
3746                         mViewInfoStore.addToPostLayout(holder, animationInfo);
3747                         ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
3748                         if (preInfo == null) {
3749                             handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
3750                         } else {
3751                             animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
3752                                     oldDisappearing, newDisappearing);
3753                         }
3754                     }
3755                 } else {
3756                     mViewInfoStore.addToPostLayout(holder, animationInfo);
3757                 }
3758             }
3759 
3760             // Step 4: Process view info lists and trigger animations
3761             mViewInfoStore.process(mViewInfoProcessCallback);
3762         }
3763 
3764         mLayout.removeAndRecycleScrapInt(mRecycler);
3765         mState.mPreviousLayoutItemCount = mState.mItemCount;
3766         mDataSetHasChangedAfterLayout = false;
3767         mState.mRunSimpleAnimations = false;
3768 
3769         mState.mRunPredictiveAnimations = false;
3770         mLayout.mRequestedSimpleAnimations = false;
3771         if (mRecycler.mChangedScrap != null) {
3772             mRecycler.mChangedScrap.clear();
3773         }
3774         if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
3775             // Initial prefetch has expanded cache, so reset until next prefetch.
3776             // This prevents initial prefetches from expanding the cache permanently.
3777             mLayout.mPrefetchMaxCountObserved = 0;
3778             mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
3779             mRecycler.updateViewCacheSize();
3780         }
3781 
3782         mLayout.onLayoutCompleted(mState);
3783         onExitLayoutOrScroll();
3784         resumeRequestLayout(false);
3785         mViewInfoStore.clear();
3786         if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
3787             dispatchOnScrolled(0, 0);
3788         }
3789         recoverFocusFromState();
3790         resetFocusInfo();
3791     }
3792 
3793     /**
3794      * This handles the case where there is an unexpected VH missing in the pre-layout map.
3795      * <p>
3796      * We might be able to detect the error in the application which will help the developer to
3797      * resolve the issue.
3798      * <p>
3799      * If it is not an expected error, we at least print an error to notify the developer and ignore
3800      * the animation.
3801      *
3802      * https://code.google.com/p/android/issues/detail?id=193958
3803      *
3804      * @param key The change key
3805      * @param holder Current ViewHolder
3806      * @param oldChangeViewHolder Changed ViewHolder
3807      */
handleMissingPreInfoForChangeError(long key, ViewHolder holder, ViewHolder oldChangeViewHolder)3808     private void handleMissingPreInfoForChangeError(long key,
3809             ViewHolder holder, ViewHolder oldChangeViewHolder) {
3810         // check if two VH have the same key, if so, print that as an error
3811         final int childCount = mChildHelper.getChildCount();
3812         for (int i = 0; i < childCount; i++) {
3813             View view = mChildHelper.getChildAt(i);
3814             ViewHolder other = getChildViewHolderInt(view);
3815             if (other == holder) {
3816                 continue;
3817             }
3818             final long otherKey = getChangedHolderKey(other);
3819             if (otherKey == key) {
3820                 if (mAdapter != null && mAdapter.hasStableIds()) {
3821                     throw new IllegalStateException("Two different ViewHolders have the same stable"
3822                             + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
3823                             + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
3824                             + exceptionLabel());
3825                 } else {
3826                     throw new IllegalStateException("Two different ViewHolders have the same change"
3827                             + " ID. This might happen due to inconsistent Adapter update events or"
3828                             + " if the LayoutManager lays out the same View multiple times."
3829                             + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
3830                             + exceptionLabel());
3831                 }
3832             }
3833         }
3834         // Very unlikely to happen but if it does, notify the developer.
3835         Log.e(TAG, "Problem while matching changed view holders with the new"
3836                 + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
3837                 + " cannot be found but it is necessary for " + holder + exceptionLabel());
3838     }
3839 
3840     /**
3841      * Records the animation information for a view holder that was bounced from hidden list. It
3842      * also clears the bounce back flag.
3843      */
recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder, ItemHolderInfo animationInfo)3844     void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
3845             ItemHolderInfo animationInfo) {
3846         // looks like this view bounced back from hidden list!
3847         viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3848         if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
3849                 && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
3850             long key = getChangedHolderKey(viewHolder);
3851             mViewInfoStore.addToOldChangeHolders(key, viewHolder);
3852         }
3853         mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
3854     }
3855 
findMinMaxChildLayoutPositions(int[] into)3856     private void findMinMaxChildLayoutPositions(int[] into) {
3857         final int count = mChildHelper.getChildCount();
3858         if (count == 0) {
3859             into[0] = NO_POSITION;
3860             into[1] = NO_POSITION;
3861             return;
3862         }
3863         int minPositionPreLayout = Integer.MAX_VALUE;
3864         int maxPositionPreLayout = Integer.MIN_VALUE;
3865         for (int i = 0; i < count; ++i) {
3866             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3867             if (holder.shouldIgnore()) {
3868                 continue;
3869             }
3870             final int pos = holder.getLayoutPosition();
3871             if (pos < minPositionPreLayout) {
3872                 minPositionPreLayout = pos;
3873             }
3874             if (pos > maxPositionPreLayout) {
3875                 maxPositionPreLayout = pos;
3876             }
3877         }
3878         into[0] = minPositionPreLayout;
3879         into[1] = maxPositionPreLayout;
3880     }
3881 
didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout)3882     private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
3883         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3884         return mMinMaxLayoutPositions[0] != minPositionPreLayout
3885                 || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
3886     }
3887 
3888     @Override
removeDetachedView(View child, boolean animate)3889     protected void removeDetachedView(View child, boolean animate) {
3890         ViewHolder vh = getChildViewHolderInt(child);
3891         if (vh != null) {
3892             if (vh.isTmpDetached()) {
3893                 vh.clearTmpDetachFlag();
3894             } else if (!vh.shouldIgnore()) {
3895                 throw new IllegalArgumentException("Called removeDetachedView with a view which"
3896                         + " is not flagged as tmp detached." + vh + exceptionLabel());
3897             }
3898         }
3899 
3900         // Clear any android.view.animation.Animation that may prevent the item from
3901         // detaching when being removed. If a child is re-added before the
3902         // lazy detach occurs, it will receive invalid attach/detach sequencing.
3903         child.clearAnimation();
3904 
3905         dispatchChildDetached(child);
3906         super.removeDetachedView(child, animate);
3907     }
3908 
3909     /**
3910      * Returns a unique key to be used while handling change animations.
3911      * It might be child's position or stable id depending on the adapter type.
3912      */
getChangedHolderKey(ViewHolder holder)3913     long getChangedHolderKey(ViewHolder holder) {
3914         return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
3915     }
3916 
animateAppearance(@onNull ViewHolder itemHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)3917     void animateAppearance(@NonNull ViewHolder itemHolder,
3918             @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
3919         itemHolder.setIsRecyclable(false);
3920         if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
3921             postAnimationRunner();
3922         }
3923     }
3924 
animateDisappearance(@onNull ViewHolder holder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo)3925     void animateDisappearance(@NonNull ViewHolder holder,
3926             @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
3927         addAnimatingView(holder);
3928         holder.setIsRecyclable(false);
3929         if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
3930             postAnimationRunner();
3931         }
3932     }
3933 
animateChange(@onNull ViewHolder oldHolder, @NonNull ViewHolder newHolder, @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo, boolean oldHolderDisappearing, boolean newHolderDisappearing)3934     private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
3935             @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
3936             boolean oldHolderDisappearing, boolean newHolderDisappearing) {
3937         oldHolder.setIsRecyclable(false);
3938         if (oldHolderDisappearing) {
3939             addAnimatingView(oldHolder);
3940         }
3941         if (oldHolder != newHolder) {
3942             if (newHolderDisappearing) {
3943                 addAnimatingView(newHolder);
3944             }
3945             oldHolder.mShadowedHolder = newHolder;
3946             // old holder should disappear after animation ends
3947             addAnimatingView(oldHolder);
3948             mRecycler.unscrapView(oldHolder);
3949             newHolder.setIsRecyclable(false);
3950             newHolder.mShadowingHolder = oldHolder;
3951         }
3952         if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
3953             postAnimationRunner();
3954         }
3955     }
3956 
3957     @Override
onLayout(boolean changed, int l, int t, int r, int b)3958     protected void onLayout(boolean changed, int l, int t, int r, int b) {
3959         TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
3960         dispatchLayout();
3961         TraceCompat.endSection();
3962         mFirstLayoutComplete = true;
3963     }
3964 
3965     @Override
requestLayout()3966     public void requestLayout() {
3967         if (mEatRequestLayout == 0 && !mLayoutFrozen) {
3968             super.requestLayout();
3969         } else {
3970             mLayoutRequestEaten = true;
3971         }
3972     }
3973 
markItemDecorInsetsDirty()3974     void markItemDecorInsetsDirty() {
3975         final int childCount = mChildHelper.getUnfilteredChildCount();
3976         for (int i = 0; i < childCount; i++) {
3977             final View child = mChildHelper.getUnfilteredChildAt(i);
3978             ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
3979         }
3980         mRecycler.markItemDecorInsetsDirty();
3981     }
3982 
3983     @Override
draw(Canvas c)3984     public void draw(Canvas c) {
3985         super.draw(c);
3986 
3987         final int count = mItemDecorations.size();
3988         for (int i = 0; i < count; i++) {
3989             mItemDecorations.get(i).onDrawOver(c, this, mState);
3990         }
3991         // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
3992         // need find children closest to edges. Not sure if it is worth the effort.
3993         boolean needsInvalidate = false;
3994         if (mLeftGlow != null && !mLeftGlow.isFinished()) {
3995             final int restore = c.save();
3996             final int padding = mClipToPadding ? getPaddingBottom() : 0;
3997             c.rotate(270);
3998             c.translate(-getHeight() + padding, 0);
3999             needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
4000             c.restoreToCount(restore);
4001         }
4002         if (mTopGlow != null && !mTopGlow.isFinished()) {
4003             final int restore = c.save();
4004             if (mClipToPadding) {
4005                 c.translate(getPaddingLeft(), getPaddingTop());
4006             }
4007             needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
4008             c.restoreToCount(restore);
4009         }
4010         if (mRightGlow != null && !mRightGlow.isFinished()) {
4011             final int restore = c.save();
4012             final int width = getWidth();
4013             final int padding = mClipToPadding ? getPaddingTop() : 0;
4014             c.rotate(90);
4015             c.translate(-padding, -width);
4016             needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
4017             c.restoreToCount(restore);
4018         }
4019         if (mBottomGlow != null && !mBottomGlow.isFinished()) {
4020             final int restore = c.save();
4021             c.rotate(180);
4022             if (mClipToPadding) {
4023                 c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
4024             } else {
4025                 c.translate(-getWidth(), -getHeight());
4026             }
4027             needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
4028             c.restoreToCount(restore);
4029         }
4030 
4031         // If some views are animating, ItemDecorators are likely to move/change with them.
4032         // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
4033         // display lists are not invalidated.
4034         if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
4035                 && mItemAnimator.isRunning()) {
4036             needsInvalidate = true;
4037         }
4038 
4039         if (needsInvalidate) {
4040             ViewCompat.postInvalidateOnAnimation(this);
4041         }
4042     }
4043 
4044     @Override
onDraw(Canvas c)4045     public void onDraw(Canvas c) {
4046         super.onDraw(c);
4047 
4048         final int count = mItemDecorations.size();
4049         for (int i = 0; i < count; i++) {
4050             mItemDecorations.get(i).onDraw(c, this, mState);
4051         }
4052     }
4053 
4054     @Override
checkLayoutParams(ViewGroup.LayoutParams p)4055     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
4056         return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
4057     }
4058 
4059     @Override
generateDefaultLayoutParams()4060     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
4061         if (mLayout == null) {
4062             throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
4063         }
4064         return mLayout.generateDefaultLayoutParams();
4065     }
4066 
4067     @Override
generateLayoutParams(AttributeSet attrs)4068     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
4069         if (mLayout == null) {
4070             throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
4071         }
4072         return mLayout.generateLayoutParams(getContext(), attrs);
4073     }
4074 
4075     @Override
generateLayoutParams(ViewGroup.LayoutParams p)4076     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
4077         if (mLayout == null) {
4078             throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
4079         }
4080         return mLayout.generateLayoutParams(p);
4081     }
4082 
4083     /**
4084      * Returns true if RecyclerView is currently running some animations.
4085      * <p>
4086      * If you want to be notified when animations are finished, use
4087      * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
4088      *
4089      * @return True if there are some item animations currently running or waiting to be started.
4090      */
isAnimating()4091     public boolean isAnimating() {
4092         return mItemAnimator != null && mItemAnimator.isRunning();
4093     }
4094 
saveOldPositions()4095     void saveOldPositions() {
4096         final int childCount = mChildHelper.getUnfilteredChildCount();
4097         for (int i = 0; i < childCount; i++) {
4098             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4099             if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
4100                 throw new IllegalStateException("view holder cannot have position -1 unless it"
4101                         + " is removed" + exceptionLabel());
4102             }
4103             if (!holder.shouldIgnore()) {
4104                 holder.saveOldPosition();
4105             }
4106         }
4107     }
4108 
clearOldPositions()4109     void clearOldPositions() {
4110         final int childCount = mChildHelper.getUnfilteredChildCount();
4111         for (int i = 0; i < childCount; i++) {
4112             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4113             if (!holder.shouldIgnore()) {
4114                 holder.clearOldPosition();
4115             }
4116         }
4117         mRecycler.clearOldPositions();
4118     }
4119 
offsetPositionRecordsForMove(int from, int to)4120     void offsetPositionRecordsForMove(int from, int to) {
4121         final int childCount = mChildHelper.getUnfilteredChildCount();
4122         final int start, end, inBetweenOffset;
4123         if (from < to) {
4124             start = from;
4125             end = to;
4126             inBetweenOffset = -1;
4127         } else {
4128             start = to;
4129             end = from;
4130             inBetweenOffset = 1;
4131         }
4132 
4133         for (int i = 0; i < childCount; i++) {
4134             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4135             if (holder == null || holder.mPosition < start || holder.mPosition > end) {
4136                 continue;
4137             }
4138             if (DEBUG) {
4139                 Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
4140                         + holder);
4141             }
4142             if (holder.mPosition == from) {
4143                 holder.offsetPosition(to - from, false);
4144             } else {
4145                 holder.offsetPosition(inBetweenOffset, false);
4146             }
4147 
4148             mState.mStructureChanged = true;
4149         }
4150         mRecycler.offsetPositionRecordsForMove(from, to);
4151         requestLayout();
4152     }
4153 
offsetPositionRecordsForInsert(int positionStart, int itemCount)4154     void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
4155         final int childCount = mChildHelper.getUnfilteredChildCount();
4156         for (int i = 0; i < childCount; i++) {
4157             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4158             if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
4159                 if (DEBUG) {
4160                     Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
4161                             + holder + " now at position " + (holder.mPosition + itemCount));
4162                 }
4163                 holder.offsetPosition(itemCount, false);
4164                 mState.mStructureChanged = true;
4165             }
4166         }
4167         mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
4168         requestLayout();
4169     }
4170 
offsetPositionRecordsForRemove(int positionStart, int itemCount, boolean applyToPreLayout)4171     void offsetPositionRecordsForRemove(int positionStart, int itemCount,
4172             boolean applyToPreLayout) {
4173         final int positionEnd = positionStart + itemCount;
4174         final int childCount = mChildHelper.getUnfilteredChildCount();
4175         for (int i = 0; i < childCount; i++) {
4176             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4177             if (holder != null && !holder.shouldIgnore()) {
4178                 if (holder.mPosition >= positionEnd) {
4179                     if (DEBUG) {
4180                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4181                                 + " holder " + holder + " now at position "
4182                                 + (holder.mPosition - itemCount));
4183                     }
4184                     holder.offsetPosition(-itemCount, applyToPreLayout);
4185                     mState.mStructureChanged = true;
4186                 } else if (holder.mPosition >= positionStart) {
4187                     if (DEBUG) {
4188                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4189                                 + " holder " + holder + " now REMOVED");
4190                     }
4191                     holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
4192                             applyToPreLayout);
4193                     mState.mStructureChanged = true;
4194                 }
4195             }
4196         }
4197         mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
4198         requestLayout();
4199     }
4200 
4201     /**
4202      * Rebind existing views for the given range, or create as needed.
4203      *
4204      * @param positionStart Adapter position to start at
4205      * @param itemCount Number of views that must explicitly be rebound
4206      */
viewRangeUpdate(int positionStart, int itemCount, Object payload)4207     void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
4208         final int childCount = mChildHelper.getUnfilteredChildCount();
4209         final int positionEnd = positionStart + itemCount;
4210 
4211         for (int i = 0; i < childCount; i++) {
4212             final View child = mChildHelper.getUnfilteredChildAt(i);
4213             final ViewHolder holder = getChildViewHolderInt(child);
4214             if (holder == null || holder.shouldIgnore()) {
4215                 continue;
4216             }
4217             if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
4218                 // We re-bind these view holders after pre-processing is complete so that
4219                 // ViewHolders have their final positions assigned.
4220                 holder.addFlags(ViewHolder.FLAG_UPDATE);
4221                 holder.addChangePayload(payload);
4222                 // lp cannot be null since we get ViewHolder from it.
4223                 ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
4224             }
4225         }
4226         mRecycler.viewRangeUpdate(positionStart, itemCount);
4227     }
4228 
canReuseUpdatedViewHolder(ViewHolder viewHolder)4229     boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
4230         return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
4231                 viewHolder.getUnmodifiedPayloads());
4232     }
4233 
4234 
4235     /**
4236      * Call this method to signal that *all* adapter content has changed (generally, because of
4237      * swapAdapter, or notifyDataSetChanged), and that once layout occurs, all attached items should
4238      * be discarded or animated.
4239      *
4240      * Attached items are labeled as invalid, and all cached items are discarded.
4241      *
4242      * It is still possible for items to be prefetched while mDataSetHasChangedAfterLayout == true,
4243      * so this method must always discard all cached views so that the only valid items that remain
4244      * in the cache, once layout occurs, are valid prefetched items.
4245      */
setDataSetChangedAfterLayout()4246     void setDataSetChangedAfterLayout() {
4247         mDataSetHasChangedAfterLayout = true;
4248         markKnownViewsInvalid();
4249     }
4250 
4251     /**
4252      * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
4253      * data change event.
4254      */
markKnownViewsInvalid()4255     void markKnownViewsInvalid() {
4256         final int childCount = mChildHelper.getUnfilteredChildCount();
4257         for (int i = 0; i < childCount; i++) {
4258             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4259             if (holder != null && !holder.shouldIgnore()) {
4260                 holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
4261             }
4262         }
4263         markItemDecorInsetsDirty();
4264         mRecycler.markKnownViewsInvalid();
4265     }
4266 
4267     /**
4268      * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
4269      * will trigger a {@link #requestLayout()} call.
4270      */
invalidateItemDecorations()4271     public void invalidateItemDecorations() {
4272         if (mItemDecorations.size() == 0) {
4273             return;
4274         }
4275         if (mLayout != null) {
4276             mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
4277                     + " or layout");
4278         }
4279         markItemDecorInsetsDirty();
4280         requestLayout();
4281     }
4282 
4283     /**
4284      * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
4285      * focus even if the View representing the Item is replaced during a layout calculation.
4286      * <p>
4287      * By default, this value is {@code true}.
4288      *
4289      * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
4290      * focus.
4291      *
4292      * @see #setPreserveFocusAfterLayout(boolean)
4293      */
getPreserveFocusAfterLayout()4294     public boolean getPreserveFocusAfterLayout() {
4295         return mPreserveFocusAfterLayout;
4296     }
4297 
4298     /**
4299      * Set whether the RecyclerView should try to keep the same Item focused after a layout
4300      * calculation or not.
4301      * <p>
4302      * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
4303      * views may lose focus during a layout calculation as their state changes or they are replaced
4304      * with another view due to type change or animation. In these cases, RecyclerView can request
4305      * focus on the new view automatically.
4306      *
4307      * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
4308      *                                 layout calculations. Defaults to true.
4309      *
4310      * @see #getPreserveFocusAfterLayout()
4311      */
setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout)4312     public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
4313         mPreserveFocusAfterLayout = preserveFocusAfterLayout;
4314     }
4315 
4316     /**
4317      * Retrieve the {@link ViewHolder} for the given child view.
4318      *
4319      * @param child Child of this RecyclerView to query for its ViewHolder
4320      * @return The child view's ViewHolder
4321      */
getChildViewHolder(View child)4322     public ViewHolder getChildViewHolder(View child) {
4323         final ViewParent parent = child.getParent();
4324         if (parent != null && parent != this) {
4325             throw new IllegalArgumentException("View " + child + " is not a direct child of "
4326                     + this);
4327         }
4328         return getChildViewHolderInt(child);
4329     }
4330 
4331     /**
4332      * Traverses the ancestors of the given view and returns the item view that contains it and
4333      * also a direct child of the RecyclerView. This returned view can be used to get the
4334      * ViewHolder by calling {@link #getChildViewHolder(View)}.
4335      *
4336      * @param view The view that is a descendant of the RecyclerView.
4337      *
4338      * @return The direct child of the RecyclerView which contains the given view or null if the
4339      * provided view is not a descendant of this RecyclerView.
4340      *
4341      * @see #getChildViewHolder(View)
4342      * @see #findContainingViewHolder(View)
4343      */
4344     @Nullable
findContainingItemView(View view)4345     public View findContainingItemView(View view) {
4346         ViewParent parent = view.getParent();
4347         while (parent != null && parent != this && parent instanceof View) {
4348             view = (View) parent;
4349             parent = view.getParent();
4350         }
4351         return parent == this ? view : null;
4352     }
4353 
4354     /**
4355      * Returns the ViewHolder that contains the given view.
4356      *
4357      * @param view The view that is a descendant of the RecyclerView.
4358      *
4359      * @return The ViewHolder that contains the given view or null if the provided view is not a
4360      * descendant of this RecyclerView.
4361      */
4362     @Nullable
findContainingViewHolder(View view)4363     public ViewHolder findContainingViewHolder(View view) {
4364         View itemView = findContainingItemView(view);
4365         return itemView == null ? null : getChildViewHolder(itemView);
4366     }
4367 
4368 
getChildViewHolderInt(View child)4369     static ViewHolder getChildViewHolderInt(View child) {
4370         if (child == null) {
4371             return null;
4372         }
4373         return ((LayoutParams) child.getLayoutParams()).mViewHolder;
4374     }
4375 
4376     /**
4377      * @deprecated use {@link #getChildAdapterPosition(View)} or
4378      * {@link #getChildLayoutPosition(View)}.
4379      */
4380     @Deprecated
getChildPosition(View child)4381     public int getChildPosition(View child) {
4382         return getChildAdapterPosition(child);
4383     }
4384 
4385     /**
4386      * Return the adapter position that the given child view corresponds to.
4387      *
4388      * @param child Child View to query
4389      * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
4390      */
getChildAdapterPosition(View child)4391     public int getChildAdapterPosition(View child) {
4392         final ViewHolder holder = getChildViewHolderInt(child);
4393         return holder != null ? holder.getAdapterPosition() : NO_POSITION;
4394     }
4395 
4396     /**
4397      * Return the adapter position of the given child view as of the latest completed layout pass.
4398      * <p>
4399      * This position may not be equal to Item's adapter position if there are pending changes
4400      * in the adapter which have not been reflected to the layout yet.
4401      *
4402      * @param child Child View to query
4403      * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
4404      * the View is representing a removed item.
4405      */
getChildLayoutPosition(View child)4406     public int getChildLayoutPosition(View child) {
4407         final ViewHolder holder = getChildViewHolderInt(child);
4408         return holder != null ? holder.getLayoutPosition() : NO_POSITION;
4409     }
4410 
4411     /**
4412      * Return the stable item id that the given child view corresponds to.
4413      *
4414      * @param child Child View to query
4415      * @return Item id corresponding to the given view or {@link #NO_ID}
4416      */
getChildItemId(View child)4417     public long getChildItemId(View child) {
4418         if (mAdapter == null || !mAdapter.hasStableIds()) {
4419             return NO_ID;
4420         }
4421         final ViewHolder holder = getChildViewHolderInt(child);
4422         return holder != null ? holder.getItemId() : NO_ID;
4423     }
4424 
4425     /**
4426      * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
4427      * {@link #findViewHolderForAdapterPosition(int)}
4428      */
4429     @Deprecated
findViewHolderForPosition(int position)4430     public ViewHolder findViewHolderForPosition(int position) {
4431         return findViewHolderForPosition(position, false);
4432     }
4433 
4434     /**
4435      * Return the ViewHolder for the item in the given position of the data set as of the latest
4436      * layout pass.
4437      * <p>
4438      * This method checks only the children of RecyclerView. If the item at the given
4439      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4440      * <p>
4441      * Note that when Adapter contents change, ViewHolder positions are not updated until the
4442      * next layout calculation. If there are pending adapter updates, the return value of this
4443      * method may not match your adapter contents. You can use
4444      * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
4445      * <p>
4446      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4447      * with the same layout position representing the same Item. In this case, the updated
4448      * ViewHolder will be returned.
4449      *
4450      * @param position The position of the item in the data set of the adapter
4451      * @return The ViewHolder at <code>position</code> or null if there is no such item
4452      */
findViewHolderForLayoutPosition(int position)4453     public ViewHolder findViewHolderForLayoutPosition(int position) {
4454         return findViewHolderForPosition(position, false);
4455     }
4456 
4457     /**
4458      * Return the ViewHolder for the item in the given position of the data set. Unlike
4459      * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
4460      * adapter changes that may not be reflected to the layout yet. On the other hand, if
4461      * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
4462      * calculated yet, this method will return <code>null</code> since the new positions of views
4463      * are unknown until the layout is calculated.
4464      * <p>
4465      * This method checks only the children of RecyclerView. If the item at the given
4466      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4467      * <p>
4468      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4469      * representing the same Item. In this case, the updated ViewHolder will be returned.
4470      *
4471      * @param position The position of the item in the data set of the adapter
4472      * @return The ViewHolder at <code>position</code> or null if there is no such item
4473      */
findViewHolderForAdapterPosition(int position)4474     public ViewHolder findViewHolderForAdapterPosition(int position) {
4475         if (mDataSetHasChangedAfterLayout) {
4476             return null;
4477         }
4478         final int childCount = mChildHelper.getUnfilteredChildCount();
4479         // hidden VHs are not preferred but if that is the only one we find, we rather return it
4480         ViewHolder hidden = null;
4481         for (int i = 0; i < childCount; i++) {
4482             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4483             if (holder != null && !holder.isRemoved()
4484                     && getAdapterPositionFor(holder) == position) {
4485                 if (mChildHelper.isHidden(holder.itemView)) {
4486                     hidden = holder;
4487                 } else {
4488                     return holder;
4489                 }
4490             }
4491         }
4492         return hidden;
4493     }
4494 
findViewHolderForPosition(int position, boolean checkNewPosition)4495     ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
4496         final int childCount = mChildHelper.getUnfilteredChildCount();
4497         ViewHolder hidden = null;
4498         for (int i = 0; i < childCount; i++) {
4499             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4500             if (holder != null && !holder.isRemoved()) {
4501                 if (checkNewPosition) {
4502                     if (holder.mPosition != position) {
4503                         continue;
4504                     }
4505                 } else if (holder.getLayoutPosition() != position) {
4506                     continue;
4507                 }
4508                 if (mChildHelper.isHidden(holder.itemView)) {
4509                     hidden = holder;
4510                 } else {
4511                     return holder;
4512                 }
4513             }
4514         }
4515         // This method should not query cached views. It creates a problem during adapter updates
4516         // when we are dealing with already laid out views. Also, for the public method, it is more
4517         // reasonable to return null if position is not laid out.
4518         return hidden;
4519     }
4520 
4521     /**
4522      * Return the ViewHolder for the item with the given id. The RecyclerView must
4523      * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
4524      * return a non-null value.
4525      * <p>
4526      * This method checks only the children of RecyclerView. If the item with the given
4527      * <code>id</code> is not laid out, it <em>will not</em> create a new one.
4528      *
4529      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
4530      * same id. In this case, the updated ViewHolder will be returned.
4531      *
4532      * @param id The id for the requested item
4533      * @return The ViewHolder with the given <code>id</code> or null if there is no such item
4534      */
findViewHolderForItemId(long id)4535     public ViewHolder findViewHolderForItemId(long id) {
4536         if (mAdapter == null || !mAdapter.hasStableIds()) {
4537             return null;
4538         }
4539         final int childCount = mChildHelper.getUnfilteredChildCount();
4540         ViewHolder hidden = null;
4541         for (int i = 0; i < childCount; i++) {
4542             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4543             if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
4544                 if (mChildHelper.isHidden(holder.itemView)) {
4545                     hidden = holder;
4546                 } else {
4547                     return holder;
4548                 }
4549             }
4550         }
4551         return hidden;
4552     }
4553 
4554     /**
4555      * Find the topmost view under the given point.
4556      *
4557      * @param x Horizontal position in pixels to search
4558      * @param y Vertical position in pixels to search
4559      * @return The child view under (x, y) or null if no matching child is found
4560      */
findChildViewUnder(float x, float y)4561     public View findChildViewUnder(float x, float y) {
4562         final int count = mChildHelper.getChildCount();
4563         for (int i = count - 1; i >= 0; i--) {
4564             final View child = mChildHelper.getChildAt(i);
4565             final float translationX = child.getTranslationX();
4566             final float translationY = child.getTranslationY();
4567             if (x >= child.getLeft() + translationX
4568                     && x <= child.getRight() + translationX
4569                     && y >= child.getTop() + translationY
4570                     && y <= child.getBottom() + translationY) {
4571                 return child;
4572             }
4573         }
4574         return null;
4575     }
4576 
4577     @Override
drawChild(Canvas canvas, View child, long drawingTime)4578     public boolean drawChild(Canvas canvas, View child, long drawingTime) {
4579         return super.drawChild(canvas, child, drawingTime);
4580     }
4581 
4582     /**
4583      * Offset the bounds of all child views by <code>dy</code> pixels.
4584      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4585      *
4586      * @param dy Vertical pixel offset to apply to the bounds of all child views
4587      */
offsetChildrenVertical(int dy)4588     public void offsetChildrenVertical(int dy) {
4589         final int childCount = mChildHelper.getChildCount();
4590         for (int i = 0; i < childCount; i++) {
4591             mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
4592         }
4593     }
4594 
4595     /**
4596      * Called when an item view is attached to this RecyclerView.
4597      *
4598      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4599      * of child views as they become attached. This will be called before a
4600      * {@link LayoutManager} measures or lays out the view and is a good time to perform these
4601      * changes.</p>
4602      *
4603      * @param child Child view that is now attached to this RecyclerView and its associated window
4604      */
onChildAttachedToWindow(View child)4605     public void onChildAttachedToWindow(View child) {
4606     }
4607 
4608     /**
4609      * Called when an item view is detached from this RecyclerView.
4610      *
4611      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4612      * of child views as they become detached. This will be called as a
4613      * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
4614      *
4615      * @param child Child view that is now detached from this RecyclerView and its associated window
4616      */
onChildDetachedFromWindow(View child)4617     public void onChildDetachedFromWindow(View child) {
4618     }
4619 
4620     /**
4621      * Offset the bounds of all child views by <code>dx</code> pixels.
4622      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4623      *
4624      * @param dx Horizontal pixel offset to apply to the bounds of all child views
4625      */
offsetChildrenHorizontal(int dx)4626     public void offsetChildrenHorizontal(int dx) {
4627         final int childCount = mChildHelper.getChildCount();
4628         for (int i = 0; i < childCount; i++) {
4629             mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
4630         }
4631     }
4632 
4633     /**
4634      * Returns the bounds of the view including its decoration and margins.
4635      *
4636      * @param view The view element to check
4637      * @param outBounds A rect that will receive the bounds of the element including its
4638      *                  decoration and margins.
4639      */
getDecoratedBoundsWithMargins(View view, Rect outBounds)4640     public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
4641         getDecoratedBoundsWithMarginsInt(view, outBounds);
4642     }
4643 
getDecoratedBoundsWithMarginsInt(View view, Rect outBounds)4644     static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
4645         final LayoutParams lp = (LayoutParams) view.getLayoutParams();
4646         final Rect insets = lp.mDecorInsets;
4647         outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
4648                 view.getTop() - insets.top - lp.topMargin,
4649                 view.getRight() + insets.right + lp.rightMargin,
4650                 view.getBottom() + insets.bottom + lp.bottomMargin);
4651     }
4652 
getItemDecorInsetsForChild(View child)4653     Rect getItemDecorInsetsForChild(View child) {
4654         final LayoutParams lp = (LayoutParams) child.getLayoutParams();
4655         if (!lp.mInsetsDirty) {
4656             return lp.mDecorInsets;
4657         }
4658 
4659         if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
4660             // changed/invalid items should not be updated until they are rebound.
4661             return lp.mDecorInsets;
4662         }
4663         final Rect insets = lp.mDecorInsets;
4664         insets.set(0, 0, 0, 0);
4665         final int decorCount = mItemDecorations.size();
4666         for (int i = 0; i < decorCount; i++) {
4667             mTempRect.set(0, 0, 0, 0);
4668             mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
4669             insets.left += mTempRect.left;
4670             insets.top += mTempRect.top;
4671             insets.right += mTempRect.right;
4672             insets.bottom += mTempRect.bottom;
4673         }
4674         lp.mInsetsDirty = false;
4675         return insets;
4676     }
4677 
4678     /**
4679      * Called when the scroll position of this RecyclerView changes. Subclasses should use
4680      * this method to respond to scrolling within the adapter's data set instead of an explicit
4681      * listener.
4682      *
4683      * <p>This method will always be invoked before listeners. If a subclass needs to perform
4684      * any additional upkeep or bookkeeping after scrolling but before listeners run,
4685      * this is a good place to do so.</p>
4686      *
4687      * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
4688      * the distance scrolled in either direction within the adapter's data set instead of absolute
4689      * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
4690      * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
4691      * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
4692      * do not correspond to the data set scroll position. However, some subclasses may choose
4693      * to use these fields as special offsets.</p>
4694      *
4695      * @param dx horizontal distance scrolled in pixels
4696      * @param dy vertical distance scrolled in pixels
4697      */
onScrolled(int dx, int dy)4698     public void onScrolled(int dx, int dy) {
4699         // Do nothing
4700     }
4701 
dispatchOnScrolled(int hresult, int vresult)4702     void dispatchOnScrolled(int hresult, int vresult) {
4703         mDispatchScrollCounter++;
4704         // Pass the current scrollX/scrollY values; no actual change in these properties occurred
4705         // but some general-purpose code may choose to respond to changes this way.
4706         final int scrollX = getScrollX();
4707         final int scrollY = getScrollY();
4708         onScrollChanged(scrollX, scrollY, scrollX, scrollY);
4709 
4710         // Pass the real deltas to onScrolled, the RecyclerView-specific method.
4711         onScrolled(hresult, vresult);
4712 
4713         // Invoke listeners last. Subclassed view methods always handle the event first.
4714         // All internal state is consistent by the time listeners are invoked.
4715         if (mScrollListener != null) {
4716             mScrollListener.onScrolled(this, hresult, vresult);
4717         }
4718         if (mScrollListeners != null) {
4719             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4720                 mScrollListeners.get(i).onScrolled(this, hresult, vresult);
4721             }
4722         }
4723         mDispatchScrollCounter--;
4724     }
4725 
4726     /**
4727      * Called when the scroll state of this RecyclerView changes. Subclasses should use this
4728      * method to respond to state changes instead of an explicit listener.
4729      *
4730      * <p>This method will always be invoked before listeners, but after the LayoutManager
4731      * responds to the scroll state change.</p>
4732      *
4733      * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
4734      *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
4735      */
onScrollStateChanged(int state)4736     public void onScrollStateChanged(int state) {
4737         // Do nothing
4738     }
4739 
dispatchOnScrollStateChanged(int state)4740     void dispatchOnScrollStateChanged(int state) {
4741         // Let the LayoutManager go first; this allows it to bring any properties into
4742         // a consistent state before the RecyclerView subclass responds.
4743         if (mLayout != null) {
4744             mLayout.onScrollStateChanged(state);
4745         }
4746 
4747         // Let the RecyclerView subclass handle this event next; any LayoutManager property
4748         // changes will be reflected by this time.
4749         onScrollStateChanged(state);
4750 
4751         // Listeners go last. All other internal state is consistent by this point.
4752         if (mScrollListener != null) {
4753             mScrollListener.onScrollStateChanged(this, state);
4754         }
4755         if (mScrollListeners != null) {
4756             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4757                 mScrollListeners.get(i).onScrollStateChanged(this, state);
4758             }
4759         }
4760     }
4761 
4762     /**
4763      * Returns whether there are pending adapter updates which are not yet applied to the layout.
4764      * <p>
4765      * If this method returns <code>true</code>, it means that what user is currently seeing may not
4766      * reflect them adapter contents (depending on what has changed).
4767      * You may use this information to defer or cancel some operations.
4768      * <p>
4769      * This method returns true if RecyclerView has not yet calculated the first layout after it is
4770      * attached to the Window or the Adapter has been replaced.
4771      *
4772      * @return True if there are some adapter updates which are not yet reflected to layout or false
4773      * if layout is up to date.
4774      */
hasPendingAdapterUpdates()4775     public boolean hasPendingAdapterUpdates() {
4776         return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
4777                 || mAdapterHelper.hasPendingUpdates();
4778     }
4779 
4780     class ViewFlinger implements Runnable {
4781         private int mLastFlingX;
4782         private int mLastFlingY;
4783         private OverScroller mScroller;
4784         Interpolator mInterpolator = sQuinticInterpolator;
4785 
4786 
4787         // When set to true, postOnAnimation callbacks are delayed until the run method completes
4788         private boolean mEatRunOnAnimationRequest = false;
4789 
4790         // Tracks if postAnimationCallback should be re-attached when it is done
4791         private boolean mReSchedulePostAnimationCallback = false;
4792 
ViewFlinger()4793         ViewFlinger() {
4794             mScroller = new OverScroller(getContext(), sQuinticInterpolator);
4795         }
4796 
4797         @Override
run()4798         public void run() {
4799             if (mLayout == null) {
4800                 stop();
4801                 return; // no layout, cannot scroll.
4802             }
4803             disableRunOnAnimationRequests();
4804             consumePendingUpdateOperations();
4805             // keep a local reference so that if it is changed during onAnimation method, it won't
4806             // cause unexpected behaviors
4807             final OverScroller scroller = mScroller;
4808             final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
4809             if (scroller.computeScrollOffset()) {
4810                 final int[] scrollConsumed = mScrollConsumed;
4811                 final int x = scroller.getCurrX();
4812                 final int y = scroller.getCurrY();
4813                 int dx = x - mLastFlingX;
4814                 int dy = y - mLastFlingY;
4815                 int hresult = 0;
4816                 int vresult = 0;
4817                 mLastFlingX = x;
4818                 mLastFlingY = y;
4819                 int overscrollX = 0, overscrollY = 0;
4820 
4821                 if (dispatchNestedPreScroll(dx, dy, scrollConsumed, null, TYPE_NON_TOUCH)) {
4822                     dx -= scrollConsumed[0];
4823                     dy -= scrollConsumed[1];
4824                 }
4825 
4826                 if (mAdapter != null) {
4827                     eatRequestLayout();
4828                     onEnterLayoutOrScroll();
4829                     TraceCompat.beginSection(TRACE_SCROLL_TAG);
4830                     fillRemainingScrollValues(mState);
4831                     if (dx != 0) {
4832                         hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
4833                         overscrollX = dx - hresult;
4834                     }
4835                     if (dy != 0) {
4836                         vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
4837                         overscrollY = dy - vresult;
4838                     }
4839                     TraceCompat.endSection();
4840                     repositionShadowingViews();
4841 
4842                     onExitLayoutOrScroll();
4843                     resumeRequestLayout(false);
4844 
4845                     if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
4846                             && smoothScroller.isRunning()) {
4847                         final int adapterSize = mState.getItemCount();
4848                         if (adapterSize == 0) {
4849                             smoothScroller.stop();
4850                         } else if (smoothScroller.getTargetPosition() >= adapterSize) {
4851                             smoothScroller.setTargetPosition(adapterSize - 1);
4852                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4853                         } else {
4854                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4855                         }
4856                     }
4857                 }
4858                 if (!mItemDecorations.isEmpty()) {
4859                     invalidate();
4860                 }
4861                 if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
4862                     considerReleasingGlowsOnScroll(dx, dy);
4863                 }
4864 
4865                 if (!dispatchNestedScroll(hresult, vresult, overscrollX, overscrollY, null,
4866                         TYPE_NON_TOUCH)
4867                         && (overscrollX != 0 || overscrollY != 0)) {
4868                     final int vel = (int) scroller.getCurrVelocity();
4869 
4870                     int velX = 0;
4871                     if (overscrollX != x) {
4872                         velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
4873                     }
4874 
4875                     int velY = 0;
4876                     if (overscrollY != y) {
4877                         velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
4878                     }
4879 
4880                     if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
4881                         absorbGlows(velX, velY);
4882                     }
4883                     if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
4884                             && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
4885                         scroller.abortAnimation();
4886                     }
4887                 }
4888                 if (hresult != 0 || vresult != 0) {
4889                     dispatchOnScrolled(hresult, vresult);
4890                 }
4891 
4892                 if (!awakenScrollBars()) {
4893                     invalidate();
4894                 }
4895 
4896                 final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
4897                         && vresult == dy;
4898                 final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
4899                         && hresult == dx;
4900                 final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
4901                         || fullyConsumedVertical;
4902 
4903                 if (scroller.isFinished() || (!fullyConsumedAny
4904                         && !hasNestedScrollingParent(TYPE_NON_TOUCH))) {
4905                     // setting state to idle will stop this.
4906                     setScrollState(SCROLL_STATE_IDLE);
4907                     if (ALLOW_THREAD_GAP_WORK) {
4908                         mPrefetchRegistry.clearPrefetchPositions();
4909                     }
4910                     stopNestedScroll(TYPE_NON_TOUCH);
4911                 } else {
4912                     postOnAnimation();
4913                     if (mGapWorker != null) {
4914                         mGapWorker.postFromTraversal(RecyclerView.this, dx, dy);
4915                     }
4916                 }
4917             }
4918             // call this after the onAnimation is complete not to have inconsistent callbacks etc.
4919             if (smoothScroller != null) {
4920                 if (smoothScroller.isPendingInitialRun()) {
4921                     smoothScroller.onAnimation(0, 0);
4922                 }
4923                 if (!mReSchedulePostAnimationCallback) {
4924                     smoothScroller.stop(); //stop if it does not trigger any scroll
4925                 }
4926             }
4927             enableRunOnAnimationRequests();
4928         }
4929 
disableRunOnAnimationRequests()4930         private void disableRunOnAnimationRequests() {
4931             mReSchedulePostAnimationCallback = false;
4932             mEatRunOnAnimationRequest = true;
4933         }
4934 
enableRunOnAnimationRequests()4935         private void enableRunOnAnimationRequests() {
4936             mEatRunOnAnimationRequest = false;
4937             if (mReSchedulePostAnimationCallback) {
4938                 postOnAnimation();
4939             }
4940         }
4941 
postOnAnimation()4942         void postOnAnimation() {
4943             if (mEatRunOnAnimationRequest) {
4944                 mReSchedulePostAnimationCallback = true;
4945             } else {
4946                 removeCallbacks(this);
4947                 ViewCompat.postOnAnimation(RecyclerView.this, this);
4948             }
4949         }
4950 
fling(int velocityX, int velocityY)4951         public void fling(int velocityX, int velocityY) {
4952             setScrollState(SCROLL_STATE_SETTLING);
4953             mLastFlingX = mLastFlingY = 0;
4954             mScroller.fling(0, 0, velocityX, velocityY,
4955                     Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
4956             postOnAnimation();
4957         }
4958 
smoothScrollBy(int dx, int dy)4959         public void smoothScrollBy(int dx, int dy) {
4960             smoothScrollBy(dx, dy, 0, 0);
4961         }
4962 
smoothScrollBy(int dx, int dy, int vx, int vy)4963         public void smoothScrollBy(int dx, int dy, int vx, int vy) {
4964             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
4965         }
4966 
distanceInfluenceForSnapDuration(float f)4967         private float distanceInfluenceForSnapDuration(float f) {
4968             f -= 0.5f; // center the values about 0.
4969             f *= 0.3f * (float) Math.PI / 2.0f;
4970             return (float) Math.sin(f);
4971         }
4972 
computeScrollDuration(int dx, int dy, int vx, int vy)4973         private int computeScrollDuration(int dx, int dy, int vx, int vy) {
4974             final int absDx = Math.abs(dx);
4975             final int absDy = Math.abs(dy);
4976             final boolean horizontal = absDx > absDy;
4977             final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
4978             final int delta = (int) Math.sqrt(dx * dx + dy * dy);
4979             final int containerSize = horizontal ? getWidth() : getHeight();
4980             final int halfContainerSize = containerSize / 2;
4981             final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
4982             final float distance = halfContainerSize + halfContainerSize
4983                     * distanceInfluenceForSnapDuration(distanceRatio);
4984 
4985             final int duration;
4986             if (velocity > 0) {
4987                 duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
4988             } else {
4989                 float absDelta = (float) (horizontal ? absDx : absDy);
4990                 duration = (int) (((absDelta / containerSize) + 1) * 300);
4991             }
4992             return Math.min(duration, MAX_SCROLL_DURATION);
4993         }
4994 
smoothScrollBy(int dx, int dy, int duration)4995         public void smoothScrollBy(int dx, int dy, int duration) {
4996             smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
4997         }
4998 
smoothScrollBy(int dx, int dy, Interpolator interpolator)4999         public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
5000             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, 0, 0),
5001                     interpolator == null ? sQuinticInterpolator : interpolator);
5002         }
5003 
smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator)5004         public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
5005             if (mInterpolator != interpolator) {
5006                 mInterpolator = interpolator;
5007                 mScroller = new OverScroller(getContext(), interpolator);
5008             }
5009             setScrollState(SCROLL_STATE_SETTLING);
5010             mLastFlingX = mLastFlingY = 0;
5011             mScroller.startScroll(0, 0, dx, dy, duration);
5012             postOnAnimation();
5013         }
5014 
stop()5015         public void stop() {
5016             removeCallbacks(this);
5017             mScroller.abortAnimation();
5018         }
5019 
5020     }
5021 
repositionShadowingViews()5022     void repositionShadowingViews() {
5023         // Fix up shadow views used by change animations
5024         int count = mChildHelper.getChildCount();
5025         for (int i = 0; i < count; i++) {
5026             View view = mChildHelper.getChildAt(i);
5027             ViewHolder holder = getChildViewHolder(view);
5028             if (holder != null && holder.mShadowingHolder != null) {
5029                 View shadowingView = holder.mShadowingHolder.itemView;
5030                 int left = view.getLeft();
5031                 int top = view.getTop();
5032                 if (left != shadowingView.getLeft() ||  top != shadowingView.getTop()) {
5033                     shadowingView.layout(left, top,
5034                             left + shadowingView.getWidth(),
5035                             top + shadowingView.getHeight());
5036                 }
5037             }
5038         }
5039     }
5040 
5041     private class RecyclerViewDataObserver extends AdapterDataObserver {
RecyclerViewDataObserver()5042         RecyclerViewDataObserver() {
5043         }
5044 
5045         @Override
onChanged()5046         public void onChanged() {
5047             assertNotInLayoutOrScroll(null);
5048             mState.mStructureChanged = true;
5049 
5050             setDataSetChangedAfterLayout();
5051             if (!mAdapterHelper.hasPendingUpdates()) {
5052                 requestLayout();
5053             }
5054         }
5055 
5056         @Override
onItemRangeChanged(int positionStart, int itemCount, Object payload)5057         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
5058             assertNotInLayoutOrScroll(null);
5059             if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
5060                 triggerUpdateProcessor();
5061             }
5062         }
5063 
5064         @Override
onItemRangeInserted(int positionStart, int itemCount)5065         public void onItemRangeInserted(int positionStart, int itemCount) {
5066             assertNotInLayoutOrScroll(null);
5067             if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
5068                 triggerUpdateProcessor();
5069             }
5070         }
5071 
5072         @Override
onItemRangeRemoved(int positionStart, int itemCount)5073         public void onItemRangeRemoved(int positionStart, int itemCount) {
5074             assertNotInLayoutOrScroll(null);
5075             if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
5076                 triggerUpdateProcessor();
5077             }
5078         }
5079 
5080         @Override
onItemRangeMoved(int fromPosition, int toPosition, int itemCount)5081         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
5082             assertNotInLayoutOrScroll(null);
5083             if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
5084                 triggerUpdateProcessor();
5085             }
5086         }
5087 
triggerUpdateProcessor()5088         void triggerUpdateProcessor() {
5089             if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
5090                 ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
5091             } else {
5092                 mAdapterUpdateDuringMeasure = true;
5093                 requestLayout();
5094             }
5095         }
5096     }
5097 
5098     /**
5099      * RecycledViewPool lets you share Views between multiple RecyclerViews.
5100      * <p>
5101      * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
5102      * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
5103      * <p>
5104      * RecyclerView automatically creates a pool for itself if you don't provide one.
5105      *
5106      */
5107     public static class RecycledViewPool {
5108         private static final int DEFAULT_MAX_SCRAP = 5;
5109 
5110         /**
5111          * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
5112          *
5113          * Note that this tracks running averages of create/bind time across all RecyclerViews
5114          * (and, indirectly, Adapters) that use this pool.
5115          *
5116          * 1) This enables us to track average create and bind times across multiple adapters. Even
5117          * though create (and especially bind) may behave differently for different Adapter
5118          * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
5119          *
5120          * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
5121          * false for all other views of its type for the same deadline. This prevents items
5122          * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
5123          */
5124         static class ScrapData {
5125             ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
5126             int mMaxScrap = DEFAULT_MAX_SCRAP;
5127             long mCreateRunningAverageNs = 0;
5128             long mBindRunningAverageNs = 0;
5129         }
5130         SparseArray<ScrapData> mScrap = new SparseArray<>();
5131 
5132         private int mAttachCount = 0;
5133 
clear()5134         public void clear() {
5135             for (int i = 0; i < mScrap.size(); i++) {
5136                 ScrapData data = mScrap.valueAt(i);
5137                 data.mScrapHeap.clear();
5138             }
5139         }
5140 
setMaxRecycledViews(int viewType, int max)5141         public void setMaxRecycledViews(int viewType, int max) {
5142             ScrapData scrapData = getScrapDataForType(viewType);
5143             scrapData.mMaxScrap = max;
5144             final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
5145             if (scrapHeap != null) {
5146                 while (scrapHeap.size() > max) {
5147                     scrapHeap.remove(scrapHeap.size() - 1);
5148                 }
5149             }
5150         }
5151 
5152         /**
5153          * Returns the current number of Views held by the RecycledViewPool of the given view type.
5154          */
getRecycledViewCount(int viewType)5155         public int getRecycledViewCount(int viewType) {
5156             return getScrapDataForType(viewType).mScrapHeap.size();
5157         }
5158 
getRecycledView(int viewType)5159         public ViewHolder getRecycledView(int viewType) {
5160             final ScrapData scrapData = mScrap.get(viewType);
5161             if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
5162                 final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
5163                 return scrapHeap.remove(scrapHeap.size() - 1);
5164             }
5165             return null;
5166         }
5167 
size()5168         int size() {
5169             int count = 0;
5170             for (int i = 0; i < mScrap.size(); i++) {
5171                 ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
5172                 if (viewHolders != null) {
5173                     count += viewHolders.size();
5174                 }
5175             }
5176             return count;
5177         }
5178 
putRecycledView(ViewHolder scrap)5179         public void putRecycledView(ViewHolder scrap) {
5180             final int viewType = scrap.getItemViewType();
5181             final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
5182             if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
5183                 return;
5184             }
5185             if (DEBUG && scrapHeap.contains(scrap)) {
5186                 throw new IllegalArgumentException("this scrap item already exists");
5187             }
5188             scrap.resetInternal();
5189             scrapHeap.add(scrap);
5190         }
5191 
runningAverage(long oldAverage, long newValue)5192         long runningAverage(long oldAverage, long newValue) {
5193             if (oldAverage == 0) {
5194                 return newValue;
5195             }
5196             return (oldAverage / 4 * 3) + (newValue / 4);
5197         }
5198 
factorInCreateTime(int viewType, long createTimeNs)5199         void factorInCreateTime(int viewType, long createTimeNs) {
5200             ScrapData scrapData = getScrapDataForType(viewType);
5201             scrapData.mCreateRunningAverageNs = runningAverage(
5202                     scrapData.mCreateRunningAverageNs, createTimeNs);
5203         }
5204 
factorInBindTime(int viewType, long bindTimeNs)5205         void factorInBindTime(int viewType, long bindTimeNs) {
5206             ScrapData scrapData = getScrapDataForType(viewType);
5207             scrapData.mBindRunningAverageNs = runningAverage(
5208                     scrapData.mBindRunningAverageNs, bindTimeNs);
5209         }
5210 
willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs)5211         boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5212             long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
5213             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5214         }
5215 
willBindInTime(int viewType, long approxCurrentNs, long deadlineNs)5216         boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5217             long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
5218             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5219         }
5220 
attach(Adapter adapter)5221         void attach(Adapter adapter) {
5222             mAttachCount++;
5223         }
5224 
detach()5225         void detach() {
5226             mAttachCount--;
5227         }
5228 
5229 
5230         /**
5231          * Detaches the old adapter and attaches the new one.
5232          * <p>
5233          * RecycledViewPool will clear its cache if it has only one adapter attached and the new
5234          * adapter uses a different ViewHolder than the oldAdapter.
5235          *
5236          * @param oldAdapter The previous adapter instance. Will be detached.
5237          * @param newAdapter The new adapter instance. Will be attached.
5238          * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
5239          *                               ViewHolder and view types.
5240          */
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, boolean compatibleWithPrevious)5241         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
5242                 boolean compatibleWithPrevious) {
5243             if (oldAdapter != null) {
5244                 detach();
5245             }
5246             if (!compatibleWithPrevious && mAttachCount == 0) {
5247                 clear();
5248             }
5249             if (newAdapter != null) {
5250                 attach(newAdapter);
5251             }
5252         }
5253 
getScrapDataForType(int viewType)5254         private ScrapData getScrapDataForType(int viewType) {
5255             ScrapData scrapData = mScrap.get(viewType);
5256             if (scrapData == null) {
5257                 scrapData = new ScrapData();
5258                 mScrap.put(viewType, scrapData);
5259             }
5260             return scrapData;
5261         }
5262     }
5263 
5264     /**
5265      * Utility method for finding an internal RecyclerView, if present
5266      */
5267     @Nullable
findNestedRecyclerView(@onNull View view)5268     static RecyclerView findNestedRecyclerView(@NonNull View view) {
5269         if (!(view instanceof ViewGroup)) {
5270             return null;
5271         }
5272         if (view instanceof RecyclerView) {
5273             return (RecyclerView) view;
5274         }
5275         final ViewGroup parent = (ViewGroup) view;
5276         final int count = parent.getChildCount();
5277         for (int i = 0; i < count; i++) {
5278             final View child = parent.getChildAt(i);
5279             final RecyclerView descendant = findNestedRecyclerView(child);
5280             if (descendant != null) {
5281                 return descendant;
5282             }
5283         }
5284         return null;
5285     }
5286 
5287     /**
5288      * Utility method for clearing holder's internal RecyclerView, if present
5289      */
clearNestedRecyclerViewIfNotNested(@onNull ViewHolder holder)5290     static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
5291         if (holder.mNestedRecyclerView != null) {
5292             View item = holder.mNestedRecyclerView.get();
5293             while (item != null) {
5294                 if (item == holder.itemView) {
5295                     return; // match found, don't need to clear
5296                 }
5297 
5298                 ViewParent parent = item.getParent();
5299                 if (parent instanceof View) {
5300                     item = (View) parent;
5301                 } else {
5302                     item = null;
5303                 }
5304             }
5305             holder.mNestedRecyclerView = null; // not nested
5306         }
5307     }
5308 
5309     /**
5310      * Time base for deadline-aware work scheduling. Overridable for testing.
5311      *
5312      * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
5313      * isn't relevant.
5314      */
getNanoTime()5315     long getNanoTime() {
5316         if (ALLOW_THREAD_GAP_WORK) {
5317             return System.nanoTime();
5318         } else {
5319             return 0;
5320         }
5321     }
5322 
5323     /**
5324      * A Recycler is responsible for managing scrapped or detached item views for reuse.
5325      *
5326      * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
5327      * that has been marked for removal or reuse.</p>
5328      *
5329      * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
5330      * an adapter's data set representing the data at a given position or item ID.
5331      * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
5332      * If not, the view can be quickly reused by the LayoutManager with no further work.
5333      * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
5334      * may be repositioned by a LayoutManager without remeasurement.</p>
5335      */
5336     public final class Recycler {
5337         final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
5338         ArrayList<ViewHolder> mChangedScrap = null;
5339 
5340         final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
5341 
5342         private final List<ViewHolder>
5343                 mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
5344 
5345         private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
5346         int mViewCacheMax = DEFAULT_CACHE_SIZE;
5347 
5348         RecycledViewPool mRecyclerPool;
5349 
5350         private ViewCacheExtension mViewCacheExtension;
5351 
5352         static final int DEFAULT_CACHE_SIZE = 2;
5353 
5354         /**
5355          * Clear scrap views out of this recycler. Detached views contained within a
5356          * recycled view pool will remain.
5357          */
clear()5358         public void clear() {
5359             mAttachedScrap.clear();
5360             recycleAndClearCachedViews();
5361         }
5362 
5363         /**
5364          * Set the maximum number of detached, valid views we should retain for later use.
5365          *
5366          * @param viewCount Number of views to keep before sending views to the shared pool
5367          */
setViewCacheSize(int viewCount)5368         public void setViewCacheSize(int viewCount) {
5369             mRequestedCacheMax = viewCount;
5370             updateViewCacheSize();
5371         }
5372 
updateViewCacheSize()5373         void updateViewCacheSize() {
5374             int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
5375             mViewCacheMax = mRequestedCacheMax + extraCache;
5376 
5377             // first, try the views that can be recycled
5378             for (int i = mCachedViews.size() - 1;
5379                     i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
5380                 recycleCachedViewAt(i);
5381             }
5382         }
5383 
5384         /**
5385          * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
5386          *
5387          * @return List of ViewHolders in the scrap list.
5388          */
getScrapList()5389         public List<ViewHolder> getScrapList() {
5390             return mUnmodifiableAttachedScrap;
5391         }
5392 
5393         /**
5394          * Helper method for getViewForPosition.
5395          * <p>
5396          * Checks whether a given view holder can be used for the provided position.
5397          *
5398          * @param holder ViewHolder
5399          * @return true if ViewHolder matches the provided position, false otherwise
5400          */
validateViewHolderForOffsetPosition(ViewHolder holder)5401         boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
5402             // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
5403             // if it is not removed, verify the type and id.
5404             if (holder.isRemoved()) {
5405                 if (DEBUG && !mState.isPreLayout()) {
5406                     throw new IllegalStateException("should not receive a removed view unless it"
5407                             + " is pre layout" + exceptionLabel());
5408                 }
5409                 return mState.isPreLayout();
5410             }
5411             if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
5412                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
5413                         + "adapter position" + holder + exceptionLabel());
5414             }
5415             if (!mState.isPreLayout()) {
5416                 // don't check type if it is pre-layout.
5417                 final int type = mAdapter.getItemViewType(holder.mPosition);
5418                 if (type != holder.getItemViewType()) {
5419                     return false;
5420                 }
5421             }
5422             if (mAdapter.hasStableIds()) {
5423                 return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
5424             }
5425             return true;
5426         }
5427 
5428         /**
5429          * Attempts to bind view, and account for relevant timing information. If
5430          * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
5431          *
5432          * @param holder Holder to be bound.
5433          * @param offsetPosition Position of item to be bound.
5434          * @param position Pre-layout position of item to be bound.
5435          * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5436          *                   complete. If FOREVER_NS is passed, this method will not fail to
5437          *                   bind the holder.
5438          * @return
5439          */
tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition, int position, long deadlineNs)5440         private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,
5441                 int position, long deadlineNs) {
5442             holder.mOwnerRecyclerView = RecyclerView.this;
5443             final int viewType = holder.getItemViewType();
5444             long startBindNs = getNanoTime();
5445             if (deadlineNs != FOREVER_NS
5446                     && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
5447                 // abort - we have a deadline we can't meet
5448                 return false;
5449             }
5450             mAdapter.bindViewHolder(holder, offsetPosition);
5451             long endBindNs = getNanoTime();
5452             mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
5453             attachAccessibilityDelegateOnBind(holder);
5454             if (mState.isPreLayout()) {
5455                 holder.mPreLayoutPosition = position;
5456             }
5457             return true;
5458         }
5459 
5460         /**
5461          * Binds the given View to the position. The View can be a View previously retrieved via
5462          * {@link #getViewForPosition(int)} or created by
5463          * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
5464          * <p>
5465          * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
5466          * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
5467          * wants to handle its own recycling logic.
5468          * <p>
5469          * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
5470          * you don't need to call this method unless you want to bind this View to another position.
5471          *
5472          * @param view The view to update.
5473          * @param position The position of the item to bind to this View.
5474          */
bindViewToPosition(View view, int position)5475         public void bindViewToPosition(View view, int position) {
5476             ViewHolder holder = getChildViewHolderInt(view);
5477             if (holder == null) {
5478                 throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
5479                         + " pass arbitrary views to this method, they should be created by the "
5480                         + "Adapter" + exceptionLabel());
5481             }
5482             final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5483             if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5484                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5485                         + "position " + position + "(offset:" + offsetPosition + ")."
5486                         + "state:" + mState.getItemCount() + exceptionLabel());
5487             }
5488             tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
5489 
5490             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5491             final LayoutParams rvLayoutParams;
5492             if (lp == null) {
5493                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5494                 holder.itemView.setLayoutParams(rvLayoutParams);
5495             } else if (!checkLayoutParams(lp)) {
5496                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5497                 holder.itemView.setLayoutParams(rvLayoutParams);
5498             } else {
5499                 rvLayoutParams = (LayoutParams) lp;
5500             }
5501 
5502             rvLayoutParams.mInsetsDirty = true;
5503             rvLayoutParams.mViewHolder = holder;
5504             rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
5505         }
5506 
5507         /**
5508          * RecyclerView provides artificial position range (item count) in pre-layout state and
5509          * automatically maps these positions to {@link Adapter} positions when
5510          * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
5511          * <p>
5512          * Usually, LayoutManager does not need to worry about this. However, in some cases, your
5513          * LayoutManager may need to call some custom component with item positions in which
5514          * case you need the actual adapter position instead of the pre layout position. You
5515          * can use this method to convert a pre-layout position to adapter (post layout) position.
5516          * <p>
5517          * Note that if the provided position belongs to a deleted ViewHolder, this method will
5518          * return -1.
5519          * <p>
5520          * Calling this method in post-layout state returns the same value back.
5521          *
5522          * @param position The pre-layout position to convert. Must be greater or equal to 0 and
5523          *                 less than {@link State#getItemCount()}.
5524          */
convertPreLayoutPositionToPostLayout(int position)5525         public int convertPreLayoutPositionToPostLayout(int position) {
5526             if (position < 0 || position >= mState.getItemCount()) {
5527                 throw new IndexOutOfBoundsException("invalid position " + position + ". State "
5528                         + "item count is " + mState.getItemCount() + exceptionLabel());
5529             }
5530             if (!mState.isPreLayout()) {
5531                 return position;
5532             }
5533             return mAdapterHelper.findPositionOffset(position);
5534         }
5535 
5536         /**
5537          * Obtain a view initialized for the given position.
5538          *
5539          * This method should be used by {@link LayoutManager} implementations to obtain
5540          * views to represent data from an {@link Adapter}.
5541          * <p>
5542          * The Recycler may reuse a scrap or detached view from a shared pool if one is
5543          * available for the correct view type. If the adapter has not indicated that the
5544          * data at the given position has changed, the Recycler will attempt to hand back
5545          * a scrap view that was previously initialized for that data without rebinding.
5546          *
5547          * @param position Position to obtain a view for
5548          * @return A view representing the data at <code>position</code> from <code>adapter</code>
5549          */
getViewForPosition(int position)5550         public View getViewForPosition(int position) {
5551             return getViewForPosition(position, false);
5552         }
5553 
getViewForPosition(int position, boolean dryRun)5554         View getViewForPosition(int position, boolean dryRun) {
5555             return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
5556         }
5557 
5558         /**
5559          * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
5560          * cache, the RecycledViewPool, or creating it directly.
5561          * <p>
5562          * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
5563          * rather than constructing or binding a ViewHolder if it doesn't think it has time.
5564          * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
5565          * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
5566          * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
5567          *
5568          * @param position Position of ViewHolder to be returned.
5569          * @param dryRun True if the ViewHolder should not be removed from scrap/cache/
5570          * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5571          *                   complete. If FOREVER_NS is passed, this method will not fail to
5572          *                   create/bind the holder if needed.
5573          *
5574          * @return ViewHolder for requested position
5575          */
5576         @Nullable
tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs)5577         ViewHolder tryGetViewHolderForPositionByDeadline(int position,
5578                 boolean dryRun, long deadlineNs) {
5579             if (position < 0 || position >= mState.getItemCount()) {
5580                 throw new IndexOutOfBoundsException("Invalid item position " + position
5581                         + "(" + position + "). Item count:" + mState.getItemCount()
5582                         + exceptionLabel());
5583             }
5584             boolean fromScrapOrHiddenOrCache = false;
5585             ViewHolder holder = null;
5586             // 0) If there is a changed scrap, try to find from there
5587             if (mState.isPreLayout()) {
5588                 holder = getChangedScrapViewForPosition(position);
5589                 fromScrapOrHiddenOrCache = holder != null;
5590             }
5591             // 1) Find by position from scrap/hidden list/cache
5592             if (holder == null) {
5593                 holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
5594                 if (holder != null) {
5595                     if (!validateViewHolderForOffsetPosition(holder)) {
5596                         // recycle holder (and unscrap if relevant) since it can't be used
5597                         if (!dryRun) {
5598                             // we would like to recycle this but need to make sure it is not used by
5599                             // animation logic etc.
5600                             holder.addFlags(ViewHolder.FLAG_INVALID);
5601                             if (holder.isScrap()) {
5602                                 removeDetachedView(holder.itemView, false);
5603                                 holder.unScrap();
5604                             } else if (holder.wasReturnedFromScrap()) {
5605                                 holder.clearReturnedFromScrapFlag();
5606                             }
5607                             recycleViewHolderInternal(holder);
5608                         }
5609                         holder = null;
5610                     } else {
5611                         fromScrapOrHiddenOrCache = true;
5612                     }
5613                 }
5614             }
5615             if (holder == null) {
5616                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5617                 if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5618                     throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5619                             + "position " + position + "(offset:" + offsetPosition + ")."
5620                             + "state:" + mState.getItemCount() + exceptionLabel());
5621                 }
5622 
5623                 final int type = mAdapter.getItemViewType(offsetPosition);
5624                 // 2) Find from scrap/cache via stable ids, if exists
5625                 if (mAdapter.hasStableIds()) {
5626                     holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
5627                             type, dryRun);
5628                     if (holder != null) {
5629                         // update position
5630                         holder.mPosition = offsetPosition;
5631                         fromScrapOrHiddenOrCache = true;
5632                     }
5633                 }
5634                 if (holder == null && mViewCacheExtension != null) {
5635                     // We are NOT sending the offsetPosition because LayoutManager does not
5636                     // know it.
5637                     final View view = mViewCacheExtension
5638                             .getViewForPositionAndType(this, position, type);
5639                     if (view != null) {
5640                         holder = getChildViewHolder(view);
5641                         if (holder == null) {
5642                             throw new IllegalArgumentException("getViewForPositionAndType returned"
5643                                     + " a view which does not have a ViewHolder"
5644                                     + exceptionLabel());
5645                         } else if (holder.shouldIgnore()) {
5646                             throw new IllegalArgumentException("getViewForPositionAndType returned"
5647                                     + " a view that is ignored. You must call stopIgnoring before"
5648                                     + " returning this view." + exceptionLabel());
5649                         }
5650                     }
5651                 }
5652                 if (holder == null) { // fallback to pool
5653                     if (DEBUG) {
5654                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
5655                                 + position + ") fetching from shared pool");
5656                     }
5657                     holder = getRecycledViewPool().getRecycledView(type);
5658                     if (holder != null) {
5659                         holder.resetInternal();
5660                         if (FORCE_INVALIDATE_DISPLAY_LIST) {
5661                             invalidateDisplayListInt(holder);
5662                         }
5663                     }
5664                 }
5665                 if (holder == null) {
5666                     long start = getNanoTime();
5667                     if (deadlineNs != FOREVER_NS
5668                             && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
5669                         // abort - we have a deadline we can't meet
5670                         return null;
5671                     }
5672                     holder = mAdapter.createViewHolder(RecyclerView.this, type);
5673                     if (ALLOW_THREAD_GAP_WORK) {
5674                         // only bother finding nested RV if prefetching
5675                         RecyclerView innerView = findNestedRecyclerView(holder.itemView);
5676                         if (innerView != null) {
5677                             holder.mNestedRecyclerView = new WeakReference<>(innerView);
5678                         }
5679                     }
5680 
5681                     long end = getNanoTime();
5682                     mRecyclerPool.factorInCreateTime(type, end - start);
5683                     if (DEBUG) {
5684                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
5685                     }
5686                 }
5687             }
5688 
5689             // This is very ugly but the only place we can grab this information
5690             // before the View is rebound and returned to the LayoutManager for post layout ops.
5691             // We don't need this in pre-layout since the VH is not updated by the LM.
5692             if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
5693                     .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
5694                 holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5695                 if (mState.mRunSimpleAnimations) {
5696                     int changeFlags = ItemAnimator
5697                             .buildAdapterChangeFlagsForAnimations(holder);
5698                     changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
5699                     final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
5700                             holder, changeFlags, holder.getUnmodifiedPayloads());
5701                     recordAnimationInfoIfBouncedHiddenView(holder, info);
5702                 }
5703             }
5704 
5705             boolean bound = false;
5706             if (mState.isPreLayout() && holder.isBound()) {
5707                 // do not update unless we absolutely have to.
5708                 holder.mPreLayoutPosition = position;
5709             } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
5710                 if (DEBUG && holder.isRemoved()) {
5711                     throw new IllegalStateException("Removed holder should be bound and it should"
5712                             + " come here only in pre-layout. Holder: " + holder
5713                             + exceptionLabel());
5714                 }
5715                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5716                 bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
5717             }
5718 
5719             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5720             final LayoutParams rvLayoutParams;
5721             if (lp == null) {
5722                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5723                 holder.itemView.setLayoutParams(rvLayoutParams);
5724             } else if (!checkLayoutParams(lp)) {
5725                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5726                 holder.itemView.setLayoutParams(rvLayoutParams);
5727             } else {
5728                 rvLayoutParams = (LayoutParams) lp;
5729             }
5730             rvLayoutParams.mViewHolder = holder;
5731             rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
5732             return holder;
5733         }
5734 
attachAccessibilityDelegateOnBind(ViewHolder holder)5735         private void attachAccessibilityDelegateOnBind(ViewHolder holder) {
5736             if (isAccessibilityEnabled()) {
5737                 final View itemView = holder.itemView;
5738                 if (ViewCompat.getImportantForAccessibility(itemView)
5739                         == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
5740                     ViewCompat.setImportantForAccessibility(itemView,
5741                             ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
5742                 }
5743                 if (!ViewCompat.hasAccessibilityDelegate(itemView)) {
5744                     holder.addFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
5745                     ViewCompat.setAccessibilityDelegate(itemView,
5746                             mAccessibilityDelegate.getItemDelegate());
5747                 }
5748             }
5749         }
5750 
invalidateDisplayListInt(ViewHolder holder)5751         private void invalidateDisplayListInt(ViewHolder holder) {
5752             if (holder.itemView instanceof ViewGroup) {
5753                 invalidateDisplayListInt((ViewGroup) holder.itemView, false);
5754             }
5755         }
5756 
invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis)5757         private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
5758             for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
5759                 final View view = viewGroup.getChildAt(i);
5760                 if (view instanceof ViewGroup) {
5761                     invalidateDisplayListInt((ViewGroup) view, true);
5762                 }
5763             }
5764             if (!invalidateThis) {
5765                 return;
5766             }
5767             // we need to force it to become invisible
5768             if (viewGroup.getVisibility() == View.INVISIBLE) {
5769                 viewGroup.setVisibility(View.VISIBLE);
5770                 viewGroup.setVisibility(View.INVISIBLE);
5771             } else {
5772                 final int visibility = viewGroup.getVisibility();
5773                 viewGroup.setVisibility(View.INVISIBLE);
5774                 viewGroup.setVisibility(visibility);
5775             }
5776         }
5777 
5778         /**
5779          * Recycle a detached view. The specified view will be added to a pool of views
5780          * for later rebinding and reuse.
5781          *
5782          * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
5783          * View is scrapped, it will be removed from scrap list.</p>
5784          *
5785          * @param view Removed view for recycling
5786          * @see LayoutManager#removeAndRecycleView(View, Recycler)
5787          */
recycleView(View view)5788         public void recycleView(View view) {
5789             // This public recycle method tries to make view recycle-able since layout manager
5790             // intended to recycle this view (e.g. even if it is in scrap or change cache)
5791             ViewHolder holder = getChildViewHolderInt(view);
5792             if (holder.isTmpDetached()) {
5793                 removeDetachedView(view, false);
5794             }
5795             if (holder.isScrap()) {
5796                 holder.unScrap();
5797             } else if (holder.wasReturnedFromScrap()) {
5798                 holder.clearReturnedFromScrapFlag();
5799             }
5800             recycleViewHolderInternal(holder);
5801         }
5802 
5803         /**
5804          * Internally, use this method instead of {@link #recycleView(android.view.View)} to
5805          * catch potential bugs.
5806          * @param view
5807          */
recycleViewInternal(View view)5808         void recycleViewInternal(View view) {
5809             recycleViewHolderInternal(getChildViewHolderInt(view));
5810         }
5811 
recycleAndClearCachedViews()5812         void recycleAndClearCachedViews() {
5813             final int count = mCachedViews.size();
5814             for (int i = count - 1; i >= 0; i--) {
5815                 recycleCachedViewAt(i);
5816             }
5817             mCachedViews.clear();
5818             if (ALLOW_THREAD_GAP_WORK) {
5819                 mPrefetchRegistry.clearPrefetchPositions();
5820             }
5821         }
5822 
5823         /**
5824          * Recycles a cached view and removes the view from the list. Views are added to cache
5825          * if and only if they are recyclable, so this method does not check it again.
5826          * <p>
5827          * A small exception to this rule is when the view does not have an animator reference
5828          * but transient state is true (due to animations created outside ItemAnimator). In that
5829          * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
5830          * still recyclable since Adapter wants to do so.
5831          *
5832          * @param cachedViewIndex The index of the view in cached views list
5833          */
recycleCachedViewAt(int cachedViewIndex)5834         void recycleCachedViewAt(int cachedViewIndex) {
5835             if (DEBUG) {
5836                 Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
5837             }
5838             ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
5839             if (DEBUG) {
5840                 Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
5841             }
5842             addViewHolderToRecycledViewPool(viewHolder, true);
5843             mCachedViews.remove(cachedViewIndex);
5844         }
5845 
5846         /**
5847          * internal implementation checks if view is scrapped or attached and throws an exception
5848          * if so.
5849          * Public version un-scraps before calling recycle.
5850          */
recycleViewHolderInternal(ViewHolder holder)5851         void recycleViewHolderInternal(ViewHolder holder) {
5852             if (holder.isScrap() || holder.itemView.getParent() != null) {
5853                 throw new IllegalArgumentException(
5854                         "Scrapped or attached views may not be recycled. isScrap:"
5855                                 + holder.isScrap() + " isAttached:"
5856                                 + (holder.itemView.getParent() != null) + exceptionLabel());
5857             }
5858 
5859             if (holder.isTmpDetached()) {
5860                 throw new IllegalArgumentException("Tmp detached view should be removed "
5861                         + "from RecyclerView before it can be recycled: " + holder
5862                         + exceptionLabel());
5863             }
5864 
5865             if (holder.shouldIgnore()) {
5866                 throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
5867                         + " should first call stopIgnoringView(view) before calling recycle."
5868                         + exceptionLabel());
5869             }
5870             //noinspection unchecked
5871             final boolean transientStatePreventsRecycling = holder
5872                     .doesTransientStatePreventRecycling();
5873             final boolean forceRecycle = mAdapter != null
5874                     && transientStatePreventsRecycling
5875                     && mAdapter.onFailedToRecycleView(holder);
5876             boolean cached = false;
5877             boolean recycled = false;
5878             if (DEBUG && mCachedViews.contains(holder)) {
5879                 throw new IllegalArgumentException("cached view received recycle internal? "
5880                         + holder + exceptionLabel());
5881             }
5882             if (forceRecycle || holder.isRecyclable()) {
5883                 if (mViewCacheMax > 0
5884                         && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
5885                         | ViewHolder.FLAG_REMOVED
5886                         | ViewHolder.FLAG_UPDATE
5887                         | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
5888                     // Retire oldest cached view
5889                     int cachedViewSize = mCachedViews.size();
5890                     if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
5891                         recycleCachedViewAt(0);
5892                         cachedViewSize--;
5893                     }
5894 
5895                     int targetCacheIndex = cachedViewSize;
5896                     if (ALLOW_THREAD_GAP_WORK
5897                             && cachedViewSize > 0
5898                             && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
5899                         // when adding the view, skip past most recently prefetched views
5900                         int cacheIndex = cachedViewSize - 1;
5901                         while (cacheIndex >= 0) {
5902                             int cachedPos = mCachedViews.get(cacheIndex).mPosition;
5903                             if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
5904                                 break;
5905                             }
5906                             cacheIndex--;
5907                         }
5908                         targetCacheIndex = cacheIndex + 1;
5909                     }
5910                     mCachedViews.add(targetCacheIndex, holder);
5911                     cached = true;
5912                 }
5913                 if (!cached) {
5914                     addViewHolderToRecycledViewPool(holder, true);
5915                     recycled = true;
5916                 }
5917             } else {
5918                 // NOTE: A view can fail to be recycled when it is scrolled off while an animation
5919                 // runs. In this case, the item is eventually recycled by
5920                 // ItemAnimatorRestoreListener#onAnimationFinished.
5921 
5922                 // TODO: consider cancelling an animation when an item is removed scrollBy,
5923                 // to return it to the pool faster
5924                 if (DEBUG) {
5925                     Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
5926                             + "re-visit here. We are still removing it from animation lists"
5927                             + exceptionLabel());
5928                 }
5929             }
5930             // even if the holder is not removed, we still call this method so that it is removed
5931             // from view holder lists.
5932             mViewInfoStore.removeViewHolder(holder);
5933             if (!cached && !recycled && transientStatePreventsRecycling) {
5934                 holder.mOwnerRecyclerView = null;
5935             }
5936         }
5937 
5938         /**
5939          * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
5940          *
5941          * Pass false to dispatchRecycled for views that have not been bound.
5942          *
5943          * @param holder Holder to be added to the pool.
5944          * @param dispatchRecycled True to dispatch View recycled callbacks.
5945          */
addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled)5946         void addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) {
5947             clearNestedRecyclerViewIfNotNested(holder);
5948             if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE)) {
5949                 holder.setFlags(0, ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
5950                 ViewCompat.setAccessibilityDelegate(holder.itemView, null);
5951             }
5952             if (dispatchRecycled) {
5953                 dispatchViewRecycled(holder);
5954             }
5955             holder.mOwnerRecyclerView = null;
5956             getRecycledViewPool().putRecycledView(holder);
5957         }
5958 
5959         /**
5960          * Used as a fast path for unscrapping and recycling a view during a bulk operation.
5961          * The caller must call {@link #clearScrap()} when it's done to update the recycler's
5962          * internal bookkeeping.
5963          */
quickRecycleScrapView(View view)5964         void quickRecycleScrapView(View view) {
5965             final ViewHolder holder = getChildViewHolderInt(view);
5966             holder.mScrapContainer = null;
5967             holder.mInChangeScrap = false;
5968             holder.clearReturnedFromScrapFlag();
5969             recycleViewHolderInternal(holder);
5970         }
5971 
5972         /**
5973          * Mark an attached view as scrap.
5974          *
5975          * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
5976          * for rebinding and reuse. Requests for a view for a given position may return a
5977          * reused or rebound scrap view instance.</p>
5978          *
5979          * @param view View to scrap
5980          */
scrapView(View view)5981         void scrapView(View view) {
5982             final ViewHolder holder = getChildViewHolderInt(view);
5983             if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
5984                     || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
5985                 if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
5986                     throw new IllegalArgumentException("Called scrap view with an invalid view."
5987                             + " Invalid views cannot be reused from scrap, they should rebound from"
5988                             + " recycler pool." + exceptionLabel());
5989                 }
5990                 holder.setScrapContainer(this, false);
5991                 mAttachedScrap.add(holder);
5992             } else {
5993                 if (mChangedScrap == null) {
5994                     mChangedScrap = new ArrayList<ViewHolder>();
5995                 }
5996                 holder.setScrapContainer(this, true);
5997                 mChangedScrap.add(holder);
5998             }
5999         }
6000 
6001         /**
6002          * Remove a previously scrapped view from the pool of eligible scrap.
6003          *
6004          * <p>This view will no longer be eligible for reuse until re-scrapped or
6005          * until it is explicitly removed and recycled.</p>
6006          */
unscrapView(ViewHolder holder)6007         void unscrapView(ViewHolder holder) {
6008             if (holder.mInChangeScrap) {
6009                 mChangedScrap.remove(holder);
6010             } else {
6011                 mAttachedScrap.remove(holder);
6012             }
6013             holder.mScrapContainer = null;
6014             holder.mInChangeScrap = false;
6015             holder.clearReturnedFromScrapFlag();
6016         }
6017 
getScrapCount()6018         int getScrapCount() {
6019             return mAttachedScrap.size();
6020         }
6021 
getScrapViewAt(int index)6022         View getScrapViewAt(int index) {
6023             return mAttachedScrap.get(index).itemView;
6024         }
6025 
clearScrap()6026         void clearScrap() {
6027             mAttachedScrap.clear();
6028             if (mChangedScrap != null) {
6029                 mChangedScrap.clear();
6030             }
6031         }
6032 
getChangedScrapViewForPosition(int position)6033         ViewHolder getChangedScrapViewForPosition(int position) {
6034             // If pre-layout, check the changed scrap for an exact match.
6035             final int changedScrapSize;
6036             if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
6037                 return null;
6038             }
6039             // find by position
6040             for (int i = 0; i < changedScrapSize; i++) {
6041                 final ViewHolder holder = mChangedScrap.get(i);
6042                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
6043                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6044                     return holder;
6045                 }
6046             }
6047             // find by id
6048             if (mAdapter.hasStableIds()) {
6049                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
6050                 if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
6051                     final long id = mAdapter.getItemId(offsetPosition);
6052                     for (int i = 0; i < changedScrapSize; i++) {
6053                         final ViewHolder holder = mChangedScrap.get(i);
6054                         if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
6055                             holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6056                             return holder;
6057                         }
6058                     }
6059                 }
6060             }
6061             return null;
6062         }
6063 
6064         /**
6065          * Returns a view for the position either from attach scrap, hidden children, or cache.
6066          *
6067          * @param position Item position
6068          * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
6069          * @return a ViewHolder that can be re-used for this position.
6070          */
getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun)6071         ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
6072             final int scrapCount = mAttachedScrap.size();
6073 
6074             // Try first for an exact, non-invalid match from scrap.
6075             for (int i = 0; i < scrapCount; i++) {
6076                 final ViewHolder holder = mAttachedScrap.get(i);
6077                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
6078                         && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
6079                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6080                     return holder;
6081                 }
6082             }
6083 
6084             if (!dryRun) {
6085                 View view = mChildHelper.findHiddenNonRemovedView(position);
6086                 if (view != null) {
6087                     // This View is good to be used. We just need to unhide, detach and move to the
6088                     // scrap list.
6089                     final ViewHolder vh = getChildViewHolderInt(view);
6090                     mChildHelper.unhide(view);
6091                     int layoutIndex = mChildHelper.indexOfChild(view);
6092                     if (layoutIndex == RecyclerView.NO_POSITION) {
6093                         throw new IllegalStateException("layout index should not be -1 after "
6094                                 + "unhiding a view:" + vh + exceptionLabel());
6095                     }
6096                     mChildHelper.detachViewFromParent(layoutIndex);
6097                     scrapView(view);
6098                     vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
6099                             | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
6100                     return vh;
6101                 }
6102             }
6103 
6104             // Search in our first-level recycled view cache.
6105             final int cacheSize = mCachedViews.size();
6106             for (int i = 0; i < cacheSize; i++) {
6107                 final ViewHolder holder = mCachedViews.get(i);
6108                 // invalid view holders may be in cache if adapter has stable ids as they can be
6109                 // retrieved via getScrapOrCachedViewForId
6110                 if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
6111                     if (!dryRun) {
6112                         mCachedViews.remove(i);
6113                     }
6114                     if (DEBUG) {
6115                         Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
6116                                 + ") found match in cache: " + holder);
6117                     }
6118                     return holder;
6119                 }
6120             }
6121             return null;
6122         }
6123 
getScrapOrCachedViewForId(long id, int type, boolean dryRun)6124         ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
6125             // Look in our attached views first
6126             final int count = mAttachedScrap.size();
6127             for (int i = count - 1; i >= 0; i--) {
6128                 final ViewHolder holder = mAttachedScrap.get(i);
6129                 if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
6130                     if (type == holder.getItemViewType()) {
6131                         holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6132                         if (holder.isRemoved()) {
6133                             // this might be valid in two cases:
6134                             // > item is removed but we are in pre-layout pass
6135                             // >> do nothing. return as is. make sure we don't rebind
6136                             // > item is removed then added to another position and we are in
6137                             // post layout.
6138                             // >> remove removed and invalid flags, add update flag to rebind
6139                             // because item was invisible to us and we don't know what happened in
6140                             // between.
6141                             if (!mState.isPreLayout()) {
6142                                 holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
6143                                         | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
6144                             }
6145                         }
6146                         return holder;
6147                     } else if (!dryRun) {
6148                         // if we are running animations, it is actually better to keep it in scrap
6149                         // but this would force layout manager to lay it out which would be bad.
6150                         // Recycle this scrap. Type mismatch.
6151                         mAttachedScrap.remove(i);
6152                         removeDetachedView(holder.itemView, false);
6153                         quickRecycleScrapView(holder.itemView);
6154                     }
6155                 }
6156             }
6157 
6158             // Search the first-level cache
6159             final int cacheSize = mCachedViews.size();
6160             for (int i = cacheSize - 1; i >= 0; i--) {
6161                 final ViewHolder holder = mCachedViews.get(i);
6162                 if (holder.getItemId() == id) {
6163                     if (type == holder.getItemViewType()) {
6164                         if (!dryRun) {
6165                             mCachedViews.remove(i);
6166                         }
6167                         return holder;
6168                     } else if (!dryRun) {
6169                         recycleCachedViewAt(i);
6170                         return null;
6171                     }
6172                 }
6173             }
6174             return null;
6175         }
6176 
dispatchViewRecycled(ViewHolder holder)6177         void dispatchViewRecycled(ViewHolder holder) {
6178             if (mRecyclerListener != null) {
6179                 mRecyclerListener.onViewRecycled(holder);
6180             }
6181             if (mAdapter != null) {
6182                 mAdapter.onViewRecycled(holder);
6183             }
6184             if (mState != null) {
6185                 mViewInfoStore.removeViewHolder(holder);
6186             }
6187             if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
6188         }
6189 
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, boolean compatibleWithPrevious)6190         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
6191                 boolean compatibleWithPrevious) {
6192             clear();
6193             getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
6194         }
6195 
offsetPositionRecordsForMove(int from, int to)6196         void offsetPositionRecordsForMove(int from, int to) {
6197             final int start, end, inBetweenOffset;
6198             if (from < to) {
6199                 start = from;
6200                 end = to;
6201                 inBetweenOffset = -1;
6202             } else {
6203                 start = to;
6204                 end = from;
6205                 inBetweenOffset = 1;
6206             }
6207             final int cachedCount = mCachedViews.size();
6208             for (int i = 0; i < cachedCount; i++) {
6209                 final ViewHolder holder = mCachedViews.get(i);
6210                 if (holder == null || holder.mPosition < start || holder.mPosition > end) {
6211                     continue;
6212                 }
6213                 if (holder.mPosition == from) {
6214                     holder.offsetPosition(to - from, false);
6215                 } else {
6216                     holder.offsetPosition(inBetweenOffset, false);
6217                 }
6218                 if (DEBUG) {
6219                     Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
6220                             + holder);
6221                 }
6222             }
6223         }
6224 
offsetPositionRecordsForInsert(int insertedAt, int count)6225         void offsetPositionRecordsForInsert(int insertedAt, int count) {
6226             final int cachedCount = mCachedViews.size();
6227             for (int i = 0; i < cachedCount; i++) {
6228                 final ViewHolder holder = mCachedViews.get(i);
6229                 if (holder != null && holder.mPosition >= insertedAt) {
6230                     if (DEBUG) {
6231                         Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder "
6232                                 + holder + " now at position " + (holder.mPosition + count));
6233                     }
6234                     holder.offsetPosition(count, true);
6235                 }
6236             }
6237         }
6238 
6239         /**
6240          * @param removedFrom Remove start index
6241          * @param count Remove count
6242          * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
6243          *                         false, they'll be applied before the second layout pass
6244          */
offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout)6245         void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
6246             final int removedEnd = removedFrom + count;
6247             final int cachedCount = mCachedViews.size();
6248             for (int i = cachedCount - 1; i >= 0; i--) {
6249                 final ViewHolder holder = mCachedViews.get(i);
6250                 if (holder != null) {
6251                     if (holder.mPosition >= removedEnd) {
6252                         if (DEBUG) {
6253                             Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
6254                                     + " holder " + holder + " now at position "
6255                                     + (holder.mPosition - count));
6256                         }
6257                         holder.offsetPosition(-count, applyToPreLayout);
6258                     } else if (holder.mPosition >= removedFrom) {
6259                         // Item for this view was removed. Dump it from the cache.
6260                         holder.addFlags(ViewHolder.FLAG_REMOVED);
6261                         recycleCachedViewAt(i);
6262                     }
6263                 }
6264             }
6265         }
6266 
setViewCacheExtension(ViewCacheExtension extension)6267         void setViewCacheExtension(ViewCacheExtension extension) {
6268             mViewCacheExtension = extension;
6269         }
6270 
setRecycledViewPool(RecycledViewPool pool)6271         void setRecycledViewPool(RecycledViewPool pool) {
6272             if (mRecyclerPool != null) {
6273                 mRecyclerPool.detach();
6274             }
6275             mRecyclerPool = pool;
6276             if (pool != null) {
6277                 mRecyclerPool.attach(getAdapter());
6278             }
6279         }
6280 
getRecycledViewPool()6281         RecycledViewPool getRecycledViewPool() {
6282             if (mRecyclerPool == null) {
6283                 mRecyclerPool = new RecycledViewPool();
6284             }
6285             return mRecyclerPool;
6286         }
6287 
viewRangeUpdate(int positionStart, int itemCount)6288         void viewRangeUpdate(int positionStart, int itemCount) {
6289             final int positionEnd = positionStart + itemCount;
6290             final int cachedCount = mCachedViews.size();
6291             for (int i = cachedCount - 1; i >= 0; i--) {
6292                 final ViewHolder holder = mCachedViews.get(i);
6293                 if (holder == null) {
6294                     continue;
6295                 }
6296 
6297                 final int pos = holder.mPosition;
6298                 if (pos >= positionStart && pos < positionEnd) {
6299                     holder.addFlags(ViewHolder.FLAG_UPDATE);
6300                     recycleCachedViewAt(i);
6301                     // cached views should not be flagged as changed because this will cause them
6302                     // to animate when they are returned from cache.
6303                 }
6304             }
6305         }
6306 
markKnownViewsInvalid()6307         void markKnownViewsInvalid() {
6308             if (mAdapter != null && mAdapter.hasStableIds()) {
6309                 final int cachedCount = mCachedViews.size();
6310                 for (int i = 0; i < cachedCount; i++) {
6311                     final ViewHolder holder = mCachedViews.get(i);
6312                     if (holder != null) {
6313                         holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
6314                         holder.addChangePayload(null);
6315                     }
6316                 }
6317             } else {
6318                 // we cannot re-use cached views in this case. Recycle them all
6319                 recycleAndClearCachedViews();
6320             }
6321         }
6322 
clearOldPositions()6323         void clearOldPositions() {
6324             final int cachedCount = mCachedViews.size();
6325             for (int i = 0; i < cachedCount; i++) {
6326                 final ViewHolder holder = mCachedViews.get(i);
6327                 holder.clearOldPosition();
6328             }
6329             final int scrapCount = mAttachedScrap.size();
6330             for (int i = 0; i < scrapCount; i++) {
6331                 mAttachedScrap.get(i).clearOldPosition();
6332             }
6333             if (mChangedScrap != null) {
6334                 final int changedScrapCount = mChangedScrap.size();
6335                 for (int i = 0; i < changedScrapCount; i++) {
6336                     mChangedScrap.get(i).clearOldPosition();
6337                 }
6338             }
6339         }
6340 
markItemDecorInsetsDirty()6341         void markItemDecorInsetsDirty() {
6342             final int cachedCount = mCachedViews.size();
6343             for (int i = 0; i < cachedCount; i++) {
6344                 final ViewHolder holder = mCachedViews.get(i);
6345                 LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
6346                 if (layoutParams != null) {
6347                     layoutParams.mInsetsDirty = true;
6348                 }
6349             }
6350         }
6351     }
6352 
6353     /**
6354      * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
6355      * be controlled by the developer.
6356      * <p>
6357      * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
6358      * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
6359      * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
6360      * {@link RecycledViewPool}.
6361      * <p>
6362      * Note that, Recycler never sends Views to this method to be cached. It is developers
6363      * responsibility to decide whether they want to keep their Views in this custom cache or let
6364      * the default recycling policy handle it.
6365      */
6366     public abstract static class ViewCacheExtension {
6367 
6368         /**
6369          * Returns a View that can be binded to the given Adapter position.
6370          * <p>
6371          * This method should <b>not</b> create a new View. Instead, it is expected to return
6372          * an already created View that can be re-used for the given type and position.
6373          * If the View is marked as ignored, it should first call
6374          * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
6375          * <p>
6376          * RecyclerView will re-bind the returned View to the position if necessary.
6377          *
6378          * @param recycler The Recycler that can be used to bind the View
6379          * @param position The adapter position
6380          * @param type     The type of the View, defined by adapter
6381          * @return A View that is bound to the given position or NULL if there is no View to re-use
6382          * @see LayoutManager#ignoreView(View)
6383          */
getViewForPositionAndType(Recycler recycler, int position, int type)6384         public abstract View getViewForPositionAndType(Recycler recycler, int position, int type);
6385     }
6386 
6387     /**
6388      * Base class for an Adapter
6389      *
6390      * <p>Adapters provide a binding from an app-specific data set to views that are displayed
6391      * within a {@link RecyclerView}.</p>
6392      *
6393      * @param <VH> A class that extends ViewHolder that will be used by the adapter.
6394      */
6395     public abstract static class Adapter<VH extends ViewHolder> {
6396         private final AdapterDataObservable mObservable = new AdapterDataObservable();
6397         private boolean mHasStableIds = false;
6398 
6399         /**
6400          * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
6401          * an item.
6402          * <p>
6403          * This new ViewHolder should be constructed with a new View that can represent the items
6404          * of the given type. You can either create a new View manually or inflate it from an XML
6405          * layout file.
6406          * <p>
6407          * The new ViewHolder will be used to display items of the adapter using
6408          * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
6409          * different items in the data set, it is a good idea to cache references to sub views of
6410          * the View to avoid unnecessary {@link View#findViewById(int)} calls.
6411          *
6412          * @param parent The ViewGroup into which the new View will be added after it is bound to
6413          *               an adapter position.
6414          * @param viewType The view type of the new View.
6415          *
6416          * @return A new ViewHolder that holds a View of the given view type.
6417          * @see #getItemViewType(int)
6418          * @see #onBindViewHolder(ViewHolder, int)
6419          */
onCreateViewHolder(ViewGroup parent, int viewType)6420         public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
6421 
6422         /**
6423          * Called by RecyclerView to display the data at the specified position. This method should
6424          * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
6425          * position.
6426          * <p>
6427          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6428          * again if the position of the item changes in the data set unless the item itself is
6429          * invalidated or the new position cannot be determined. For this reason, you should only
6430          * use the <code>position</code> parameter while acquiring the related data item inside
6431          * this method and should not keep a copy of it. If you need the position of an item later
6432          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6433          * have the updated adapter position.
6434          *
6435          * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
6436          * handle efficient partial bind.
6437          *
6438          * @param holder The ViewHolder which should be updated to represent the contents of the
6439          *        item at the given position in the data set.
6440          * @param position The position of the item within the adapter's data set.
6441          */
onBindViewHolder(VH holder, int position)6442         public abstract void onBindViewHolder(VH holder, int position);
6443 
6444         /**
6445          * Called by RecyclerView to display the data at the specified position. This method
6446          * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
6447          * the given position.
6448          * <p>
6449          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6450          * again if the position of the item changes in the data set unless the item itself is
6451          * invalidated or the new position cannot be determined. For this reason, you should only
6452          * use the <code>position</code> parameter while acquiring the related data item inside
6453          * this method and should not keep a copy of it. If you need the position of an item later
6454          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6455          * have the updated adapter position.
6456          * <p>
6457          * Partial bind vs full bind:
6458          * <p>
6459          * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
6460          * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
6461          * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
6462          * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
6463          * Adapter should not assume that the payload passed in notify methods will be received by
6464          * onBindViewHolder().  For example when the view is not attached to the screen, the
6465          * payload in notifyItemChange() will be simply dropped.
6466          *
6467          * @param holder The ViewHolder which should be updated to represent the contents of the
6468          *               item at the given position in the data set.
6469          * @param position The position of the item within the adapter's data set.
6470          * @param payloads A non-null list of merged payloads. Can be empty list if requires full
6471          *                 update.
6472          */
onBindViewHolder(VH holder, int position, List<Object> payloads)6473         public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
6474             onBindViewHolder(holder, position);
6475         }
6476 
6477         /**
6478          * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
6479          * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
6480          *
6481          * @see #onCreateViewHolder(ViewGroup, int)
6482          */
createViewHolder(ViewGroup parent, int viewType)6483         public final VH createViewHolder(ViewGroup parent, int viewType) {
6484             TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
6485             final VH holder = onCreateViewHolder(parent, viewType);
6486             holder.mItemViewType = viewType;
6487             TraceCompat.endSection();
6488             return holder;
6489         }
6490 
6491         /**
6492          * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
6493          * {@link ViewHolder} contents with the item at the given position and also sets up some
6494          * private fields to be used by RecyclerView.
6495          *
6496          * @see #onBindViewHolder(ViewHolder, int)
6497          */
bindViewHolder(VH holder, int position)6498         public final void bindViewHolder(VH holder, int position) {
6499             holder.mPosition = position;
6500             if (hasStableIds()) {
6501                 holder.mItemId = getItemId(position);
6502             }
6503             holder.setFlags(ViewHolder.FLAG_BOUND,
6504                     ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
6505                             | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
6506             TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
6507             onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
6508             holder.clearPayload();
6509             final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
6510             if (layoutParams instanceof RecyclerView.LayoutParams) {
6511                 ((LayoutParams) layoutParams).mInsetsDirty = true;
6512             }
6513             TraceCompat.endSection();
6514         }
6515 
6516         /**
6517          * Return the view type of the item at <code>position</code> for the purposes
6518          * of view recycling.
6519          *
6520          * <p>The default implementation of this method returns 0, making the assumption of
6521          * a single view type for the adapter. Unlike ListView adapters, types need not
6522          * be contiguous. Consider using id resources to uniquely identify item view types.
6523          *
6524          * @param position position to query
6525          * @return integer value identifying the type of the view needed to represent the item at
6526          *                 <code>position</code>. Type codes need not be contiguous.
6527          */
getItemViewType(int position)6528         public int getItemViewType(int position) {
6529             return 0;
6530         }
6531 
6532         /**
6533          * Indicates whether each item in the data set can be represented with a unique identifier
6534          * of type {@link java.lang.Long}.
6535          *
6536          * @param hasStableIds Whether items in data set have unique identifiers or not.
6537          * @see #hasStableIds()
6538          * @see #getItemId(int)
6539          */
setHasStableIds(boolean hasStableIds)6540         public void setHasStableIds(boolean hasStableIds) {
6541             if (hasObservers()) {
6542                 throw new IllegalStateException("Cannot change whether this adapter has "
6543                         + "stable IDs while the adapter has registered observers.");
6544             }
6545             mHasStableIds = hasStableIds;
6546         }
6547 
6548         /**
6549          * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
6550          * would return false this method should return {@link #NO_ID}. The default implementation
6551          * of this method returns {@link #NO_ID}.
6552          *
6553          * @param position Adapter position to query
6554          * @return the stable ID of the item at position
6555          */
getItemId(int position)6556         public long getItemId(int position) {
6557             return NO_ID;
6558         }
6559 
6560         /**
6561          * Returns the total number of items in the data set held by the adapter.
6562          *
6563          * @return The total number of items in this adapter.
6564          */
getItemCount()6565         public abstract int getItemCount();
6566 
6567         /**
6568          * Returns true if this adapter publishes a unique <code>long</code> value that can
6569          * act as a key for the item at a given position in the data set. If that item is relocated
6570          * in the data set, the ID returned for that item should be the same.
6571          *
6572          * @return true if this adapter's items have stable IDs
6573          */
hasStableIds()6574         public final boolean hasStableIds() {
6575             return mHasStableIds;
6576         }
6577 
6578         /**
6579          * Called when a view created by this adapter has been recycled.
6580          *
6581          * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
6582          * needs to be attached to its parent {@link RecyclerView}. This can be because it has
6583          * fallen out of visibility or a set of cached views represented by views still
6584          * attached to the parent RecyclerView. If an item view has large or expensive data
6585          * bound to it such as large bitmaps, this may be a good place to release those
6586          * resources.</p>
6587          * <p>
6588          * RecyclerView calls this method right before clearing ViewHolder's internal data and
6589          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
6590          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
6591          * its adapter position.
6592          *
6593          * @param holder The ViewHolder for the view being recycled
6594          */
onViewRecycled(VH holder)6595         public void onViewRecycled(VH holder) {
6596         }
6597 
6598         /**
6599          * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
6600          * due to its transient state. Upon receiving this callback, Adapter can clear the
6601          * animation(s) that effect the View's transient state and return <code>true</code> so that
6602          * the View can be recycled. Keep in mind that the View in question is already removed from
6603          * the RecyclerView.
6604          * <p>
6605          * In some cases, it is acceptable to recycle a View although it has transient state. Most
6606          * of the time, this is a case where the transient state will be cleared in
6607          * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
6608          * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
6609          * value of this method to decide whether the View should be recycled or not.
6610          * <p>
6611          * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
6612          * should never receive this callback because RecyclerView keeps those Views as children
6613          * until their animations are complete. This callback is useful when children of the item
6614          * views create animations which may not be easy to implement using an {@link ItemAnimator}.
6615          * <p>
6616          * You should <em>never</em> fix this issue by calling
6617          * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
6618          * <code>holder.itemView.setHasTransientState(true);</code>. Each
6619          * <code>View.setHasTransientState(true)</code> call must be matched by a
6620          * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
6621          * may become inconsistent. You should always prefer to end or cancel animations that are
6622          * triggering the transient state instead of handling it manually.
6623          *
6624          * @param holder The ViewHolder containing the View that could not be recycled due to its
6625          *               transient state.
6626          * @return True if the View should be recycled, false otherwise. Note that if this method
6627          * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
6628          * the View and recycle it regardless. If this method returns <code>false</code>,
6629          * RecyclerView will check the View's transient state again before giving a final decision.
6630          * Default implementation returns false.
6631          */
onFailedToRecycleView(VH holder)6632         public boolean onFailedToRecycleView(VH holder) {
6633             return false;
6634         }
6635 
6636         /**
6637          * Called when a view created by this adapter has been attached to a window.
6638          *
6639          * <p>This can be used as a reasonable signal that the view is about to be seen
6640          * by the user. If the adapter previously freed any resources in
6641          * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
6642          * those resources should be restored here.</p>
6643          *
6644          * @param holder Holder of the view being attached
6645          */
onViewAttachedToWindow(VH holder)6646         public void onViewAttachedToWindow(VH holder) {
6647         }
6648 
6649         /**
6650          * Called when a view created by this adapter has been detached from its window.
6651          *
6652          * <p>Becoming detached from the window is not necessarily a permanent condition;
6653          * the consumer of an Adapter's views may choose to cache views offscreen while they
6654          * are not visible, attaching and detaching them as appropriate.</p>
6655          *
6656          * @param holder Holder of the view being detached
6657          */
onViewDetachedFromWindow(VH holder)6658         public void onViewDetachedFromWindow(VH holder) {
6659         }
6660 
6661         /**
6662          * Returns true if one or more observers are attached to this adapter.
6663          *
6664          * @return true if this adapter has observers
6665          */
hasObservers()6666         public final boolean hasObservers() {
6667             return mObservable.hasObservers();
6668         }
6669 
6670         /**
6671          * Register a new observer to listen for data changes.
6672          *
6673          * <p>The adapter may publish a variety of events describing specific changes.
6674          * Not all adapters may support all change types and some may fall back to a generic
6675          * {@link android.support.v7.widget.RecyclerView.AdapterDataObserver#onChanged()
6676          * "something changed"} event if more specific data is not available.</p>
6677          *
6678          * <p>Components registering observers with an adapter are responsible for
6679          * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6680          * unregistering} those observers when finished.</p>
6681          *
6682          * @param observer Observer to register
6683          *
6684          * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6685          */
registerAdapterDataObserver(AdapterDataObserver observer)6686         public void registerAdapterDataObserver(AdapterDataObserver observer) {
6687             mObservable.registerObserver(observer);
6688         }
6689 
6690         /**
6691          * Unregister an observer currently listening for data changes.
6692          *
6693          * <p>The unregistered observer will no longer receive events about changes
6694          * to the adapter.</p>
6695          *
6696          * @param observer Observer to unregister
6697          *
6698          * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
6699          */
unregisterAdapterDataObserver(AdapterDataObserver observer)6700         public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
6701             mObservable.unregisterObserver(observer);
6702         }
6703 
6704         /**
6705          * Called by RecyclerView when it starts observing this Adapter.
6706          * <p>
6707          * Keep in mind that same adapter may be observed by multiple RecyclerViews.
6708          *
6709          * @param recyclerView The RecyclerView instance which started observing this adapter.
6710          * @see #onDetachedFromRecyclerView(RecyclerView)
6711          */
onAttachedToRecyclerView(RecyclerView recyclerView)6712         public void onAttachedToRecyclerView(RecyclerView recyclerView) {
6713         }
6714 
6715         /**
6716          * Called by RecyclerView when it stops observing this Adapter.
6717          *
6718          * @param recyclerView The RecyclerView instance which stopped observing this adapter.
6719          * @see #onAttachedToRecyclerView(RecyclerView)
6720          */
onDetachedFromRecyclerView(RecyclerView recyclerView)6721         public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
6722         }
6723 
6724         /**
6725          * Notify any registered observers that the data set has changed.
6726          *
6727          * <p>There are two different classes of data change events, item changes and structural
6728          * changes. Item changes are when a single item has its data updated but no positional
6729          * changes have occurred. Structural changes are when items are inserted, removed or moved
6730          * within the data set.</p>
6731          *
6732          * <p>This event does not specify what about the data set has changed, forcing
6733          * any observers to assume that all existing items and structure may no longer be valid.
6734          * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
6735          *
6736          * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
6737          * for adapters that report that they have {@link #hasStableIds() stable IDs} when
6738          * this method is used. This can help for the purposes of animation and visual
6739          * object persistence but individual item views will still need to be rebound
6740          * and relaid out.</p>
6741          *
6742          * <p>If you are writing an adapter it will always be more efficient to use the more
6743          * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
6744          * as a last resort.</p>
6745          *
6746          * @see #notifyItemChanged(int)
6747          * @see #notifyItemInserted(int)
6748          * @see #notifyItemRemoved(int)
6749          * @see #notifyItemRangeChanged(int, int)
6750          * @see #notifyItemRangeInserted(int, int)
6751          * @see #notifyItemRangeRemoved(int, int)
6752          */
notifyDataSetChanged()6753         public final void notifyDataSetChanged() {
6754             mObservable.notifyChanged();
6755         }
6756 
6757         /**
6758          * Notify any registered observers that the item at <code>position</code> has changed.
6759          * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
6760          *
6761          * <p>This is an item change event, not a structural change event. It indicates that any
6762          * reflection of the data at <code>position</code> is out of date and should be updated.
6763          * The item at <code>position</code> retains the same identity.</p>
6764          *
6765          * @param position Position of the item that has changed
6766          *
6767          * @see #notifyItemRangeChanged(int, int)
6768          */
notifyItemChanged(int position)6769         public final void notifyItemChanged(int position) {
6770             mObservable.notifyItemRangeChanged(position, 1);
6771         }
6772 
6773         /**
6774          * Notify any registered observers that the item at <code>position</code> has changed with
6775          * an optional payload object.
6776          *
6777          * <p>This is an item change event, not a structural change event. It indicates that any
6778          * reflection of the data at <code>position</code> is out of date and should be updated.
6779          * The item at <code>position</code> retains the same identity.
6780          * </p>
6781          *
6782          * <p>
6783          * Client can optionally pass a payload for partial change. These payloads will be merged
6784          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6785          * item is already represented by a ViewHolder and it will be rebound to the same
6786          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6787          * payloads on that item and prevent future payload until
6788          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6789          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6790          * attached, the payload will be simply dropped.
6791          *
6792          * @param position Position of the item that has changed
6793          * @param payload Optional parameter, use null to identify a "full" update
6794          *
6795          * @see #notifyItemRangeChanged(int, int)
6796          */
notifyItemChanged(int position, Object payload)6797         public final void notifyItemChanged(int position, Object payload) {
6798             mObservable.notifyItemRangeChanged(position, 1, payload);
6799         }
6800 
6801         /**
6802          * Notify any registered observers that the <code>itemCount</code> items starting at
6803          * position <code>positionStart</code> have changed.
6804          * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
6805          *
6806          * <p>This is an item change event, not a structural change event. It indicates that
6807          * any reflection of the data in the given position range is out of date and should
6808          * be updated. The items in the given range retain the same identity.</p>
6809          *
6810          * @param positionStart Position of the first item that has changed
6811          * @param itemCount Number of items that have changed
6812          *
6813          * @see #notifyItemChanged(int)
6814          */
notifyItemRangeChanged(int positionStart, int itemCount)6815         public final void notifyItemRangeChanged(int positionStart, int itemCount) {
6816             mObservable.notifyItemRangeChanged(positionStart, itemCount);
6817         }
6818 
6819         /**
6820          * Notify any registered observers that the <code>itemCount</code> items starting at
6821          * position <code>positionStart</code> have changed. An optional payload can be
6822          * passed to each changed item.
6823          *
6824          * <p>This is an item change event, not a structural change event. It indicates that any
6825          * reflection of the data in the given position range is out of date and should be updated.
6826          * The items in the given range retain the same identity.
6827          * </p>
6828          *
6829          * <p>
6830          * Client can optionally pass a payload for partial change. These payloads will be merged
6831          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6832          * item is already represented by a ViewHolder and it will be rebound to the same
6833          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6834          * payloads on that item and prevent future payload until
6835          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6836          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6837          * attached, the payload will be simply dropped.
6838          *
6839          * @param positionStart Position of the first item that has changed
6840          * @param itemCount Number of items that have changed
6841          * @param payload  Optional parameter, use null to identify a "full" update
6842          *
6843          * @see #notifyItemChanged(int)
6844          */
notifyItemRangeChanged(int positionStart, int itemCount, Object payload)6845         public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
6846             mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
6847         }
6848 
6849         /**
6850          * Notify any registered observers that the item reflected at <code>position</code>
6851          * has been newly inserted. The item previously at <code>position</code> is now at
6852          * position <code>position + 1</code>.
6853          *
6854          * <p>This is a structural change event. Representations of other existing items in the
6855          * data set are still considered up to date and will not be rebound, though their
6856          * positions may be altered.</p>
6857          *
6858          * @param position Position of the newly inserted item in the data set
6859          *
6860          * @see #notifyItemRangeInserted(int, int)
6861          */
notifyItemInserted(int position)6862         public final void notifyItemInserted(int position) {
6863             mObservable.notifyItemRangeInserted(position, 1);
6864         }
6865 
6866         /**
6867          * Notify any registered observers that the item reflected at <code>fromPosition</code>
6868          * has been moved to <code>toPosition</code>.
6869          *
6870          * <p>This is a structural change event. Representations of other existing items in the
6871          * data set are still considered up to date and will not be rebound, though their
6872          * positions may be altered.</p>
6873          *
6874          * @param fromPosition Previous position of the item.
6875          * @param toPosition New position of the item.
6876          */
notifyItemMoved(int fromPosition, int toPosition)6877         public final void notifyItemMoved(int fromPosition, int toPosition) {
6878             mObservable.notifyItemMoved(fromPosition, toPosition);
6879         }
6880 
6881         /**
6882          * Notify any registered observers that the currently reflected <code>itemCount</code>
6883          * items starting at <code>positionStart</code> have been newly inserted. The items
6884          * previously located at <code>positionStart</code> and beyond can now be found starting
6885          * at position <code>positionStart + itemCount</code>.
6886          *
6887          * <p>This is a structural change event. Representations of other existing items in the
6888          * data set are still considered up to date and will not be rebound, though their positions
6889          * may be altered.</p>
6890          *
6891          * @param positionStart Position of the first item that was inserted
6892          * @param itemCount Number of items inserted
6893          *
6894          * @see #notifyItemInserted(int)
6895          */
notifyItemRangeInserted(int positionStart, int itemCount)6896         public final void notifyItemRangeInserted(int positionStart, int itemCount) {
6897             mObservable.notifyItemRangeInserted(positionStart, itemCount);
6898         }
6899 
6900         /**
6901          * Notify any registered observers that the item previously located at <code>position</code>
6902          * has been removed from the data set. The items previously located at and after
6903          * <code>position</code> may now be found at <code>oldPosition - 1</code>.
6904          *
6905          * <p>This is a structural change event. Representations of other existing items in the
6906          * data set are still considered up to date and will not be rebound, though their positions
6907          * may be altered.</p>
6908          *
6909          * @param position Position of the item that has now been removed
6910          *
6911          * @see #notifyItemRangeRemoved(int, int)
6912          */
notifyItemRemoved(int position)6913         public final void notifyItemRemoved(int position) {
6914             mObservable.notifyItemRangeRemoved(position, 1);
6915         }
6916 
6917         /**
6918          * Notify any registered observers that the <code>itemCount</code> items previously
6919          * located at <code>positionStart</code> have been removed from the data set. The items
6920          * previously located at and after <code>positionStart + itemCount</code> may now be found
6921          * at <code>oldPosition - itemCount</code>.
6922          *
6923          * <p>This is a structural change event. Representations of other existing items in the data
6924          * set are still considered up to date and will not be rebound, though their positions
6925          * may be altered.</p>
6926          *
6927          * @param positionStart Previous position of the first item that was removed
6928          * @param itemCount Number of items removed from the data set
6929          */
notifyItemRangeRemoved(int positionStart, int itemCount)6930         public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
6931             mObservable.notifyItemRangeRemoved(positionStart, itemCount);
6932         }
6933     }
6934 
dispatchChildDetached(View child)6935     void dispatchChildDetached(View child) {
6936         final ViewHolder viewHolder = getChildViewHolderInt(child);
6937         onChildDetachedFromWindow(child);
6938         if (mAdapter != null && viewHolder != null) {
6939             mAdapter.onViewDetachedFromWindow(viewHolder);
6940         }
6941         if (mOnChildAttachStateListeners != null) {
6942             final int cnt = mOnChildAttachStateListeners.size();
6943             for (int i = cnt - 1; i >= 0; i--) {
6944                 mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
6945             }
6946         }
6947     }
6948 
dispatchChildAttached(View child)6949     void dispatchChildAttached(View child) {
6950         final ViewHolder viewHolder = getChildViewHolderInt(child);
6951         onChildAttachedToWindow(child);
6952         if (mAdapter != null && viewHolder != null) {
6953             mAdapter.onViewAttachedToWindow(viewHolder);
6954         }
6955         if (mOnChildAttachStateListeners != null) {
6956             final int cnt = mOnChildAttachStateListeners.size();
6957             for (int i = cnt - 1; i >= 0; i--) {
6958                 mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
6959             }
6960         }
6961     }
6962 
6963     /**
6964      * A <code>LayoutManager</code> is responsible for measuring and positioning item views
6965      * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
6966      * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
6967      * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
6968      * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
6969      * layout managers are provided for general use.
6970      * <p/>
6971      * If the LayoutManager specifies a default constructor or one with the signature
6972      * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
6973      * instantiate and set the LayoutManager when being inflated. Most used properties can
6974      * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
6975      * a LayoutManager specifies both constructors, the non-default constructor will take
6976      * precedence.
6977      *
6978      */
6979     public abstract static class LayoutManager {
6980         ChildHelper mChildHelper;
6981         RecyclerView mRecyclerView;
6982 
6983         /**
6984          * The callback used for retrieving information about a RecyclerView and its children in the
6985          * horizontal direction.
6986          */
6987         private final ViewBoundsCheck.Callback mHorizontalBoundCheckCallback =
6988                 new ViewBoundsCheck.Callback() {
6989                     @Override
6990                     public int getChildCount() {
6991                         return LayoutManager.this.getChildCount();
6992                     }
6993 
6994                     @Override
6995                     public View getParent() {
6996                         return mRecyclerView;
6997                     }
6998 
6999                     @Override
7000                     public View getChildAt(int index) {
7001                         return LayoutManager.this.getChildAt(index);
7002                     }
7003 
7004                     @Override
7005                     public int getParentStart() {
7006                         return LayoutManager.this.getPaddingLeft();
7007                     }
7008 
7009                     @Override
7010                     public int getParentEnd() {
7011                         return LayoutManager.this.getWidth() - LayoutManager.this.getPaddingRight();
7012                     }
7013 
7014                     @Override
7015                     public int getChildStart(View view) {
7016                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7017                                 view.getLayoutParams();
7018                         return LayoutManager.this.getDecoratedLeft(view) - params.leftMargin;
7019                     }
7020 
7021                     @Override
7022                     public int getChildEnd(View view) {
7023                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7024                                 view.getLayoutParams();
7025                         return LayoutManager.this.getDecoratedRight(view) + params.rightMargin;
7026                     }
7027                 };
7028 
7029         /**
7030          * The callback used for retrieving information about a RecyclerView and its children in the
7031          * vertical direction.
7032          */
7033         private final ViewBoundsCheck.Callback mVerticalBoundCheckCallback =
7034                 new ViewBoundsCheck.Callback() {
7035                     @Override
7036                     public int getChildCount() {
7037                         return LayoutManager.this.getChildCount();
7038                     }
7039 
7040                     @Override
7041                     public View getParent() {
7042                         return mRecyclerView;
7043                     }
7044 
7045                     @Override
7046                     public View getChildAt(int index) {
7047                         return LayoutManager.this.getChildAt(index);
7048                     }
7049 
7050                     @Override
7051                     public int getParentStart() {
7052                         return LayoutManager.this.getPaddingTop();
7053                     }
7054 
7055                     @Override
7056                     public int getParentEnd() {
7057                         return LayoutManager.this.getHeight()
7058                                 - LayoutManager.this.getPaddingBottom();
7059                     }
7060 
7061                     @Override
7062                     public int getChildStart(View view) {
7063                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7064                                 view.getLayoutParams();
7065                         return LayoutManager.this.getDecoratedTop(view) - params.topMargin;
7066                     }
7067 
7068                     @Override
7069                     public int getChildEnd(View view) {
7070                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7071                                 view.getLayoutParams();
7072                         return LayoutManager.this.getDecoratedBottom(view) + params.bottomMargin;
7073                     }
7074                 };
7075 
7076         /**
7077          * Utility objects used to check the boundaries of children against their parent
7078          * RecyclerView.
7079          * @see #isViewPartiallyVisible(View, boolean, boolean),
7080          * {@link LinearLayoutManager#findOneVisibleChild(int, int, boolean, boolean)},
7081          * and {@link LinearLayoutManager#findOnePartiallyOrCompletelyInvisibleChild(int, int)}.
7082          */
7083         ViewBoundsCheck mHorizontalBoundCheck = new ViewBoundsCheck(mHorizontalBoundCheckCallback);
7084         ViewBoundsCheck mVerticalBoundCheck = new ViewBoundsCheck(mVerticalBoundCheckCallback);
7085 
7086         @Nullable
7087         SmoothScroller mSmoothScroller;
7088 
7089         boolean mRequestedSimpleAnimations = false;
7090 
7091         boolean mIsAttachedToWindow = false;
7092 
7093         boolean mAutoMeasure = false;
7094 
7095         /**
7096          * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
7097          * if the space that will be given to it is already larger than what it has measured before.
7098          */
7099         private boolean mMeasurementCacheEnabled = true;
7100 
7101         private boolean mItemPrefetchEnabled = true;
7102 
7103         /**
7104          * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
7105          * requested by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)} or
7106          * {@link #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)} call.
7107          *
7108          * If expanded by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)},
7109          * will be reset upon layout to prevent initial prefetches (often large, since they're
7110          * proportional to expected child count) from expanding cache permanently.
7111          */
7112         int mPrefetchMaxCountObserved;
7113 
7114         /**
7115          * If true, mPrefetchMaxCountObserved is only valid until next layout, and should be reset.
7116          */
7117         boolean mPrefetchMaxObservedInInitialPrefetch;
7118 
7119         /**
7120          * These measure specs might be the measure specs that were passed into RecyclerView's
7121          * onMeasure method OR fake measure specs created by the RecyclerView.
7122          * For example, when a layout is run, RecyclerView always sets these specs to be
7123          * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
7124          * <p>
7125          * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
7126          * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
7127          * corrupt values. Older platforms have no responsibility to provide a size if they set
7128          * mode to unspecified.
7129          */
7130         private int mWidthMode, mHeightMode;
7131         private int mWidth, mHeight;
7132 
7133 
7134         /**
7135          * Interface for LayoutManagers to request items to be prefetched, based on position, with
7136          * specified distance from viewport, which indicates priority.
7137          *
7138          * @see LayoutManager#collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
7139          * @see LayoutManager#collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
7140          */
7141         public interface LayoutPrefetchRegistry {
7142             /**
7143              * Requests an an item to be prefetched, based on position, with a specified distance,
7144              * indicating priority.
7145              *
7146              * @param layoutPosition Position of the item to prefetch.
7147              * @param pixelDistance Distance from the current viewport to the bounds of the item,
7148              *                      must be non-negative.
7149              */
addPosition(int layoutPosition, int pixelDistance)7150             void addPosition(int layoutPosition, int pixelDistance);
7151         }
7152 
setRecyclerView(RecyclerView recyclerView)7153         void setRecyclerView(RecyclerView recyclerView) {
7154             if (recyclerView == null) {
7155                 mRecyclerView = null;
7156                 mChildHelper = null;
7157                 mWidth = 0;
7158                 mHeight = 0;
7159             } else {
7160                 mRecyclerView = recyclerView;
7161                 mChildHelper = recyclerView.mChildHelper;
7162                 mWidth = recyclerView.getWidth();
7163                 mHeight = recyclerView.getHeight();
7164             }
7165             mWidthMode = MeasureSpec.EXACTLY;
7166             mHeightMode = MeasureSpec.EXACTLY;
7167         }
7168 
setMeasureSpecs(int wSpec, int hSpec)7169         void setMeasureSpecs(int wSpec, int hSpec) {
7170             mWidth = MeasureSpec.getSize(wSpec);
7171             mWidthMode = MeasureSpec.getMode(wSpec);
7172             if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
7173                 mWidth = 0;
7174             }
7175 
7176             mHeight = MeasureSpec.getSize(hSpec);
7177             mHeightMode = MeasureSpec.getMode(hSpec);
7178             if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
7179                 mHeight = 0;
7180             }
7181         }
7182 
7183         /**
7184          * Called after a layout is calculated during a measure pass when using auto-measure.
7185          * <p>
7186          * It simply traverses all children to calculate a bounding box then calls
7187          * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
7188          * if they need to handle the bounding box differently.
7189          * <p>
7190          * For example, GridLayoutManager override that method to ensure that even if a column is
7191          * empty, the GridLayoutManager still measures wide enough to include it.
7192          *
7193          * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
7194          * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
7195          */
setMeasuredDimensionFromChildren(int widthSpec, int heightSpec)7196         void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
7197             final int count = getChildCount();
7198             if (count == 0) {
7199                 mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
7200                 return;
7201             }
7202             int minX = Integer.MAX_VALUE;
7203             int minY = Integer.MAX_VALUE;
7204             int maxX = Integer.MIN_VALUE;
7205             int maxY = Integer.MIN_VALUE;
7206 
7207             for (int i = 0; i < count; i++) {
7208                 View child = getChildAt(i);
7209                 final Rect bounds = mRecyclerView.mTempRect;
7210                 getDecoratedBoundsWithMargins(child, bounds);
7211                 if (bounds.left < minX) {
7212                     minX = bounds.left;
7213                 }
7214                 if (bounds.right > maxX) {
7215                     maxX = bounds.right;
7216                 }
7217                 if (bounds.top < minY) {
7218                     minY = bounds.top;
7219                 }
7220                 if (bounds.bottom > maxY) {
7221                     maxY = bounds.bottom;
7222                 }
7223             }
7224             mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
7225             setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
7226         }
7227 
7228         /**
7229          * Sets the measured dimensions from the given bounding box of the children and the
7230          * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
7231          * called after the RecyclerView calls
7232          * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a measurement pass.
7233          * <p>
7234          * This method should call {@link #setMeasuredDimension(int, int)}.
7235          * <p>
7236          * The default implementation adds the RecyclerView's padding to the given bounding box
7237          * then caps the value to be within the given measurement specs.
7238          * <p>
7239          * This method is only called if the LayoutManager opted into the auto measurement API.
7240          *
7241          * @param childrenBounds The bounding box of all children
7242          * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
7243          * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
7244          *
7245          * @see #setAutoMeasureEnabled(boolean)
7246          */
setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec)7247         public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
7248             int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
7249             int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
7250             int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
7251             int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
7252             setMeasuredDimension(width, height);
7253         }
7254 
7255         /**
7256          * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
7257          */
requestLayout()7258         public void requestLayout() {
7259             if (mRecyclerView != null) {
7260                 mRecyclerView.requestLayout();
7261             }
7262         }
7263 
7264         /**
7265          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7266          * {@link IllegalStateException} if it <b>is not</b>.
7267          *
7268          * @param message The message for the exception. Can be null.
7269          * @see #assertNotInLayoutOrScroll(String)
7270          */
assertInLayoutOrScroll(String message)7271         public void assertInLayoutOrScroll(String message) {
7272             if (mRecyclerView != null) {
7273                 mRecyclerView.assertInLayoutOrScroll(message);
7274             }
7275         }
7276 
7277         /**
7278          * Chooses a size from the given specs and parameters that is closest to the desired size
7279          * and also complies with the spec.
7280          *
7281          * @param spec The measureSpec
7282          * @param desired The preferred measurement
7283          * @param min The minimum value
7284          *
7285          * @return A size that fits to the given specs
7286          */
chooseSize(int spec, int desired, int min)7287         public static int chooseSize(int spec, int desired, int min) {
7288             final int mode = View.MeasureSpec.getMode(spec);
7289             final int size = View.MeasureSpec.getSize(spec);
7290             switch (mode) {
7291                 case View.MeasureSpec.EXACTLY:
7292                     return size;
7293                 case View.MeasureSpec.AT_MOST:
7294                     return Math.min(size, Math.max(desired, min));
7295                 case View.MeasureSpec.UNSPECIFIED:
7296                 default:
7297                     return Math.max(desired, min);
7298             }
7299         }
7300 
7301         /**
7302          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7303          * {@link IllegalStateException} if it <b>is</b>.
7304          *
7305          * @param message The message for the exception. Can be null.
7306          * @see #assertInLayoutOrScroll(String)
7307          */
assertNotInLayoutOrScroll(String message)7308         public void assertNotInLayoutOrScroll(String message) {
7309             if (mRecyclerView != null) {
7310                 mRecyclerView.assertNotInLayoutOrScroll(message);
7311             }
7312         }
7313 
7314         /**
7315          * Defines whether the layout should be measured by the RecyclerView or the LayoutManager
7316          * wants to handle the layout measurements itself.
7317          * <p>
7318          * This method is usually called by the LayoutManager with value {@code true} if it wants
7319          * to support WRAP_CONTENT. If you are using a public LayoutManager but want to customize
7320          * the measurement logic, you can call this method with {@code false} and override
7321          * {@link LayoutManager#onMeasure(int, int)} to implement your custom measurement logic.
7322          * <p>
7323          * AutoMeasure is a convenience mechanism for LayoutManagers to easily wrap their content or
7324          * handle various specs provided by the RecyclerView's parent.
7325          * It works by calling {@link LayoutManager#onLayoutChildren(Recycler, State)} during an
7326          * {@link RecyclerView#onMeasure(int, int)} call, then calculating desired dimensions based
7327          * on children's positions. It does this while supporting all existing animation
7328          * capabilities of the RecyclerView.
7329          * <p>
7330          * AutoMeasure works as follows:
7331          * <ol>
7332          * <li>LayoutManager should call {@code setAutoMeasureEnabled(true)} to enable it. All of
7333          * the framework LayoutManagers use {@code auto-measure}.</li>
7334          * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided specs are
7335          * exact, RecyclerView will only call LayoutManager's {@code onMeasure} and return without
7336          * doing any layout calculation.</li>
7337          * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
7338          * layout process in {@code onMeasure} call. It will process all pending Adapter updates and
7339          * decide whether to run a predictive layout or not. If it decides to do so, it will first
7340          * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
7341          * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
7342          * return the width and height of the RecyclerView as of the last layout calculation.
7343          * <p>
7344          * After handling the predictive case, RecyclerView will call
7345          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7346          * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
7347          * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
7348          * {@link #getWidth()} and {@link #getWidthMode()}.</li>
7349          * <li>After the layout calculation, RecyclerView sets the measured width & height by
7350          * calculating the bounding box for the children (+ RecyclerView's padding). The
7351          * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
7352          * different values. For instance, GridLayoutManager overrides this value to handle the case
7353          * where if it is vertical and has 3 columns but only 2 items, it should still measure its
7354          * width to fit 3 items, not 2.</li>
7355          * <li>Any following on measure call to the RecyclerView will run
7356          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7357          * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
7358          * take care of which views are actually added / removed / moved / changed for animations so
7359          * that the LayoutManager should not worry about them and handle each
7360          * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.
7361          * </li>
7362          * <li>When measure is complete and RecyclerView's
7363          * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
7364          * whether it already did layout calculations during the measure pass and if so, it re-uses
7365          * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
7366          * if the last measure spec was different from the final dimensions or adapter contents
7367          * have changed between the measure call and the layout call.</li>
7368          * <li>Finally, animations are calculated and run as usual.</li>
7369          * </ol>
7370          *
7371          * @param enabled <code>True</code> if the Layout should be measured by the
7372          *                             RecyclerView, <code>false</code> if the LayoutManager wants
7373          *                             to measure itself.
7374          *
7375          * @see #setMeasuredDimension(Rect, int, int)
7376          * @see #isAutoMeasureEnabled()
7377          */
setAutoMeasureEnabled(boolean enabled)7378         public void setAutoMeasureEnabled(boolean enabled) {
7379             mAutoMeasure = enabled;
7380         }
7381 
7382         /**
7383          * Returns whether the LayoutManager uses the automatic measurement API or not.
7384          *
7385          * @return <code>True</code> if the LayoutManager is measured by the RecyclerView or
7386          * <code>false</code> if it measures itself.
7387          *
7388          * @see #setAutoMeasureEnabled(boolean)
7389          */
isAutoMeasureEnabled()7390         public boolean isAutoMeasureEnabled() {
7391             return mAutoMeasure;
7392         }
7393 
7394         /**
7395          * Returns whether this LayoutManager supports automatic item animations.
7396          * A LayoutManager wishing to support item animations should obey certain
7397          * rules as outlined in {@link #onLayoutChildren(Recycler, State)}.
7398          * The default return value is <code>false</code>, so subclasses of LayoutManager
7399          * will not get predictive item animations by default.
7400          *
7401          * <p>Whether item animations are enabled in a RecyclerView is determined both
7402          * by the return value from this method and the
7403          * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
7404          * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
7405          * method returns false, then simple item animations will be enabled, in which
7406          * views that are moving onto or off of the screen are simply faded in/out. If
7407          * the RecyclerView has a non-null ItemAnimator and this method returns true,
7408          * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to
7409          * setup up the information needed to more intelligently predict where appearing
7410          * and disappearing views should be animated from/to.</p>
7411          *
7412          * @return true if predictive item animations should be enabled, false otherwise
7413          */
supportsPredictiveItemAnimations()7414         public boolean supportsPredictiveItemAnimations() {
7415             return false;
7416         }
7417 
7418         /**
7419          * Sets whether the LayoutManager should be queried for views outside of
7420          * its viewport while the UI thread is idle between frames.
7421          *
7422          * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
7423          * view system traversals on devices running API 21 or greater. Default value is true.</p>
7424          *
7425          * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
7426          * to RenderThread and the starting up its next frame at the next VSync pulse. By
7427          * prefetching out of window views in this time period, delays from inflation and view
7428          * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
7429          *
7430          * <p>While prefetch is enabled, it will have the side effect of expanding the effective
7431          * size of the View cache to hold prefetched views.</p>
7432          *
7433          * @param enabled <code>True</code> if items should be prefetched in between traversals.
7434          *
7435          * @see #isItemPrefetchEnabled()
7436          */
setItemPrefetchEnabled(boolean enabled)7437         public final void setItemPrefetchEnabled(boolean enabled) {
7438             if (enabled != mItemPrefetchEnabled) {
7439                 mItemPrefetchEnabled = enabled;
7440                 mPrefetchMaxCountObserved = 0;
7441                 if (mRecyclerView != null) {
7442                     mRecyclerView.mRecycler.updateViewCacheSize();
7443                 }
7444             }
7445         }
7446 
7447         /**
7448          * Sets whether the LayoutManager should be queried for views outside of
7449          * its viewport while the UI thread is idle between frames.
7450          *
7451          * @see #setItemPrefetchEnabled(boolean)
7452          *
7453          * @return true if item prefetch is enabled, false otherwise
7454          */
isItemPrefetchEnabled()7455         public final boolean isItemPrefetchEnabled() {
7456             return mItemPrefetchEnabled;
7457         }
7458 
7459         /**
7460          * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
7461          *
7462          * <p>If item prefetch is enabled, this method is called in between traversals to gather
7463          * which positions the LayoutManager will soon need, given upcoming movement in subsequent
7464          * traversals.</p>
7465          *
7466          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7467          * each item to be prepared, and these positions will have their ViewHolders created and
7468          * bound, if there is sufficient time available, in advance of being needed by a
7469          * scroll or layout.</p>
7470          *
7471          * @param dx X movement component.
7472          * @param dy Y movement component.
7473          * @param state State of RecyclerView
7474          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7475          *
7476          * @see #isItemPrefetchEnabled()
7477          * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
7478          */
collectAdjacentPrefetchPositions(int dx, int dy, State state, LayoutPrefetchRegistry layoutPrefetchRegistry)7479         public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
7480                 LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7481 
7482         /**
7483          * Gather all positions from the LayoutManager to be prefetched in preperation for its
7484          * RecyclerView to come on screen, due to the movement of another, containing RecyclerView.
7485          *
7486          * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
7487          *
7488          * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
7489          * LayoutManager, this method is called in between draw traversals to gather
7490          * which positions this LayoutManager will first need, once it appears on the screen.</p>
7491          *
7492          * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
7493          * vertically scrolling LayoutManager, this method would be called when the horizontal list
7494          * is about to come onscreen.</p>
7495          *
7496          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7497          * each item to be prepared, and these positions will have their ViewHolders created and
7498          * bound, if there is sufficient time available, in advance of being needed by a
7499          * scroll or layout.</p>
7500          *
7501          * @param adapterItemCount number of items in the associated adapter.
7502          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7503          *
7504          * @see #isItemPrefetchEnabled()
7505          * @see #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
7506          */
collectInitialPrefetchPositions(int adapterItemCount, LayoutPrefetchRegistry layoutPrefetchRegistry)7507         public void collectInitialPrefetchPositions(int adapterItemCount,
7508                 LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7509 
dispatchAttachedToWindow(RecyclerView view)7510         void dispatchAttachedToWindow(RecyclerView view) {
7511             mIsAttachedToWindow = true;
7512             onAttachedToWindow(view);
7513         }
7514 
dispatchDetachedFromWindow(RecyclerView view, Recycler recycler)7515         void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
7516             mIsAttachedToWindow = false;
7517             onDetachedFromWindow(view, recycler);
7518         }
7519 
7520         /**
7521          * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
7522          * to a window.
7523          *
7524          * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
7525          * is attached to window.
7526          */
isAttachedToWindow()7527         public boolean isAttachedToWindow() {
7528             return mIsAttachedToWindow;
7529         }
7530 
7531         /**
7532          * Causes the Runnable to execute on the next animation time step.
7533          * The runnable will be run on the user interface thread.
7534          * <p>
7535          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7536          *
7537          * @param action The Runnable that will be executed.
7538          *
7539          * @see #removeCallbacks
7540          */
postOnAnimation(Runnable action)7541         public void postOnAnimation(Runnable action) {
7542             if (mRecyclerView != null) {
7543                 ViewCompat.postOnAnimation(mRecyclerView, action);
7544             }
7545         }
7546 
7547         /**
7548          * Removes the specified Runnable from the message queue.
7549          * <p>
7550          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7551          *
7552          * @param action The Runnable to remove from the message handling queue
7553          *
7554          * @return true if RecyclerView could ask the Handler to remove the Runnable,
7555          *         false otherwise. When the returned value is true, the Runnable
7556          *         may or may not have been actually removed from the message queue
7557          *         (for instance, if the Runnable was not in the queue already.)
7558          *
7559          * @see #postOnAnimation
7560          */
removeCallbacks(Runnable action)7561         public boolean removeCallbacks(Runnable action) {
7562             if (mRecyclerView != null) {
7563                 return mRecyclerView.removeCallbacks(action);
7564             }
7565             return false;
7566         }
7567         /**
7568          * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
7569          * is attached to a window.
7570          * <p>
7571          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7572          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7573          * not requested on the RecyclerView while it was detached.
7574          * <p>
7575          * Subclass implementations should always call through to the superclass implementation.
7576          *
7577          * @param view The RecyclerView this LayoutManager is bound to
7578          *
7579          * @see #onDetachedFromWindow(RecyclerView, Recycler)
7580          */
7581         @CallSuper
onAttachedToWindow(RecyclerView view)7582         public void onAttachedToWindow(RecyclerView view) {
7583         }
7584 
7585         /**
7586          * @deprecated
7587          * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
7588          */
7589         @Deprecated
onDetachedFromWindow(RecyclerView view)7590         public void onDetachedFromWindow(RecyclerView view) {
7591 
7592         }
7593 
7594         /**
7595          * Called when this LayoutManager is detached from its parent RecyclerView or when
7596          * its parent RecyclerView is detached from its window.
7597          * <p>
7598          * LayoutManager should clear all of its View references as another LayoutManager might be
7599          * assigned to the RecyclerView.
7600          * <p>
7601          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7602          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7603          * not requested on the RecyclerView while it was detached.
7604          * <p>
7605          * If your LayoutManager has View references that it cleans in on-detach, it should also
7606          * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
7607          * RecyclerView is re-attached.
7608          * <p>
7609          * Subclass implementations should always call through to the superclass implementation.
7610          *
7611          * @param view The RecyclerView this LayoutManager is bound to
7612          * @param recycler The recycler to use if you prefer to recycle your children instead of
7613          *                 keeping them around.
7614          *
7615          * @see #onAttachedToWindow(RecyclerView)
7616          */
7617         @CallSuper
onDetachedFromWindow(RecyclerView view, Recycler recycler)7618         public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
7619             onDetachedFromWindow(view);
7620         }
7621 
7622         /**
7623          * Check if the RecyclerView is configured to clip child views to its padding.
7624          *
7625          * @return true if this RecyclerView clips children to its padding, false otherwise
7626          */
getClipToPadding()7627         public boolean getClipToPadding() {
7628             return mRecyclerView != null && mRecyclerView.mClipToPadding;
7629         }
7630 
7631         /**
7632          * Lay out all relevant child views from the given adapter.
7633          *
7634          * The LayoutManager is in charge of the behavior of item animations. By default,
7635          * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
7636          * item animations are enabled. This means that add/remove operations on the
7637          * adapter will result in animations to add new or appearing items, removed or
7638          * disappearing items, and moved items. If a LayoutManager returns false from
7639          * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
7640          * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
7641          * RecyclerView will have enough information to run those animations in a simple
7642          * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
7643          * simply fade views in and out, whether they are actually added/removed or whether
7644          * they are moved on or off the screen due to other add/remove operations.
7645          *
7646          * <p>A LayoutManager wanting a better item animation experience, where items can be
7647          * animated onto and off of the screen according to where the items exist when they
7648          * are not on screen, then the LayoutManager should return true from
7649          * {@link #supportsPredictiveItemAnimations()} and add additional logic to
7650          * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
7651          * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
7652          * once as a "pre" layout step to determine where items would have been prior to
7653          * a real layout, and again to do the "real" layout. In the pre-layout phase,
7654          * items will remember their pre-layout positions to allow them to be laid out
7655          * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
7656          * be returned from the scrap to help determine correct placement of other items.
7657          * These removed items should not be added to the child list, but should be used
7658          * to help calculate correct positioning of other views, including views that
7659          * were not previously onscreen (referred to as APPEARING views), but whose
7660          * pre-layout offscreen position can be determined given the extra
7661          * information about the pre-layout removed views.</p>
7662          *
7663          * <p>The second layout pass is the real layout in which only non-removed views
7664          * will be used. The only additional requirement during this pass is, if
7665          * {@link #supportsPredictiveItemAnimations()} returns true, to note which
7666          * views exist in the child list prior to layout and which are not there after
7667          * layout (referred to as DISAPPEARING views), and to position/layout those views
7668          * appropriately, without regard to the actual bounds of the RecyclerView. This allows
7669          * the animation system to know the location to which to animate these disappearing
7670          * views.</p>
7671          *
7672          * <p>The default LayoutManager implementations for RecyclerView handle all of these
7673          * requirements for animations already. Clients of RecyclerView can either use one
7674          * of these layout managers directly or look at their implementations of
7675          * onLayoutChildren() to see how they account for the APPEARING and
7676          * DISAPPEARING views.</p>
7677          *
7678          * @param recycler         Recycler to use for fetching potentially cached views for a
7679          *                         position
7680          * @param state            Transient state of RecyclerView
7681          */
onLayoutChildren(Recycler recycler, State state)7682         public void onLayoutChildren(Recycler recycler, State state) {
7683             Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
7684         }
7685 
7686         /**
7687          * Called after a full layout calculation is finished. The layout calculation may include
7688          * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
7689          * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
7690          * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
7691          * <p>
7692          * This is a good place for the LayoutManager to do some cleanup like pending scroll
7693          * position, saved state etc.
7694          *
7695          * @param state Transient state of RecyclerView
7696          */
onLayoutCompleted(State state)7697         public void onLayoutCompleted(State state) {
7698         }
7699 
7700         /**
7701          * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
7702          *
7703          * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
7704          * to store extra information specific to the layout. Client code should subclass
7705          * {@link RecyclerView.LayoutParams} for this purpose.</p>
7706          *
7707          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7708          * you must also override
7709          * {@link #checkLayoutParams(LayoutParams)},
7710          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7711          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7712          *
7713          * @return A new LayoutParams for a child view
7714          */
generateDefaultLayoutParams()7715         public abstract LayoutParams generateDefaultLayoutParams();
7716 
7717         /**
7718          * Determines the validity of the supplied LayoutParams object.
7719          *
7720          * <p>This should check to make sure that the object is of the correct type
7721          * and all values are within acceptable ranges. The default implementation
7722          * returns <code>true</code> for non-null params.</p>
7723          *
7724          * @param lp LayoutParams object to check
7725          * @return true if this LayoutParams object is valid, false otherwise
7726          */
checkLayoutParams(LayoutParams lp)7727         public boolean checkLayoutParams(LayoutParams lp) {
7728             return lp != null;
7729         }
7730 
7731         /**
7732          * Create a LayoutParams object suitable for this LayoutManager, copying relevant
7733          * values from the supplied LayoutParams object if possible.
7734          *
7735          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7736          * you must also override
7737          * {@link #checkLayoutParams(LayoutParams)},
7738          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7739          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7740          *
7741          * @param lp Source LayoutParams object to copy values from
7742          * @return a new LayoutParams object
7743          */
generateLayoutParams(ViewGroup.LayoutParams lp)7744         public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
7745             if (lp instanceof LayoutParams) {
7746                 return new LayoutParams((LayoutParams) lp);
7747             } else if (lp instanceof MarginLayoutParams) {
7748                 return new LayoutParams((MarginLayoutParams) lp);
7749             } else {
7750                 return new LayoutParams(lp);
7751             }
7752         }
7753 
7754         /**
7755          * Create a LayoutParams object suitable for this LayoutManager from
7756          * an inflated layout resource.
7757          *
7758          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7759          * you must also override
7760          * {@link #checkLayoutParams(LayoutParams)},
7761          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7762          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7763          *
7764          * @param c Context for obtaining styled attributes
7765          * @param attrs AttributeSet describing the supplied arguments
7766          * @return a new LayoutParams object
7767          */
generateLayoutParams(Context c, AttributeSet attrs)7768         public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
7769             return new LayoutParams(c, attrs);
7770         }
7771 
7772         /**
7773          * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
7774          * The default implementation does nothing and returns 0.
7775          *
7776          * @param dx            distance to scroll by in pixels. X increases as scroll position
7777          *                      approaches the right.
7778          * @param recycler      Recycler to use for fetching potentially cached views for a
7779          *                      position
7780          * @param state         Transient state of RecyclerView
7781          * @return The actual distance scrolled. The return value will be negative if dx was
7782          * negative and scrolling proceeeded in that direction.
7783          * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
7784          */
scrollHorizontallyBy(int dx, Recycler recycler, State state)7785         public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
7786             return 0;
7787         }
7788 
7789         /**
7790          * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
7791          * The default implementation does nothing and returns 0.
7792          *
7793          * @param dy            distance to scroll in pixels. Y increases as scroll position
7794          *                      approaches the bottom.
7795          * @param recycler      Recycler to use for fetching potentially cached views for a
7796          *                      position
7797          * @param state         Transient state of RecyclerView
7798          * @return The actual distance scrolled. The return value will be negative if dy was
7799          * negative and scrolling proceeeded in that direction.
7800          * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
7801          */
scrollVerticallyBy(int dy, Recycler recycler, State state)7802         public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
7803             return 0;
7804         }
7805 
7806         /**
7807          * Query if horizontal scrolling is currently supported. The default implementation
7808          * returns false.
7809          *
7810          * @return True if this LayoutManager can scroll the current contents horizontally
7811          */
canScrollHorizontally()7812         public boolean canScrollHorizontally() {
7813             return false;
7814         }
7815 
7816         /**
7817          * Query if vertical scrolling is currently supported. The default implementation
7818          * returns false.
7819          *
7820          * @return True if this LayoutManager can scroll the current contents vertically
7821          */
canScrollVertically()7822         public boolean canScrollVertically() {
7823             return false;
7824         }
7825 
7826         /**
7827          * Scroll to the specified adapter position.
7828          *
7829          * Actual position of the item on the screen depends on the LayoutManager implementation.
7830          * @param position Scroll to this adapter position.
7831          */
scrollToPosition(int position)7832         public void scrollToPosition(int position) {
7833             if (DEBUG) {
7834                 Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
7835             }
7836         }
7837 
7838         /**
7839          * <p>Smooth scroll to the specified adapter position.</p>
7840          * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
7841          * instance and call {@link #startSmoothScroll(SmoothScroller)}.
7842          * </p>
7843          * @param recyclerView The RecyclerView to which this layout manager is attached
7844          * @param state    Current State of RecyclerView
7845          * @param position Scroll to this adapter position.
7846          */
smoothScrollToPosition(RecyclerView recyclerView, State state, int position)7847         public void smoothScrollToPosition(RecyclerView recyclerView, State state,
7848                 int position) {
7849             Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
7850         }
7851 
7852         /**
7853          * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
7854          * <p>Calling this method will cancel any previous smooth scroll request.</p>
7855          * @param smoothScroller Instance which defines how smooth scroll should be animated
7856          */
startSmoothScroll(SmoothScroller smoothScroller)7857         public void startSmoothScroll(SmoothScroller smoothScroller) {
7858             if (mSmoothScroller != null && smoothScroller != mSmoothScroller
7859                     && mSmoothScroller.isRunning()) {
7860                 mSmoothScroller.stop();
7861             }
7862             mSmoothScroller = smoothScroller;
7863             mSmoothScroller.start(mRecyclerView, this);
7864         }
7865 
7866         /**
7867          * @return true if RecycylerView is currently in the state of smooth scrolling.
7868          */
isSmoothScrolling()7869         public boolean isSmoothScrolling() {
7870             return mSmoothScroller != null && mSmoothScroller.isRunning();
7871         }
7872 
7873 
7874         /**
7875          * Returns the resolved layout direction for this RecyclerView.
7876          *
7877          * @return {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout
7878          * direction is RTL or returns
7879          * {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction
7880          * is not RTL.
7881          */
getLayoutDirection()7882         public int getLayoutDirection() {
7883             return ViewCompat.getLayoutDirection(mRecyclerView);
7884         }
7885 
7886         /**
7887          * Ends all animations on the view created by the {@link ItemAnimator}.
7888          *
7889          * @param view The View for which the animations should be ended.
7890          * @see RecyclerView.ItemAnimator#endAnimations()
7891          */
endAnimation(View view)7892         public void endAnimation(View view) {
7893             if (mRecyclerView.mItemAnimator != null) {
7894                 mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
7895             }
7896         }
7897 
7898         /**
7899          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
7900          * to the layout that is known to be going away, either because it has been
7901          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
7902          * visible portion of the container but is being laid out in order to inform RecyclerView
7903          * in how to animate the item out of view.
7904          * <p>
7905          * Views added via this method are going to be invisible to LayoutManager after the
7906          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
7907          * or won't be included in {@link #getChildCount()} method.
7908          *
7909          * @param child View to add and then remove with animation.
7910          */
addDisappearingView(View child)7911         public void addDisappearingView(View child) {
7912             addDisappearingView(child, -1);
7913         }
7914 
7915         /**
7916          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
7917          * to the layout that is known to be going away, either because it has been
7918          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
7919          * visible portion of the container but is being laid out in order to inform RecyclerView
7920          * in how to animate the item out of view.
7921          * <p>
7922          * Views added via this method are going to be invisible to LayoutManager after the
7923          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
7924          * or won't be included in {@link #getChildCount()} method.
7925          *
7926          * @param child View to add and then remove with animation.
7927          * @param index Index of the view.
7928          */
addDisappearingView(View child, int index)7929         public void addDisappearingView(View child, int index) {
7930             addViewInt(child, index, true);
7931         }
7932 
7933         /**
7934          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
7935          * use this method to add views obtained from a {@link Recycler} using
7936          * {@link Recycler#getViewForPosition(int)}.
7937          *
7938          * @param child View to add
7939          */
addView(View child)7940         public void addView(View child) {
7941             addView(child, -1);
7942         }
7943 
7944         /**
7945          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
7946          * use this method to add views obtained from a {@link Recycler} using
7947          * {@link Recycler#getViewForPosition(int)}.
7948          *
7949          * @param child View to add
7950          * @param index Index to add child at
7951          */
addView(View child, int index)7952         public void addView(View child, int index) {
7953             addViewInt(child, index, false);
7954         }
7955 
addViewInt(View child, int index, boolean disappearing)7956         private void addViewInt(View child, int index, boolean disappearing) {
7957             final ViewHolder holder = getChildViewHolderInt(child);
7958             if (disappearing || holder.isRemoved()) {
7959                 // these views will be hidden at the end of the layout pass.
7960                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
7961             } else {
7962                 // This may look like unnecessary but may happen if layout manager supports
7963                 // predictive layouts and adapter removed then re-added the same item.
7964                 // In this case, added version will be visible in the post layout (because add is
7965                 // deferred) but RV will still bind it to the same View.
7966                 // So if a View re-appears in post layout pass, remove it from disappearing list.
7967                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
7968             }
7969             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
7970             if (holder.wasReturnedFromScrap() || holder.isScrap()) {
7971                 if (holder.isScrap()) {
7972                     holder.unScrap();
7973                 } else {
7974                     holder.clearReturnedFromScrapFlag();
7975                 }
7976                 mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
7977                 if (DISPATCH_TEMP_DETACH) {
7978                     ViewCompat.dispatchFinishTemporaryDetach(child);
7979                 }
7980             } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
7981                 // ensure in correct position
7982                 int currentIndex = mChildHelper.indexOfChild(child);
7983                 if (index == -1) {
7984                     index = mChildHelper.getChildCount();
7985                 }
7986                 if (currentIndex == -1) {
7987                     throw new IllegalStateException("Added View has RecyclerView as parent but"
7988                             + " view is not a real child. Unfiltered index:"
7989                             + mRecyclerView.indexOfChild(child) + mRecyclerView.exceptionLabel());
7990                 }
7991                 if (currentIndex != index) {
7992                     mRecyclerView.mLayout.moveView(currentIndex, index);
7993                 }
7994             } else {
7995                 mChildHelper.addView(child, index, false);
7996                 lp.mInsetsDirty = true;
7997                 if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
7998                     mSmoothScroller.onChildAttachedToWindow(child);
7999                 }
8000             }
8001             if (lp.mPendingInvalidate) {
8002                 if (DEBUG) {
8003                     Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
8004                 }
8005                 holder.itemView.invalidate();
8006                 lp.mPendingInvalidate = false;
8007             }
8008         }
8009 
8010         /**
8011          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
8012          * use this method to completely remove a child view that is no longer needed.
8013          * LayoutManagers should strongly consider recycling removed views using
8014          * {@link Recycler#recycleView(android.view.View)}.
8015          *
8016          * @param child View to remove
8017          */
removeView(View child)8018         public void removeView(View child) {
8019             mChildHelper.removeView(child);
8020         }
8021 
8022         /**
8023          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
8024          * use this method to completely remove a child view that is no longer needed.
8025          * LayoutManagers should strongly consider recycling removed views using
8026          * {@link Recycler#recycleView(android.view.View)}.
8027          *
8028          * @param index Index of the child view to remove
8029          */
removeViewAt(int index)8030         public void removeViewAt(int index) {
8031             final View child = getChildAt(index);
8032             if (child != null) {
8033                 mChildHelper.removeViewAt(index);
8034             }
8035         }
8036 
8037         /**
8038          * Remove all views from the currently attached RecyclerView. This will not recycle
8039          * any of the affected views; the LayoutManager is responsible for doing so if desired.
8040          */
removeAllViews()8041         public void removeAllViews() {
8042             // Only remove non-animating views
8043             final int childCount = getChildCount();
8044             for (int i = childCount - 1; i >= 0; i--) {
8045                 mChildHelper.removeViewAt(i);
8046             }
8047         }
8048 
8049         /**
8050          * Returns offset of the RecyclerView's text baseline from the its top boundary.
8051          *
8052          * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
8053          * there is no baseline.
8054          */
getBaseline()8055         public int getBaseline() {
8056             return -1;
8057         }
8058 
8059         /**
8060          * Returns the adapter position of the item represented by the given View. This does not
8061          * contain any adapter changes that might have happened after the last layout.
8062          *
8063          * @param view The view to query
8064          * @return The adapter position of the item which is rendered by this View.
8065          */
getPosition(View view)8066         public int getPosition(View view) {
8067             return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
8068         }
8069 
8070         /**
8071          * Returns the View type defined by the adapter.
8072          *
8073          * @param view The view to query
8074          * @return The type of the view assigned by the adapter.
8075          */
getItemViewType(View view)8076         public int getItemViewType(View view) {
8077             return getChildViewHolderInt(view).getItemViewType();
8078         }
8079 
8080         /**
8081          * Traverses the ancestors of the given view and returns the item view that contains it
8082          * and also a direct child of the LayoutManager.
8083          * <p>
8084          * Note that this method may return null if the view is a child of the RecyclerView but
8085          * not a child of the LayoutManager (e.g. running a disappear animation).
8086          *
8087          * @param view The view that is a descendant of the LayoutManager.
8088          *
8089          * @return The direct child of the LayoutManager which contains the given view or null if
8090          * the provided view is not a descendant of this LayoutManager.
8091          *
8092          * @see RecyclerView#getChildViewHolder(View)
8093          * @see RecyclerView#findContainingViewHolder(View)
8094          */
8095         @Nullable
findContainingItemView(View view)8096         public View findContainingItemView(View view) {
8097             if (mRecyclerView == null) {
8098                 return null;
8099             }
8100             View found = mRecyclerView.findContainingItemView(view);
8101             if (found == null) {
8102                 return null;
8103             }
8104             if (mChildHelper.isHidden(found)) {
8105                 return null;
8106             }
8107             return found;
8108         }
8109 
8110         /**
8111          * Finds the view which represents the given adapter position.
8112          * <p>
8113          * This method traverses each child since it has no information about child order.
8114          * Override this method to improve performance if your LayoutManager keeps data about
8115          * child views.
8116          * <p>
8117          * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
8118          *
8119          * @param position Position of the item in adapter
8120          * @return The child view that represents the given position or null if the position is not
8121          * laid out
8122          */
findViewByPosition(int position)8123         public View findViewByPosition(int position) {
8124             final int childCount = getChildCount();
8125             for (int i = 0; i < childCount; i++) {
8126                 View child = getChildAt(i);
8127                 ViewHolder vh = getChildViewHolderInt(child);
8128                 if (vh == null) {
8129                     continue;
8130                 }
8131                 if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
8132                         && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
8133                     return child;
8134                 }
8135             }
8136             return null;
8137         }
8138 
8139         /**
8140          * Temporarily detach a child view.
8141          *
8142          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
8143          * views currently attached to the RecyclerView. Generally LayoutManager implementations
8144          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
8145          * so that the detached view may be rebound and reused.</p>
8146          *
8147          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
8148          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
8149          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
8150          * before the LayoutManager entry point method called by RecyclerView returns.</p>
8151          *
8152          * @param child Child to detach
8153          */
detachView(View child)8154         public void detachView(View child) {
8155             final int ind = mChildHelper.indexOfChild(child);
8156             if (ind >= 0) {
8157                 detachViewInternal(ind, child);
8158             }
8159         }
8160 
8161         /**
8162          * Temporarily detach a child view.
8163          *
8164          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
8165          * views currently attached to the RecyclerView. Generally LayoutManager implementations
8166          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
8167          * so that the detached view may be rebound and reused.</p>
8168          *
8169          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
8170          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
8171          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
8172          * before the LayoutManager entry point method called by RecyclerView returns.</p>
8173          *
8174          * @param index Index of the child to detach
8175          */
detachViewAt(int index)8176         public void detachViewAt(int index) {
8177             detachViewInternal(index, getChildAt(index));
8178         }
8179 
detachViewInternal(int index, View view)8180         private void detachViewInternal(int index, View view) {
8181             if (DISPATCH_TEMP_DETACH) {
8182                 ViewCompat.dispatchStartTemporaryDetach(view);
8183             }
8184             mChildHelper.detachViewFromParent(index);
8185         }
8186 
8187         /**
8188          * Reattach a previously {@link #detachView(android.view.View) detached} view.
8189          * This method should not be used to reattach views that were previously
8190          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8191          *
8192          * @param child Child to reattach
8193          * @param index Intended child index for child
8194          * @param lp LayoutParams for child
8195          */
attachView(View child, int index, LayoutParams lp)8196         public void attachView(View child, int index, LayoutParams lp) {
8197             ViewHolder vh = getChildViewHolderInt(child);
8198             if (vh.isRemoved()) {
8199                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
8200             } else {
8201                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
8202             }
8203             mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
8204             if (DISPATCH_TEMP_DETACH)  {
8205                 ViewCompat.dispatchFinishTemporaryDetach(child);
8206             }
8207         }
8208 
8209         /**
8210          * Reattach a previously {@link #detachView(android.view.View) detached} view.
8211          * This method should not be used to reattach views that were previously
8212          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8213          *
8214          * @param child Child to reattach
8215          * @param index Intended child index for child
8216          */
attachView(View child, int index)8217         public void attachView(View child, int index) {
8218             attachView(child, index, (LayoutParams) child.getLayoutParams());
8219         }
8220 
8221         /**
8222          * Reattach a previously {@link #detachView(android.view.View) detached} view.
8223          * This method should not be used to reattach views that were previously
8224          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8225          *
8226          * @param child Child to reattach
8227          */
attachView(View child)8228         public void attachView(View child) {
8229             attachView(child, -1);
8230         }
8231 
8232         /**
8233          * Finish removing a view that was previously temporarily
8234          * {@link #detachView(android.view.View) detached}.
8235          *
8236          * @param child Detached child to remove
8237          */
removeDetachedView(View child)8238         public void removeDetachedView(View child) {
8239             mRecyclerView.removeDetachedView(child, false);
8240         }
8241 
8242         /**
8243          * Moves a View from one position to another.
8244          *
8245          * @param fromIndex The View's initial index
8246          * @param toIndex The View's target index
8247          */
moveView(int fromIndex, int toIndex)8248         public void moveView(int fromIndex, int toIndex) {
8249             View view = getChildAt(fromIndex);
8250             if (view == null) {
8251                 throw new IllegalArgumentException("Cannot move a child from non-existing index:"
8252                         + fromIndex + mRecyclerView.toString());
8253             }
8254             detachViewAt(fromIndex);
8255             attachView(view, toIndex);
8256         }
8257 
8258         /**
8259          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8260          *
8261          * <p>Scrapping a view allows it to be rebound and reused to show updated or
8262          * different data.</p>
8263          *
8264          * @param child Child to detach and scrap
8265          * @param recycler Recycler to deposit the new scrap view into
8266          */
detachAndScrapView(View child, Recycler recycler)8267         public void detachAndScrapView(View child, Recycler recycler) {
8268             int index = mChildHelper.indexOfChild(child);
8269             scrapOrRecycleView(recycler, index, child);
8270         }
8271 
8272         /**
8273          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8274          *
8275          * <p>Scrapping a view allows it to be rebound and reused to show updated or
8276          * different data.</p>
8277          *
8278          * @param index Index of child to detach and scrap
8279          * @param recycler Recycler to deposit the new scrap view into
8280          */
detachAndScrapViewAt(int index, Recycler recycler)8281         public void detachAndScrapViewAt(int index, Recycler recycler) {
8282             final View child = getChildAt(index);
8283             scrapOrRecycleView(recycler, index, child);
8284         }
8285 
8286         /**
8287          * Remove a child view and recycle it using the given Recycler.
8288          *
8289          * @param child Child to remove and recycle
8290          * @param recycler Recycler to use to recycle child
8291          */
removeAndRecycleView(View child, Recycler recycler)8292         public void removeAndRecycleView(View child, Recycler recycler) {
8293             removeView(child);
8294             recycler.recycleView(child);
8295         }
8296 
8297         /**
8298          * Remove a child view and recycle it using the given Recycler.
8299          *
8300          * @param index Index of child to remove and recycle
8301          * @param recycler Recycler to use to recycle child
8302          */
removeAndRecycleViewAt(int index, Recycler recycler)8303         public void removeAndRecycleViewAt(int index, Recycler recycler) {
8304             final View view = getChildAt(index);
8305             removeViewAt(index);
8306             recycler.recycleView(view);
8307         }
8308 
8309         /**
8310          * Return the current number of child views attached to the parent RecyclerView.
8311          * This does not include child views that were temporarily detached and/or scrapped.
8312          *
8313          * @return Number of attached children
8314          */
getChildCount()8315         public int getChildCount() {
8316             return mChildHelper != null ? mChildHelper.getChildCount() : 0;
8317         }
8318 
8319         /**
8320          * Return the child view at the given index
8321          * @param index Index of child to return
8322          * @return Child view at index
8323          */
getChildAt(int index)8324         public View getChildAt(int index) {
8325             return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
8326         }
8327 
8328         /**
8329          * Return the width measurement spec mode of the RecyclerView.
8330          * <p>
8331          * This value is set only if the LayoutManager opts into the auto measure api via
8332          * {@link #setAutoMeasureEnabled(boolean)}.
8333          * <p>
8334          * When RecyclerView is running a layout, this value is always set to
8335          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8336          *
8337          * @return Width measure spec mode.
8338          *
8339          * @see View.MeasureSpec#getMode(int)
8340          * @see View#onMeasure(int, int)
8341          */
getWidthMode()8342         public int getWidthMode() {
8343             return mWidthMode;
8344         }
8345 
8346         /**
8347          * Return the height measurement spec mode of the RecyclerView.
8348          * <p>
8349          * This value is set only if the LayoutManager opts into the auto measure api via
8350          * {@link #setAutoMeasureEnabled(boolean)}.
8351          * <p>
8352          * When RecyclerView is running a layout, this value is always set to
8353          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8354          *
8355          * @return Height measure spec mode.
8356          *
8357          * @see View.MeasureSpec#getMode(int)
8358          * @see View#onMeasure(int, int)
8359          */
getHeightMode()8360         public int getHeightMode() {
8361             return mHeightMode;
8362         }
8363 
8364         /**
8365          * Return the width of the parent RecyclerView
8366          *
8367          * @return Width in pixels
8368          */
getWidth()8369         public int getWidth() {
8370             return mWidth;
8371         }
8372 
8373         /**
8374          * Return the height of the parent RecyclerView
8375          *
8376          * @return Height in pixels
8377          */
getHeight()8378         public int getHeight() {
8379             return mHeight;
8380         }
8381 
8382         /**
8383          * Return the left padding of the parent RecyclerView
8384          *
8385          * @return Padding in pixels
8386          */
getPaddingLeft()8387         public int getPaddingLeft() {
8388             return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
8389         }
8390 
8391         /**
8392          * Return the top padding of the parent RecyclerView
8393          *
8394          * @return Padding in pixels
8395          */
getPaddingTop()8396         public int getPaddingTop() {
8397             return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
8398         }
8399 
8400         /**
8401          * Return the right padding of the parent RecyclerView
8402          *
8403          * @return Padding in pixels
8404          */
getPaddingRight()8405         public int getPaddingRight() {
8406             return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
8407         }
8408 
8409         /**
8410          * Return the bottom padding of the parent RecyclerView
8411          *
8412          * @return Padding in pixels
8413          */
getPaddingBottom()8414         public int getPaddingBottom() {
8415             return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
8416         }
8417 
8418         /**
8419          * Return the start padding of the parent RecyclerView
8420          *
8421          * @return Padding in pixels
8422          */
getPaddingStart()8423         public int getPaddingStart() {
8424             return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;
8425         }
8426 
8427         /**
8428          * Return the end padding of the parent RecyclerView
8429          *
8430          * @return Padding in pixels
8431          */
getPaddingEnd()8432         public int getPaddingEnd() {
8433             return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;
8434         }
8435 
8436         /**
8437          * Returns true if the RecyclerView this LayoutManager is bound to has focus.
8438          *
8439          * @return True if the RecyclerView has focus, false otherwise.
8440          * @see View#isFocused()
8441          */
isFocused()8442         public boolean isFocused() {
8443             return mRecyclerView != null && mRecyclerView.isFocused();
8444         }
8445 
8446         /**
8447          * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
8448          *
8449          * @return true if the RecyclerView has or contains focus
8450          * @see View#hasFocus()
8451          */
hasFocus()8452         public boolean hasFocus() {
8453             return mRecyclerView != null && mRecyclerView.hasFocus();
8454         }
8455 
8456         /**
8457          * Returns the item View which has or contains focus.
8458          *
8459          * @return A direct child of RecyclerView which has focus or contains the focused child.
8460          */
getFocusedChild()8461         public View getFocusedChild() {
8462             if (mRecyclerView == null) {
8463                 return null;
8464             }
8465             final View focused = mRecyclerView.getFocusedChild();
8466             if (focused == null || mChildHelper.isHidden(focused)) {
8467                 return null;
8468             }
8469             return focused;
8470         }
8471 
8472         /**
8473          * Returns the number of items in the adapter bound to the parent RecyclerView.
8474          * <p>
8475          * Note that this number is not necessarily equal to
8476          * {@link State#getItemCount() State#getItemCount()}. In methods where {@link State} is
8477          * available, you should use {@link State#getItemCount() State#getItemCount()} instead.
8478          * For more details, check the documentation for
8479          * {@link State#getItemCount() State#getItemCount()}.
8480          *
8481          * @return The number of items in the bound adapter
8482          * @see State#getItemCount()
8483          */
getItemCount()8484         public int getItemCount() {
8485             final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
8486             return a != null ? a.getItemCount() : 0;
8487         }
8488 
8489         /**
8490          * Offset all child views attached to the parent RecyclerView by dx pixels along
8491          * the horizontal axis.
8492          *
8493          * @param dx Pixels to offset by
8494          */
offsetChildrenHorizontal(int dx)8495         public void offsetChildrenHorizontal(int dx) {
8496             if (mRecyclerView != null) {
8497                 mRecyclerView.offsetChildrenHorizontal(dx);
8498             }
8499         }
8500 
8501         /**
8502          * Offset all child views attached to the parent RecyclerView by dy pixels along
8503          * the vertical axis.
8504          *
8505          * @param dy Pixels to offset by
8506          */
offsetChildrenVertical(int dy)8507         public void offsetChildrenVertical(int dy) {
8508             if (mRecyclerView != null) {
8509                 mRecyclerView.offsetChildrenVertical(dy);
8510             }
8511         }
8512 
8513         /**
8514          * Flags a view so that it will not be scrapped or recycled.
8515          * <p>
8516          * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
8517          * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
8518          * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
8519          * ignore the child.
8520          * <p>
8521          * Before this child can be recycled again, you have to call
8522          * {@link #stopIgnoringView(View)}.
8523          * <p>
8524          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8525          *
8526          * @param view View to ignore.
8527          * @see #stopIgnoringView(View)
8528          */
ignoreView(View view)8529         public void ignoreView(View view) {
8530             if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
8531                 // checking this because calling this method on a recycled or detached view may
8532                 // cause loss of state.
8533                 throw new IllegalArgumentException("View should be fully attached to be ignored"
8534                         + mRecyclerView.exceptionLabel());
8535             }
8536             final ViewHolder vh = getChildViewHolderInt(view);
8537             vh.addFlags(ViewHolder.FLAG_IGNORE);
8538             mRecyclerView.mViewInfoStore.removeViewHolder(vh);
8539         }
8540 
8541         /**
8542          * View can be scrapped and recycled again.
8543          * <p>
8544          * Note that calling this method removes all information in the view holder.
8545          * <p>
8546          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8547          *
8548          * @param view View to ignore.
8549          */
stopIgnoringView(View view)8550         public void stopIgnoringView(View view) {
8551             final ViewHolder vh = getChildViewHolderInt(view);
8552             vh.stopIgnoring();
8553             vh.resetInternal();
8554             vh.addFlags(ViewHolder.FLAG_INVALID);
8555         }
8556 
8557         /**
8558          * Temporarily detach and scrap all currently attached child views. Views will be scrapped
8559          * into the given Recycler. The Recycler may prefer to reuse scrap views before
8560          * other views that were previously recycled.
8561          *
8562          * @param recycler Recycler to scrap views into
8563          */
detachAndScrapAttachedViews(Recycler recycler)8564         public void detachAndScrapAttachedViews(Recycler recycler) {
8565             final int childCount = getChildCount();
8566             for (int i = childCount - 1; i >= 0; i--) {
8567                 final View v = getChildAt(i);
8568                 scrapOrRecycleView(recycler, i, v);
8569             }
8570         }
8571 
scrapOrRecycleView(Recycler recycler, int index, View view)8572         private void scrapOrRecycleView(Recycler recycler, int index, View view) {
8573             final ViewHolder viewHolder = getChildViewHolderInt(view);
8574             if (viewHolder.shouldIgnore()) {
8575                 if (DEBUG) {
8576                     Log.d(TAG, "ignoring view " + viewHolder);
8577                 }
8578                 return;
8579             }
8580             if (viewHolder.isInvalid() && !viewHolder.isRemoved()
8581                     && !mRecyclerView.mAdapter.hasStableIds()) {
8582                 removeViewAt(index);
8583                 recycler.recycleViewHolderInternal(viewHolder);
8584             } else {
8585                 detachViewAt(index);
8586                 recycler.scrapView(view);
8587                 mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
8588             }
8589         }
8590 
8591         /**
8592          * Recycles the scrapped views.
8593          * <p>
8594          * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
8595          * the expected behavior if scrapped views are used for animations. Otherwise, we need to
8596          * call remove and invalidate RecyclerView to ensure UI update.
8597          *
8598          * @param recycler Recycler
8599          */
removeAndRecycleScrapInt(Recycler recycler)8600         void removeAndRecycleScrapInt(Recycler recycler) {
8601             final int scrapCount = recycler.getScrapCount();
8602             // Loop backward, recycler might be changed by removeDetachedView()
8603             for (int i = scrapCount - 1; i >= 0; i--) {
8604                 final View scrap = recycler.getScrapViewAt(i);
8605                 final ViewHolder vh = getChildViewHolderInt(scrap);
8606                 if (vh.shouldIgnore()) {
8607                     continue;
8608                 }
8609                 // If the scrap view is animating, we need to cancel them first. If we cancel it
8610                 // here, ItemAnimator callback may recycle it which will cause double recycling.
8611                 // To avoid this, we mark it as not recycleable before calling the item animator.
8612                 // Since removeDetachedView calls a user API, a common mistake (ending animations on
8613                 // the view) may recycle it too, so we guard it before we call user APIs.
8614                 vh.setIsRecyclable(false);
8615                 if (vh.isTmpDetached()) {
8616                     mRecyclerView.removeDetachedView(scrap, false);
8617                 }
8618                 if (mRecyclerView.mItemAnimator != null) {
8619                     mRecyclerView.mItemAnimator.endAnimation(vh);
8620                 }
8621                 vh.setIsRecyclable(true);
8622                 recycler.quickRecycleScrapView(scrap);
8623             }
8624             recycler.clearScrap();
8625             if (scrapCount > 0) {
8626                 mRecyclerView.invalidate();
8627             }
8628         }
8629 
8630 
8631         /**
8632          * Measure a child view using standard measurement policy, taking the padding
8633          * of the parent RecyclerView and any added item decorations into account.
8634          *
8635          * <p>If the RecyclerView can be scrolled in either dimension the caller may
8636          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8637          *
8638          * @param child Child view to measure
8639          * @param widthUsed Width in pixels currently consumed by other views, if relevant
8640          * @param heightUsed Height in pixels currently consumed by other views, if relevant
8641          */
measureChild(View child, int widthUsed, int heightUsed)8642         public void measureChild(View child, int widthUsed, int heightUsed) {
8643             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8644 
8645             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8646             widthUsed += insets.left + insets.right;
8647             heightUsed += insets.top + insets.bottom;
8648             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8649                     getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
8650                     canScrollHorizontally());
8651             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8652                     getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
8653                     canScrollVertically());
8654             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8655                 child.measure(widthSpec, heightSpec);
8656             }
8657         }
8658 
8659         /**
8660          * RecyclerView internally does its own View measurement caching which should help with
8661          * WRAP_CONTENT.
8662          * <p>
8663          * Use this method if the View is already measured once in this layout pass.
8664          */
shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp)8665         boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8666             return !mMeasurementCacheEnabled
8667                     || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
8668                     || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
8669         }
8670 
8671         // we may consider making this public
8672         /**
8673          * RecyclerView internally does its own View measurement caching which should help with
8674          * WRAP_CONTENT.
8675          * <p>
8676          * Use this method if the View is not yet measured and you need to decide whether to
8677          * measure this View or not.
8678          */
shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp)8679         boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8680             return child.isLayoutRequested()
8681                     || !mMeasurementCacheEnabled
8682                     || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
8683                     || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
8684         }
8685 
8686         /**
8687          * In addition to the View Framework's measurement cache, RecyclerView uses its own
8688          * additional measurement cache for its children to avoid re-measuring them when not
8689          * necessary. It is on by default but it can be turned off via
8690          * {@link #setMeasurementCacheEnabled(boolean)}.
8691          *
8692          * @return True if measurement cache is enabled, false otherwise.
8693          *
8694          * @see #setMeasurementCacheEnabled(boolean)
8695          */
isMeasurementCacheEnabled()8696         public boolean isMeasurementCacheEnabled() {
8697             return mMeasurementCacheEnabled;
8698         }
8699 
8700         /**
8701          * Sets whether RecyclerView should use its own measurement cache for the children. This is
8702          * a more aggressive cache than the framework uses.
8703          *
8704          * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
8705          *
8706          * @see #isMeasurementCacheEnabled()
8707          */
setMeasurementCacheEnabled(boolean measurementCacheEnabled)8708         public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
8709             mMeasurementCacheEnabled = measurementCacheEnabled;
8710         }
8711 
isMeasurementUpToDate(int childSize, int spec, int dimension)8712         private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
8713             final int specMode = MeasureSpec.getMode(spec);
8714             final int specSize = MeasureSpec.getSize(spec);
8715             if (dimension > 0 && childSize != dimension) {
8716                 return false;
8717             }
8718             switch (specMode) {
8719                 case MeasureSpec.UNSPECIFIED:
8720                     return true;
8721                 case MeasureSpec.AT_MOST:
8722                     return specSize >= childSize;
8723                 case MeasureSpec.EXACTLY:
8724                     return  specSize == childSize;
8725             }
8726             return false;
8727         }
8728 
8729         /**
8730          * Measure a child view using standard measurement policy, taking the padding
8731          * of the parent RecyclerView, any added item decorations and the child margins
8732          * into account.
8733          *
8734          * <p>If the RecyclerView can be scrolled in either dimension the caller may
8735          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8736          *
8737          * @param child Child view to measure
8738          * @param widthUsed Width in pixels currently consumed by other views, if relevant
8739          * @param heightUsed Height in pixels currently consumed by other views, if relevant
8740          */
measureChildWithMargins(View child, int widthUsed, int heightUsed)8741         public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
8742             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8743 
8744             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8745             widthUsed += insets.left + insets.right;
8746             heightUsed += insets.top + insets.bottom;
8747 
8748             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8749                     getPaddingLeft() + getPaddingRight()
8750                             + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
8751                     canScrollHorizontally());
8752             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8753                     getPaddingTop() + getPaddingBottom()
8754                             + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
8755                     canScrollVertically());
8756             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8757                 child.measure(widthSpec, heightSpec);
8758             }
8759         }
8760 
8761         /**
8762          * Calculate a MeasureSpec value for measuring a child view in one dimension.
8763          *
8764          * @param parentSize Size of the parent view where the child will be placed
8765          * @param padding Total space currently consumed by other elements of the parent
8766          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
8767          *                       Generally obtained from the child view's LayoutParams
8768          * @param canScroll true if the parent RecyclerView can scroll in this dimension
8769          *
8770          * @return a MeasureSpec value for the child view
8771          * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
8772          */
8773         @Deprecated
getChildMeasureSpec(int parentSize, int padding, int childDimension, boolean canScroll)8774         public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
8775                 boolean canScroll) {
8776             int size = Math.max(0, parentSize - padding);
8777             int resultSize = 0;
8778             int resultMode = 0;
8779             if (canScroll) {
8780                 if (childDimension >= 0) {
8781                     resultSize = childDimension;
8782                     resultMode = MeasureSpec.EXACTLY;
8783                 } else {
8784                     // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
8785                     // instead using UNSPECIFIED.
8786                     resultSize = 0;
8787                     resultMode = MeasureSpec.UNSPECIFIED;
8788                 }
8789             } else {
8790                 if (childDimension >= 0) {
8791                     resultSize = childDimension;
8792                     resultMode = MeasureSpec.EXACTLY;
8793                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
8794                     resultSize = size;
8795                     // TODO this should be my spec.
8796                     resultMode = MeasureSpec.EXACTLY;
8797                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8798                     resultSize = size;
8799                     resultMode = MeasureSpec.AT_MOST;
8800                 }
8801             }
8802             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
8803         }
8804 
8805         /**
8806          * Calculate a MeasureSpec value for measuring a child view in one dimension.
8807          *
8808          * @param parentSize Size of the parent view where the child will be placed
8809          * @param parentMode The measurement spec mode of the parent
8810          * @param padding Total space currently consumed by other elements of parent
8811          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
8812          *                       Generally obtained from the child view's LayoutParams
8813          * @param canScroll true if the parent RecyclerView can scroll in this dimension
8814          *
8815          * @return a MeasureSpec value for the child view
8816          */
getChildMeasureSpec(int parentSize, int parentMode, int padding, int childDimension, boolean canScroll)8817         public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
8818                 int childDimension, boolean canScroll) {
8819             int size = Math.max(0, parentSize - padding);
8820             int resultSize = 0;
8821             int resultMode = 0;
8822             if (canScroll) {
8823                 if (childDimension >= 0) {
8824                     resultSize = childDimension;
8825                     resultMode = MeasureSpec.EXACTLY;
8826                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
8827                     switch (parentMode) {
8828                         case MeasureSpec.AT_MOST:
8829                         case MeasureSpec.EXACTLY:
8830                             resultSize = size;
8831                             resultMode = parentMode;
8832                             break;
8833                         case MeasureSpec.UNSPECIFIED:
8834                             resultSize = 0;
8835                             resultMode = MeasureSpec.UNSPECIFIED;
8836                             break;
8837                     }
8838                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8839                     resultSize = 0;
8840                     resultMode = MeasureSpec.UNSPECIFIED;
8841                 }
8842             } else {
8843                 if (childDimension >= 0) {
8844                     resultSize = childDimension;
8845                     resultMode = MeasureSpec.EXACTLY;
8846                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
8847                     resultSize = size;
8848                     resultMode = parentMode;
8849                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8850                     resultSize = size;
8851                     if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
8852                         resultMode = MeasureSpec.AT_MOST;
8853                     } else {
8854                         resultMode = MeasureSpec.UNSPECIFIED;
8855                     }
8856 
8857                 }
8858             }
8859             //noinspection WrongConstant
8860             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
8861         }
8862 
8863         /**
8864          * Returns the measured width of the given child, plus the additional size of
8865          * any insets applied by {@link ItemDecoration ItemDecorations}.
8866          *
8867          * @param child Child view to query
8868          * @return child's measured width plus <code>ItemDecoration</code> insets
8869          *
8870          * @see View#getMeasuredWidth()
8871          */
getDecoratedMeasuredWidth(View child)8872         public int getDecoratedMeasuredWidth(View child) {
8873             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8874             return child.getMeasuredWidth() + insets.left + insets.right;
8875         }
8876 
8877         /**
8878          * Returns the measured height of the given child, plus the additional size of
8879          * any insets applied by {@link ItemDecoration ItemDecorations}.
8880          *
8881          * @param child Child view to query
8882          * @return child's measured height plus <code>ItemDecoration</code> insets
8883          *
8884          * @see View#getMeasuredHeight()
8885          */
getDecoratedMeasuredHeight(View child)8886         public int getDecoratedMeasuredHeight(View child) {
8887             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8888             return child.getMeasuredHeight() + insets.top + insets.bottom;
8889         }
8890 
8891         /**
8892          * Lay out the given child view within the RecyclerView using coordinates that
8893          * include any current {@link ItemDecoration ItemDecorations}.
8894          *
8895          * <p>LayoutManagers should prefer working in sizes and coordinates that include
8896          * item decoration insets whenever possible. This allows the LayoutManager to effectively
8897          * ignore decoration insets within measurement and layout code. See the following
8898          * methods:</p>
8899          * <ul>
8900          *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
8901          *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
8902          *     <li>{@link #measureChild(View, int, int)}</li>
8903          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
8904          *     <li>{@link #getDecoratedLeft(View)}</li>
8905          *     <li>{@link #getDecoratedTop(View)}</li>
8906          *     <li>{@link #getDecoratedRight(View)}</li>
8907          *     <li>{@link #getDecoratedBottom(View)}</li>
8908          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
8909          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
8910          * </ul>
8911          *
8912          * @param child Child to lay out
8913          * @param left Left edge, with item decoration insets included
8914          * @param top Top edge, with item decoration insets included
8915          * @param right Right edge, with item decoration insets included
8916          * @param bottom Bottom edge, with item decoration insets included
8917          *
8918          * @see View#layout(int, int, int, int)
8919          * @see #layoutDecoratedWithMargins(View, int, int, int, int)
8920          */
layoutDecorated(View child, int left, int top, int right, int bottom)8921         public void layoutDecorated(View child, int left, int top, int right, int bottom) {
8922             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8923             child.layout(left + insets.left, top + insets.top, right - insets.right,
8924                     bottom - insets.bottom);
8925         }
8926 
8927         /**
8928          * Lay out the given child view within the RecyclerView using coordinates that
8929          * include any current {@link ItemDecoration ItemDecorations} and margins.
8930          *
8931          * <p>LayoutManagers should prefer working in sizes and coordinates that include
8932          * item decoration insets whenever possible. This allows the LayoutManager to effectively
8933          * ignore decoration insets within measurement and layout code. See the following
8934          * methods:</p>
8935          * <ul>
8936          *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
8937          *     <li>{@link #measureChild(View, int, int)}</li>
8938          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
8939          *     <li>{@link #getDecoratedLeft(View)}</li>
8940          *     <li>{@link #getDecoratedTop(View)}</li>
8941          *     <li>{@link #getDecoratedRight(View)}</li>
8942          *     <li>{@link #getDecoratedBottom(View)}</li>
8943          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
8944          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
8945          * </ul>
8946          *
8947          * @param child Child to lay out
8948          * @param left Left edge, with item decoration insets and left margin included
8949          * @param top Top edge, with item decoration insets and top margin included
8950          * @param right Right edge, with item decoration insets and right margin included
8951          * @param bottom Bottom edge, with item decoration insets and bottom margin included
8952          *
8953          * @see View#layout(int, int, int, int)
8954          * @see #layoutDecorated(View, int, int, int, int)
8955          */
layoutDecoratedWithMargins(View child, int left, int top, int right, int bottom)8956         public void layoutDecoratedWithMargins(View child, int left, int top, int right,
8957                 int bottom) {
8958             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8959             final Rect insets = lp.mDecorInsets;
8960             child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
8961                     right - insets.right - lp.rightMargin,
8962                     bottom - insets.bottom - lp.bottomMargin);
8963         }
8964 
8965         /**
8966          * Calculates the bounding box of the View while taking into account its matrix changes
8967          * (translation, scale etc) with respect to the RecyclerView.
8968          * <p>
8969          * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
8970          * the View's matrix so that the decor offsets also go through the same transformation.
8971          *
8972          * @param child The ItemView whose bounding box should be calculated.
8973          * @param includeDecorInsets True if the decor insets should be included in the bounding box
8974          * @param out The rectangle into which the output will be written.
8975          */
getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out)8976         public void getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out) {
8977             if (includeDecorInsets) {
8978                 Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8979                 out.set(-insets.left, -insets.top,
8980                         child.getWidth() + insets.right, child.getHeight() + insets.bottom);
8981             } else {
8982                 out.set(0, 0, child.getWidth(), child.getHeight());
8983             }
8984 
8985             if (mRecyclerView != null) {
8986                 final Matrix childMatrix = child.getMatrix();
8987                 if (childMatrix != null && !childMatrix.isIdentity()) {
8988                     final RectF tempRectF = mRecyclerView.mTempRectF;
8989                     tempRectF.set(out);
8990                     childMatrix.mapRect(tempRectF);
8991                     out.set(
8992                             (int) Math.floor(tempRectF.left),
8993                             (int) Math.floor(tempRectF.top),
8994                             (int) Math.ceil(tempRectF.right),
8995                             (int) Math.ceil(tempRectF.bottom)
8996                     );
8997                 }
8998             }
8999             out.offset(child.getLeft(), child.getTop());
9000         }
9001 
9002         /**
9003          * Returns the bounds of the view including its decoration and margins.
9004          *
9005          * @param view The view element to check
9006          * @param outBounds A rect that will receive the bounds of the element including its
9007          *                  decoration and margins.
9008          */
getDecoratedBoundsWithMargins(View view, Rect outBounds)9009         public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
9010             RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
9011         }
9012 
9013         /**
9014          * Returns the left edge of the given child view within its parent, offset by any applied
9015          * {@link ItemDecoration ItemDecorations}.
9016          *
9017          * @param child Child to query
9018          * @return Child left edge with offsets applied
9019          * @see #getLeftDecorationWidth(View)
9020          */
getDecoratedLeft(View child)9021         public int getDecoratedLeft(View child) {
9022             return child.getLeft() - getLeftDecorationWidth(child);
9023         }
9024 
9025         /**
9026          * Returns the top edge of the given child view within its parent, offset by any applied
9027          * {@link ItemDecoration ItemDecorations}.
9028          *
9029          * @param child Child to query
9030          * @return Child top edge with offsets applied
9031          * @see #getTopDecorationHeight(View)
9032          */
getDecoratedTop(View child)9033         public int getDecoratedTop(View child) {
9034             return child.getTop() - getTopDecorationHeight(child);
9035         }
9036 
9037         /**
9038          * Returns the right edge of the given child view within its parent, offset by any applied
9039          * {@link ItemDecoration ItemDecorations}.
9040          *
9041          * @param child Child to query
9042          * @return Child right edge with offsets applied
9043          * @see #getRightDecorationWidth(View)
9044          */
getDecoratedRight(View child)9045         public int getDecoratedRight(View child) {
9046             return child.getRight() + getRightDecorationWidth(child);
9047         }
9048 
9049         /**
9050          * Returns the bottom edge of the given child view within its parent, offset by any applied
9051          * {@link ItemDecoration ItemDecorations}.
9052          *
9053          * @param child Child to query
9054          * @return Child bottom edge with offsets applied
9055          * @see #getBottomDecorationHeight(View)
9056          */
getDecoratedBottom(View child)9057         public int getDecoratedBottom(View child) {
9058             return child.getBottom() + getBottomDecorationHeight(child);
9059         }
9060 
9061         /**
9062          * Calculates the item decor insets applied to the given child and updates the provided
9063          * Rect instance with the inset values.
9064          * <ul>
9065          *     <li>The Rect's left is set to the total width of left decorations.</li>
9066          *     <li>The Rect's top is set to the total height of top decorations.</li>
9067          *     <li>The Rect's right is set to the total width of right decorations.</li>
9068          *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
9069          * </ul>
9070          * <p>
9071          * Note that item decorations are automatically calculated when one of the LayoutManager's
9072          * measure child methods is called. If you need to measure the child with custom specs via
9073          * {@link View#measure(int, int)}, you can use this method to get decorations.
9074          *
9075          * @param child The child view whose decorations should be calculated
9076          * @param outRect The Rect to hold result values
9077          */
calculateItemDecorationsForChild(View child, Rect outRect)9078         public void calculateItemDecorationsForChild(View child, Rect outRect) {
9079             if (mRecyclerView == null) {
9080                 outRect.set(0, 0, 0, 0);
9081                 return;
9082             }
9083             Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
9084             outRect.set(insets);
9085         }
9086 
9087         /**
9088          * Returns the total height of item decorations applied to child's top.
9089          * <p>
9090          * Note that this value is not updated until the View is measured or
9091          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9092          *
9093          * @param child Child to query
9094          * @return The total height of item decorations applied to the child's top.
9095          * @see #getDecoratedTop(View)
9096          * @see #calculateItemDecorationsForChild(View, Rect)
9097          */
getTopDecorationHeight(View child)9098         public int getTopDecorationHeight(View child) {
9099             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
9100         }
9101 
9102         /**
9103          * Returns the total height of item decorations applied to child's bottom.
9104          * <p>
9105          * Note that this value is not updated until the View is measured or
9106          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9107          *
9108          * @param child Child to query
9109          * @return The total height of item decorations applied to the child's bottom.
9110          * @see #getDecoratedBottom(View)
9111          * @see #calculateItemDecorationsForChild(View, Rect)
9112          */
getBottomDecorationHeight(View child)9113         public int getBottomDecorationHeight(View child) {
9114             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
9115         }
9116 
9117         /**
9118          * Returns the total width of item decorations applied to child's left.
9119          * <p>
9120          * Note that this value is not updated until the View is measured or
9121          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9122          *
9123          * @param child Child to query
9124          * @return The total width of item decorations applied to the child's left.
9125          * @see #getDecoratedLeft(View)
9126          * @see #calculateItemDecorationsForChild(View, Rect)
9127          */
getLeftDecorationWidth(View child)9128         public int getLeftDecorationWidth(View child) {
9129             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
9130         }
9131 
9132         /**
9133          * Returns the total width of item decorations applied to child's right.
9134          * <p>
9135          * Note that this value is not updated until the View is measured or
9136          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9137          *
9138          * @param child Child to query
9139          * @return The total width of item decorations applied to the child's right.
9140          * @see #getDecoratedRight(View)
9141          * @see #calculateItemDecorationsForChild(View, Rect)
9142          */
getRightDecorationWidth(View child)9143         public int getRightDecorationWidth(View child) {
9144             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
9145         }
9146 
9147         /**
9148          * Called when searching for a focusable view in the given direction has failed
9149          * for the current content of the RecyclerView.
9150          *
9151          * <p>This is the LayoutManager's opportunity to populate views in the given direction
9152          * to fulfill the request if it can. The LayoutManager should attach and return
9153          * the view to be focused, if a focusable view in the given direction is found.
9154          * Otherwise, if all the existing (or the newly populated views) are unfocusable, it returns
9155          * the next unfocusable view to become visible on the screen. This unfocusable view is
9156          * typically the first view that's either partially or fully out of RV's padded bounded
9157          * area in the given direction. The default implementation returns null.</p>
9158          *
9159          * @param focused   The currently focused view
9160          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9161          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9162          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9163          *                  or 0 for not applicable
9164          * @param recycler  The recycler to use for obtaining views for currently offscreen items
9165          * @param state     Transient state of RecyclerView
9166          * @return The chosen view to be focused if a focusable view is found, otherwise an
9167          * unfocusable view to become visible onto the screen, else null.
9168          */
9169         @Nullable
onFocusSearchFailed(View focused, int direction, Recycler recycler, State state)9170         public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
9171                 State state) {
9172             return null;
9173         }
9174 
9175         /**
9176          * This method gives a LayoutManager an opportunity to intercept the initial focus search
9177          * before the default behavior of {@link FocusFinder} is used. If this method returns
9178          * null FocusFinder will attempt to find a focusable child view. If it fails
9179          * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
9180          * will be called to give the LayoutManager an opportunity to add new views for items
9181          * that did not have attached views representing them. The LayoutManager should not add
9182          * or remove views from this method.
9183          *
9184          * @param focused The currently focused view
9185          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9186          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9187          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9188          * @return A descendant view to focus or null to fall back to default behavior.
9189          *         The default implementation returns null.
9190          */
onInterceptFocusSearch(View focused, int direction)9191         public View onInterceptFocusSearch(View focused, int direction) {
9192             return null;
9193         }
9194 
9195         /**
9196          * Returns the scroll amount that brings the given rect in child's coordinate system within
9197          * the padded area of RecyclerView.
9198          * @param parent The parent RecyclerView.
9199          * @param child The direct child making the request.
9200          * @param rect The rectangle in the child's coordinates the child
9201          *             wishes to be on the screen.
9202          * @param immediate True to forbid animated or delayed scrolling,
9203          *                  false otherwise
9204          * @return The array containing the scroll amount in x and y directions that brings the
9205          * given rect into RV's padded area.
9206          */
getChildRectangleOnScreenScrollAmount(RecyclerView parent, View child, Rect rect, boolean immediate)9207         private int[] getChildRectangleOnScreenScrollAmount(RecyclerView parent, View child,
9208                 Rect rect, boolean immediate) {
9209             int[] out = new int[2];
9210             final int parentLeft = getPaddingLeft();
9211             final int parentTop = getPaddingTop();
9212             final int parentRight = getWidth() - getPaddingRight();
9213             final int parentBottom = getHeight() - getPaddingBottom();
9214             final int childLeft = child.getLeft() + rect.left - child.getScrollX();
9215             final int childTop = child.getTop() + rect.top - child.getScrollY();
9216             final int childRight = childLeft + rect.width();
9217             final int childBottom = childTop + rect.height();
9218 
9219             final int offScreenLeft = Math.min(0, childLeft - parentLeft);
9220             final int offScreenTop = Math.min(0, childTop - parentTop);
9221             final int offScreenRight = Math.max(0, childRight - parentRight);
9222             final int offScreenBottom = Math.max(0, childBottom - parentBottom);
9223 
9224             // Favor the "start" layout direction over the end when bringing one side or the other
9225             // of a large rect into view. If we decide to bring in end because start is already
9226             // visible, limit the scroll such that start won't go out of bounds.
9227             final int dx;
9228             if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) {
9229                 dx = offScreenRight != 0 ? offScreenRight
9230                         : Math.max(offScreenLeft, childRight - parentRight);
9231             } else {
9232                 dx = offScreenLeft != 0 ? offScreenLeft
9233                         : Math.min(childLeft - parentLeft, offScreenRight);
9234             }
9235 
9236             // Favor bringing the top into view over the bottom. If top is already visible and
9237             // we should scroll to make bottom visible, make sure top does not go out of bounds.
9238             final int dy = offScreenTop != 0 ? offScreenTop
9239                     : Math.min(childTop - parentTop, offScreenBottom);
9240             out[0] = dx;
9241             out[1] = dy;
9242             return out;
9243         }
9244         /**
9245          * Called when a child of the RecyclerView wants a particular rectangle to be positioned
9246          * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
9247          * android.graphics.Rect, boolean)} for more details.
9248          *
9249          * <p>The base implementation will attempt to perform a standard programmatic scroll
9250          * to bring the given rect into view, within the padded area of the RecyclerView.</p>
9251          *
9252          * @param child The direct child making the request.
9253          * @param rect  The rectangle in the child's coordinates the child
9254          *              wishes to be on the screen.
9255          * @param immediate True to forbid animated or delayed scrolling,
9256          *                  false otherwise
9257          * @return Whether the group scrolled to handle the operation
9258          */
requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect, boolean immediate)9259         public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
9260                 boolean immediate) {
9261             return requestChildRectangleOnScreen(parent, child, rect, immediate, false);
9262         }
9263 
9264         /**
9265          * Requests that the given child of the RecyclerView be positioned onto the screen. This
9266          * method can be called for both unfocusable and focusable child views. For unfocusable
9267          * child views, focusedChildVisible is typically true in which case, layout manager
9268          * makes the child view visible only if the currently focused child stays in-bounds of RV.
9269          * @param parent The parent RecyclerView.
9270          * @param child The direct child making the request.
9271          * @param rect The rectangle in the child's coordinates the child
9272          *              wishes to be on the screen.
9273          * @param immediate True to forbid animated or delayed scrolling,
9274          *                  false otherwise
9275          * @param focusedChildVisible Whether the currently focused view must stay visible.
9276          * @return Whether the group scrolled to handle the operation
9277          */
requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect, boolean immediate, boolean focusedChildVisible)9278         public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
9279                 boolean immediate,
9280                 boolean focusedChildVisible) {
9281             int[] scrollAmount = getChildRectangleOnScreenScrollAmount(parent, child, rect,
9282                     immediate);
9283             int dx = scrollAmount[0];
9284             int dy = scrollAmount[1];
9285             if (!focusedChildVisible || isFocusedChildVisibleAfterScrolling(parent, dx, dy)) {
9286                 if (dx != 0 || dy != 0) {
9287                     if (immediate) {
9288                         parent.scrollBy(dx, dy);
9289                     } else {
9290                         parent.smoothScrollBy(dx, dy);
9291                     }
9292                     return true;
9293                 }
9294             }
9295             return false;
9296         }
9297 
9298         /**
9299          * Returns whether the given child view is partially or fully visible within the padded
9300          * bounded area of RecyclerView, depending on the input parameters.
9301          * A view is partially visible if it has non-zero overlap with RV's padded bounded area.
9302          * If acceptEndPointInclusion flag is set to true, it's also considered partially
9303          * visible if it's located outside RV's bounds and it's hitting either RV's start or end
9304          * bounds.
9305          *
9306          * @param child The child view to be examined.
9307          * @param completelyVisible If true, the method returns true if and only if the child is
9308          *                          completely visible. If false, the method returns true if and
9309          *                          only if the child is only partially visible (that is it will
9310          *                          return false if the child is either completely visible or out
9311          *                          of RV's bounds).
9312          * @param acceptEndPointInclusion If the view's endpoint intersection with RV's start of end
9313          *                                bounds is enough to consider it partially visible,
9314          *                                false otherwise.
9315          * @return True if the given child is partially or fully visible, false otherwise.
9316          */
isViewPartiallyVisible(@onNull View child, boolean completelyVisible, boolean acceptEndPointInclusion)9317         public boolean isViewPartiallyVisible(@NonNull View child, boolean completelyVisible,
9318                 boolean acceptEndPointInclusion) {
9319             int boundsFlag = (ViewBoundsCheck.FLAG_CVS_GT_PVS | ViewBoundsCheck.FLAG_CVS_EQ_PVS
9320                     | ViewBoundsCheck.FLAG_CVE_LT_PVE | ViewBoundsCheck.FLAG_CVE_EQ_PVE);
9321             boolean isViewFullyVisible = mHorizontalBoundCheck.isViewWithinBoundFlags(child,
9322                     boundsFlag)
9323                     && mVerticalBoundCheck.isViewWithinBoundFlags(child, boundsFlag);
9324             if (completelyVisible) {
9325                 return isViewFullyVisible;
9326             } else {
9327                 return !isViewFullyVisible;
9328             }
9329         }
9330 
9331         /**
9332          * Returns whether the currently focused child stays within RV's bounds with the given
9333          * amount of scrolling.
9334          * @param parent The parent RecyclerView.
9335          * @param dx The scrolling in x-axis direction to be performed.
9336          * @param dy The scrolling in y-axis direction to be performed.
9337          * @return {@code false} if the focused child is not at least partially visible after
9338          *         scrolling or no focused child exists, {@code true} otherwise.
9339          */
isFocusedChildVisibleAfterScrolling(RecyclerView parent, int dx, int dy)9340         private boolean isFocusedChildVisibleAfterScrolling(RecyclerView parent, int dx, int dy) {
9341             final View focusedChild = parent.getFocusedChild();
9342             if (focusedChild == null) {
9343                 return false;
9344             }
9345             final int parentLeft = getPaddingLeft();
9346             final int parentTop = getPaddingTop();
9347             final int parentRight = getWidth() - getPaddingRight();
9348             final int parentBottom = getHeight() - getPaddingBottom();
9349             final Rect bounds = mRecyclerView.mTempRect;
9350             getDecoratedBoundsWithMargins(focusedChild, bounds);
9351 
9352             if (bounds.left - dx >= parentRight || bounds.right - dx <= parentLeft
9353                     || bounds.top - dy >= parentBottom || bounds.bottom - dy <= parentTop) {
9354                 return false;
9355             }
9356             return true;
9357         }
9358 
9359         /**
9360          * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
9361          */
9362         @Deprecated
onRequestChildFocus(RecyclerView parent, View child, View focused)9363         public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
9364             // eat the request if we are in the middle of a scroll or layout
9365             return isSmoothScrolling() || parent.isComputingLayout();
9366         }
9367 
9368         /**
9369          * Called when a descendant view of the RecyclerView requests focus.
9370          *
9371          * <p>A LayoutManager wishing to keep focused views aligned in a specific
9372          * portion of the view may implement that behavior in an override of this method.</p>
9373          *
9374          * <p>If the LayoutManager executes different behavior that should override the default
9375          * behavior of scrolling the focused child on screen instead of running alongside it,
9376          * this method should return true.</p>
9377          *
9378          * @param parent  The RecyclerView hosting this LayoutManager
9379          * @param state   Current state of RecyclerView
9380          * @param child   Direct child of the RecyclerView containing the newly focused view
9381          * @param focused The newly focused view. This may be the same view as child or it may be
9382          *                null
9383          * @return true if the default scroll behavior should be suppressed
9384          */
onRequestChildFocus(RecyclerView parent, State state, View child, View focused)9385         public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
9386                 View focused) {
9387             return onRequestChildFocus(parent, child, focused);
9388         }
9389 
9390         /**
9391          * Called if the RecyclerView this LayoutManager is bound to has a different adapter set.
9392          * The LayoutManager may use this opportunity to clear caches and configure state such
9393          * that it can relayout appropriately with the new data and potentially new view types.
9394          *
9395          * <p>The default implementation removes all currently attached views.</p>
9396          *
9397          * @param oldAdapter The previous adapter instance. Will be null if there was previously no
9398          *                   adapter.
9399          * @param newAdapter The new adapter instance. Might be null if
9400          *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
9401          */
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter)9402         public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
9403         }
9404 
9405         /**
9406          * Called to populate focusable views within the RecyclerView.
9407          *
9408          * <p>The LayoutManager implementation should return <code>true</code> if the default
9409          * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
9410          * suppressed.</p>
9411          *
9412          * <p>The default implementation returns <code>false</code> to trigger RecyclerView
9413          * to fall back to the default ViewGroup behavior.</p>
9414          *
9415          * @param recyclerView The RecyclerView hosting this LayoutManager
9416          * @param views List of output views. This method should add valid focusable views
9417          *              to this list.
9418          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9419          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9420          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9421          * @param focusableMode The type of focusables to be added.
9422          *
9423          * @return true to suppress the default behavior, false to add default focusables after
9424          *         this method returns.
9425          *
9426          * @see #FOCUSABLES_ALL
9427          * @see #FOCUSABLES_TOUCH_MODE
9428          */
onAddFocusables(RecyclerView recyclerView, ArrayList<View> views, int direction, int focusableMode)9429         public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,
9430                 int direction, int focusableMode) {
9431             return false;
9432         }
9433 
9434         /**
9435          * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving
9436          * detailed information on what has actually changed.
9437          *
9438          * @param recyclerView
9439          */
onItemsChanged(RecyclerView recyclerView)9440         public void onItemsChanged(RecyclerView recyclerView) {
9441         }
9442 
9443         /**
9444          * Called when items have been added to the adapter. The LayoutManager may choose to
9445          * requestLayout if the inserted items would require refreshing the currently visible set
9446          * of child views. (e.g. currently empty space would be filled by appended items, etc.)
9447          *
9448          * @param recyclerView
9449          * @param positionStart
9450          * @param itemCount
9451          */
onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount)9452         public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
9453         }
9454 
9455         /**
9456          * Called when items have been removed from the adapter.
9457          *
9458          * @param recyclerView
9459          * @param positionStart
9460          * @param itemCount
9461          */
onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount)9462         public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
9463         }
9464 
9465         /**
9466          * Called when items have been changed in the adapter.
9467          * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
9468          * instead, then this callback will not be invoked.
9469          *
9470          * @param recyclerView
9471          * @param positionStart
9472          * @param itemCount
9473          */
onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount)9474         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
9475         }
9476 
9477         /**
9478          * Called when items have been changed in the adapter and with optional payload.
9479          * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
9480          *
9481          * @param recyclerView
9482          * @param positionStart
9483          * @param itemCount
9484          * @param payload
9485          */
onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount, Object payload)9486         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
9487                 Object payload) {
9488             onItemsUpdated(recyclerView, positionStart, itemCount);
9489         }
9490 
9491         /**
9492          * Called when an item is moved withing the adapter.
9493          * <p>
9494          * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
9495          * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
9496          * is called.
9497          *
9498          * @param recyclerView
9499          * @param from
9500          * @param to
9501          * @param itemCount
9502          */
onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount)9503         public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
9504 
9505         }
9506 
9507 
9508         /**
9509          * <p>Override this method if you want to support scroll bars.</p>
9510          *
9511          * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
9512          *
9513          * <p>Default implementation returns 0.</p>
9514          *
9515          * @param state Current state of RecyclerView
9516          * @return The horizontal extent of the scrollbar's thumb
9517          * @see RecyclerView#computeHorizontalScrollExtent()
9518          */
computeHorizontalScrollExtent(State state)9519         public int computeHorizontalScrollExtent(State state) {
9520             return 0;
9521         }
9522 
9523         /**
9524          * <p>Override this method if you want to support scroll bars.</p>
9525          *
9526          * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
9527          *
9528          * <p>Default implementation returns 0.</p>
9529          *
9530          * @param state Current State of RecyclerView where you can find total item count
9531          * @return The horizontal offset of the scrollbar's thumb
9532          * @see RecyclerView#computeHorizontalScrollOffset()
9533          */
computeHorizontalScrollOffset(State state)9534         public int computeHorizontalScrollOffset(State state) {
9535             return 0;
9536         }
9537 
9538         /**
9539          * <p>Override this method if you want to support scroll bars.</p>
9540          *
9541          * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
9542          *
9543          * <p>Default implementation returns 0.</p>
9544          *
9545          * @param state Current State of RecyclerView where you can find total item count
9546          * @return The total horizontal range represented by the vertical scrollbar
9547          * @see RecyclerView#computeHorizontalScrollRange()
9548          */
computeHorizontalScrollRange(State state)9549         public int computeHorizontalScrollRange(State state) {
9550             return 0;
9551         }
9552 
9553         /**
9554          * <p>Override this method if you want to support scroll bars.</p>
9555          *
9556          * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
9557          *
9558          * <p>Default implementation returns 0.</p>
9559          *
9560          * @param state Current state of RecyclerView
9561          * @return The vertical extent of the scrollbar's thumb
9562          * @see RecyclerView#computeVerticalScrollExtent()
9563          */
computeVerticalScrollExtent(State state)9564         public int computeVerticalScrollExtent(State state) {
9565             return 0;
9566         }
9567 
9568         /**
9569          * <p>Override this method if you want to support scroll bars.</p>
9570          *
9571          * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
9572          *
9573          * <p>Default implementation returns 0.</p>
9574          *
9575          * @param state Current State of RecyclerView where you can find total item count
9576          * @return The vertical offset of the scrollbar's thumb
9577          * @see RecyclerView#computeVerticalScrollOffset()
9578          */
computeVerticalScrollOffset(State state)9579         public int computeVerticalScrollOffset(State state) {
9580             return 0;
9581         }
9582 
9583         /**
9584          * <p>Override this method if you want to support scroll bars.</p>
9585          *
9586          * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
9587          *
9588          * <p>Default implementation returns 0.</p>
9589          *
9590          * @param state Current State of RecyclerView where you can find total item count
9591          * @return The total vertical range represented by the vertical scrollbar
9592          * @see RecyclerView#computeVerticalScrollRange()
9593          */
computeVerticalScrollRange(State state)9594         public int computeVerticalScrollRange(State state) {
9595             return 0;
9596         }
9597 
9598         /**
9599          * Measure the attached RecyclerView. Implementations must call
9600          * {@link #setMeasuredDimension(int, int)} before returning.
9601          *
9602          * <p>The default implementation will handle EXACTLY measurements and respect
9603          * the minimum width and height properties of the host RecyclerView if measured
9604          * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
9605          * will consume all available space.</p>
9606          *
9607          * @param recycler Recycler
9608          * @param state Transient state of RecyclerView
9609          * @param widthSpec Width {@link android.view.View.MeasureSpec}
9610          * @param heightSpec Height {@link android.view.View.MeasureSpec}
9611          */
onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec)9612         public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
9613             mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
9614         }
9615 
9616         /**
9617          * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
9618          * host RecyclerView.
9619          *
9620          * @param widthSize Measured width
9621          * @param heightSize Measured height
9622          */
setMeasuredDimension(int widthSize, int heightSize)9623         public void setMeasuredDimension(int widthSize, int heightSize) {
9624             mRecyclerView.setMeasuredDimension(widthSize, heightSize);
9625         }
9626 
9627         /**
9628          * @return The host RecyclerView's {@link View#getMinimumWidth()}
9629          */
getMinimumWidth()9630         public int getMinimumWidth() {
9631             return ViewCompat.getMinimumWidth(mRecyclerView);
9632         }
9633 
9634         /**
9635          * @return The host RecyclerView's {@link View#getMinimumHeight()}
9636          */
getMinimumHeight()9637         public int getMinimumHeight() {
9638             return ViewCompat.getMinimumHeight(mRecyclerView);
9639         }
9640         /**
9641          * <p>Called when the LayoutManager should save its state. This is a good time to save your
9642          * scroll position, configuration and anything else that may be required to restore the same
9643          * layout state if the LayoutManager is recreated.</p>
9644          * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
9645          * restore. This will let you share information between your LayoutManagers but it is also
9646          * your responsibility to make sure they use the same parcelable class.</p>
9647          *
9648          * @return Necessary information for LayoutManager to be able to restore its state
9649          */
onSaveInstanceState()9650         public Parcelable onSaveInstanceState() {
9651             return null;
9652         }
9653 
9654 
onRestoreInstanceState(Parcelable state)9655         public void onRestoreInstanceState(Parcelable state) {
9656 
9657         }
9658 
stopSmoothScroller()9659         void stopSmoothScroller() {
9660             if (mSmoothScroller != null) {
9661                 mSmoothScroller.stop();
9662             }
9663         }
9664 
onSmoothScrollerStopped(SmoothScroller smoothScroller)9665         private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
9666             if (mSmoothScroller == smoothScroller) {
9667                 mSmoothScroller = null;
9668             }
9669         }
9670 
9671         /**
9672          * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
9673          *
9674          * @param state The new scroll state for RecyclerView
9675          */
onScrollStateChanged(int state)9676         public void onScrollStateChanged(int state) {
9677         }
9678 
9679         /**
9680          * Removes all views and recycles them using the given recycler.
9681          * <p>
9682          * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
9683          * <p>
9684          * If a View is marked as "ignored", it is not removed nor recycled.
9685          *
9686          * @param recycler Recycler to use to recycle children
9687          * @see #removeAndRecycleView(View, Recycler)
9688          * @see #removeAndRecycleViewAt(int, Recycler)
9689          * @see #ignoreView(View)
9690          */
removeAndRecycleAllViews(Recycler recycler)9691         public void removeAndRecycleAllViews(Recycler recycler) {
9692             for (int i = getChildCount() - 1; i >= 0; i--) {
9693                 final View view = getChildAt(i);
9694                 if (!getChildViewHolderInt(view).shouldIgnore()) {
9695                     removeAndRecycleViewAt(i, recycler);
9696                 }
9697             }
9698         }
9699 
9700         // called by accessibility delegate
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info)9701         void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
9702             onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
9703         }
9704 
9705         /**
9706          * Called by the AccessibilityDelegate when the information about the current layout should
9707          * be populated.
9708          * <p>
9709          * Default implementation adds a {@link
9710          * android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}.
9711          * <p>
9712          * You should override
9713          * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
9714          * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
9715          * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
9716          * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
9717          * more accurate accessibility information.
9718          *
9719          * @param recycler The Recycler that can be used to convert view positions into adapter
9720          *                 positions
9721          * @param state    The current state of RecyclerView
9722          * @param info     The info that should be filled by the LayoutManager
9723          * @see View#onInitializeAccessibilityNodeInfo(
9724          *android.view.accessibility.AccessibilityNodeInfo)
9725          * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9726          * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9727          * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
9728          * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9729          */
onInitializeAccessibilityNodeInfo(Recycler recycler, State state, AccessibilityNodeInfoCompat info)9730         public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
9731                 AccessibilityNodeInfoCompat info) {
9732             if (mRecyclerView.canScrollVertically(-1) || mRecyclerView.canScrollHorizontally(-1)) {
9733                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
9734                 info.setScrollable(true);
9735             }
9736             if (mRecyclerView.canScrollVertically(1) || mRecyclerView.canScrollHorizontally(1)) {
9737                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
9738                 info.setScrollable(true);
9739             }
9740             final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo =
9741                     AccessibilityNodeInfoCompat.CollectionInfoCompat
9742                             .obtain(getRowCountForAccessibility(recycler, state),
9743                                     getColumnCountForAccessibility(recycler, state),
9744                                     isLayoutHierarchical(recycler, state),
9745                                     getSelectionModeForAccessibility(recycler, state));
9746             info.setCollectionInfo(collectionInfo);
9747         }
9748 
9749         // called by accessibility delegate
onInitializeAccessibilityEvent(AccessibilityEvent event)9750         public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
9751             onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
9752         }
9753 
9754         /**
9755          * Called by the accessibility delegate to initialize an accessibility event.
9756          * <p>
9757          * Default implementation adds item count and scroll information to the event.
9758          *
9759          * @param recycler The Recycler that can be used to convert view positions into adapter
9760          *                 positions
9761          * @param state    The current state of RecyclerView
9762          * @param event    The event instance to initialize
9763          * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
9764          */
onInitializeAccessibilityEvent(Recycler recycler, State state, AccessibilityEvent event)9765         public void onInitializeAccessibilityEvent(Recycler recycler, State state,
9766                 AccessibilityEvent event) {
9767             if (mRecyclerView == null || event == null) {
9768                 return;
9769             }
9770             event.setScrollable(mRecyclerView.canScrollVertically(1)
9771                     || mRecyclerView.canScrollVertically(-1)
9772                     || mRecyclerView.canScrollHorizontally(-1)
9773                     || mRecyclerView.canScrollHorizontally(1));
9774 
9775             if (mRecyclerView.mAdapter != null) {
9776                 event.setItemCount(mRecyclerView.mAdapter.getItemCount());
9777             }
9778         }
9779 
9780         // called by accessibility delegate
onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info)9781         void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) {
9782             final ViewHolder vh = getChildViewHolderInt(host);
9783             // avoid trying to create accessibility node info for removed children
9784             if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
9785                 onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
9786                         mRecyclerView.mState, host, info);
9787             }
9788         }
9789 
9790         /**
9791          * Called by the AccessibilityDelegate when the accessibility information for a specific
9792          * item should be populated.
9793          * <p>
9794          * Default implementation adds basic positioning information about the item.
9795          *
9796          * @param recycler The Recycler that can be used to convert view positions into adapter
9797          *                 positions
9798          * @param state    The current state of RecyclerView
9799          * @param host     The child for which accessibility node info should be populated
9800          * @param info     The info to fill out about the item
9801          * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
9802          * android.view.accessibility.AccessibilityNodeInfo)
9803          */
onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state, View host, AccessibilityNodeInfoCompat info)9804         public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
9805                 View host, AccessibilityNodeInfoCompat info) {
9806             int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
9807             int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
9808             final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo =
9809                     AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
9810                             columnIndexGuess, 1, false, false);
9811             info.setCollectionItemInfo(itemInfo);
9812         }
9813 
9814         /**
9815          * A LayoutManager can call this method to force RecyclerView to run simple animations in
9816          * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
9817          * change).
9818          * <p>
9819          * Note that, calling this method will not guarantee that RecyclerView will run animations
9820          * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
9821          * not run any animations but will still clear this flag after the layout is complete.
9822          *
9823          */
requestSimpleAnimationsInNextLayout()9824         public void requestSimpleAnimationsInNextLayout() {
9825             mRequestedSimpleAnimations = true;
9826         }
9827 
9828         /**
9829          * Returns the selection mode for accessibility. Should be
9830          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE},
9831          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or
9832          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}.
9833          * <p>
9834          * Default implementation returns
9835          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
9836          *
9837          * @param recycler The Recycler that can be used to convert view positions into adapter
9838          *                 positions
9839          * @param state    The current state of RecyclerView
9840          * @return Selection mode for accessibility. Default implementation returns
9841          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
9842          */
getSelectionModeForAccessibility(Recycler recycler, State state)9843         public int getSelectionModeForAccessibility(Recycler recycler, State state) {
9844             return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
9845         }
9846 
9847         /**
9848          * Returns the number of rows for accessibility.
9849          * <p>
9850          * Default implementation returns the number of items in the adapter if LayoutManager
9851          * supports vertical scrolling or 1 if LayoutManager does not support vertical
9852          * scrolling.
9853          *
9854          * @param recycler The Recycler that can be used to convert view positions into adapter
9855          *                 positions
9856          * @param state    The current state of RecyclerView
9857          * @return The number of rows in LayoutManager for accessibility.
9858          */
getRowCountForAccessibility(Recycler recycler, State state)9859         public int getRowCountForAccessibility(Recycler recycler, State state) {
9860             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
9861                 return 1;
9862             }
9863             return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
9864         }
9865 
9866         /**
9867          * Returns the number of columns for accessibility.
9868          * <p>
9869          * Default implementation returns the number of items in the adapter if LayoutManager
9870          * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
9871          * scrolling.
9872          *
9873          * @param recycler The Recycler that can be used to convert view positions into adapter
9874          *                 positions
9875          * @param state    The current state of RecyclerView
9876          * @return The number of rows in LayoutManager for accessibility.
9877          */
getColumnCountForAccessibility(Recycler recycler, State state)9878         public int getColumnCountForAccessibility(Recycler recycler, State state) {
9879             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
9880                 return 1;
9881             }
9882             return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
9883         }
9884 
9885         /**
9886          * Returns whether layout is hierarchical or not to be used for accessibility.
9887          * <p>
9888          * Default implementation returns false.
9889          *
9890          * @param recycler The Recycler that can be used to convert view positions into adapter
9891          *                 positions
9892          * @param state    The current state of RecyclerView
9893          * @return True if layout is hierarchical.
9894          */
isLayoutHierarchical(Recycler recycler, State state)9895         public boolean isLayoutHierarchical(Recycler recycler, State state) {
9896             return false;
9897         }
9898 
9899         // called by accessibility delegate
performAccessibilityAction(int action, Bundle args)9900         boolean performAccessibilityAction(int action, Bundle args) {
9901             return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
9902                     action, args);
9903         }
9904 
9905         /**
9906          * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
9907          *
9908          * @param recycler  The Recycler that can be used to convert view positions into adapter
9909          *                  positions
9910          * @param state     The current state of RecyclerView
9911          * @param action    The action to perform
9912          * @param args      Optional action arguments
9913          * @see View#performAccessibilityAction(int, android.os.Bundle)
9914          */
performAccessibilityAction(Recycler recycler, State state, int action, Bundle args)9915         public boolean performAccessibilityAction(Recycler recycler, State state, int action,
9916                 Bundle args) {
9917             if (mRecyclerView == null) {
9918                 return false;
9919             }
9920             int vScroll = 0, hScroll = 0;
9921             switch (action) {
9922                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
9923                     if (mRecyclerView.canScrollVertically(-1)) {
9924                         vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
9925                     }
9926                     if (mRecyclerView.canScrollHorizontally(-1)) {
9927                         hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
9928                     }
9929                     break;
9930                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
9931                     if (mRecyclerView.canScrollVertically(1)) {
9932                         vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
9933                     }
9934                     if (mRecyclerView.canScrollHorizontally(1)) {
9935                         hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
9936                     }
9937                     break;
9938             }
9939             if (vScroll == 0 && hScroll == 0) {
9940                 return false;
9941             }
9942             mRecyclerView.scrollBy(hScroll, vScroll);
9943             return true;
9944         }
9945 
9946         // called by accessibility delegate
performAccessibilityActionForItem(View view, int action, Bundle args)9947         boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
9948             return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
9949                     view, action, args);
9950         }
9951 
9952         /**
9953          * Called by AccessibilityDelegate when an accessibility action is requested on one of the
9954          * children of LayoutManager.
9955          * <p>
9956          * Default implementation does not do anything.
9957          *
9958          * @param recycler The Recycler that can be used to convert view positions into adapter
9959          *                 positions
9960          * @param state    The current state of RecyclerView
9961          * @param view     The child view on which the action is performed
9962          * @param action   The action to perform
9963          * @param args     Optional action arguments
9964          * @return true if action is handled
9965          * @see View#performAccessibilityAction(int, android.os.Bundle)
9966          */
performAccessibilityActionForItem(Recycler recycler, State state, View view, int action, Bundle args)9967         public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
9968                 int action, Bundle args) {
9969             return false;
9970         }
9971 
9972         /**
9973          * Parse the xml attributes to get the most common properties used by layout managers.
9974          *
9975          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
9976          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
9977          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
9978          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
9979          *
9980          * @return an object containing the properties as specified in the attrs.
9981          */
getProperties(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)9982         public static Properties getProperties(Context context, AttributeSet attrs,
9983                 int defStyleAttr, int defStyleRes) {
9984             Properties properties = new Properties();
9985             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
9986                     defStyleAttr, defStyleRes);
9987             properties.orientation = a.getInt(R.styleable.RecyclerView_android_orientation,
9988                     VERTICAL);
9989             properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
9990             properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
9991             properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
9992             a.recycle();
9993             return properties;
9994         }
9995 
setExactMeasureSpecsFrom(RecyclerView recyclerView)9996         void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
9997             setMeasureSpecs(
9998                     MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
9999                     MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
10000             );
10001         }
10002 
10003         /**
10004          * Internal API to allow LayoutManagers to be measured twice.
10005          * <p>
10006          * This is not public because LayoutManagers should be able to handle their layouts in one
10007          * pass but it is very convenient to make existing LayoutManagers support wrapping content
10008          * when both orientations are undefined.
10009          * <p>
10010          * This API will be removed after default LayoutManagers properly implement wrap content in
10011          * non-scroll orientation.
10012          */
shouldMeasureTwice()10013         boolean shouldMeasureTwice() {
10014             return false;
10015         }
10016 
hasFlexibleChildInBothOrientations()10017         boolean hasFlexibleChildInBothOrientations() {
10018             final int childCount = getChildCount();
10019             for (int i = 0; i < childCount; i++) {
10020                 final View child = getChildAt(i);
10021                 final ViewGroup.LayoutParams lp = child.getLayoutParams();
10022                 if (lp.width < 0 && lp.height < 0) {
10023                     return true;
10024                 }
10025             }
10026             return false;
10027         }
10028 
10029         /**
10030          * Some general properties that a LayoutManager may want to use.
10031          */
10032         public static class Properties {
10033             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation */
10034             public int orientation;
10035             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount */
10036             public int spanCount;
10037             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout */
10038             public boolean reverseLayout;
10039             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd */
10040             public boolean stackFromEnd;
10041         }
10042     }
10043 
10044     /**
10045      * An ItemDecoration allows the application to add a special drawing and layout offset
10046      * to specific item views from the adapter's data set. This can be useful for drawing dividers
10047      * between items, highlights, visual grouping boundaries and more.
10048      *
10049      * <p>All ItemDecorations are drawn in the order they were added, before the item
10050      * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
10051      * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
10052      * RecyclerView.State)}.</p>
10053      */
10054     public abstract static class ItemDecoration {
10055         /**
10056          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
10057          * Any content drawn by this method will be drawn before the item views are drawn,
10058          * and will thus appear underneath the views.
10059          *
10060          * @param c Canvas to draw into
10061          * @param parent RecyclerView this ItemDecoration is drawing into
10062          * @param state The current state of RecyclerView
10063          */
onDraw(Canvas c, RecyclerView parent, State state)10064         public void onDraw(Canvas c, RecyclerView parent, State state) {
10065             onDraw(c, parent);
10066         }
10067 
10068         /**
10069          * @deprecated
10070          * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
10071          */
10072         @Deprecated
onDraw(Canvas c, RecyclerView parent)10073         public void onDraw(Canvas c, RecyclerView parent) {
10074         }
10075 
10076         /**
10077          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
10078          * Any content drawn by this method will be drawn after the item views are drawn
10079          * and will thus appear over the views.
10080          *
10081          * @param c Canvas to draw into
10082          * @param parent RecyclerView this ItemDecoration is drawing into
10083          * @param state The current state of RecyclerView.
10084          */
onDrawOver(Canvas c, RecyclerView parent, State state)10085         public void onDrawOver(Canvas c, RecyclerView parent, State state) {
10086             onDrawOver(c, parent);
10087         }
10088 
10089         /**
10090          * @deprecated
10091          * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
10092          */
10093         @Deprecated
onDrawOver(Canvas c, RecyclerView parent)10094         public void onDrawOver(Canvas c, RecyclerView parent) {
10095         }
10096 
10097 
10098         /**
10099          * @deprecated
10100          * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
10101          */
10102         @Deprecated
getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)10103         public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
10104             outRect.set(0, 0, 0, 0);
10105         }
10106 
10107         /**
10108          * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
10109          * the number of pixels that the item view should be inset by, similar to padding or margin.
10110          * The default implementation sets the bounds of outRect to 0 and returns.
10111          *
10112          * <p>
10113          * If this ItemDecoration does not affect the positioning of item views, it should set
10114          * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
10115          * before returning.
10116          *
10117          * <p>
10118          * If you need to access Adapter for additional data, you can call
10119          * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
10120          * View.
10121          *
10122          * @param outRect Rect to receive the output.
10123          * @param view    The child view to decorate
10124          * @param parent  RecyclerView this ItemDecoration is decorating
10125          * @param state   The current state of RecyclerView.
10126          */
getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)10127         public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
10128             getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
10129                     parent);
10130         }
10131     }
10132 
10133     /**
10134      * An OnItemTouchListener allows the application to intercept touch events in progress at the
10135      * view hierarchy level of the RecyclerView before those touch events are considered for
10136      * RecyclerView's own scrolling behavior.
10137      *
10138      * <p>This can be useful for applications that wish to implement various forms of gestural
10139      * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
10140      * a touch interaction already in progress even if the RecyclerView is already handling that
10141      * gesture stream itself for the purposes of scrolling.</p>
10142      *
10143      * @see SimpleOnItemTouchListener
10144      */
10145     public interface OnItemTouchListener {
10146         /**
10147          * Silently observe and/or take over touch events sent to the RecyclerView
10148          * before they are handled by either the RecyclerView itself or its child views.
10149          *
10150          * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
10151          * in the order in which each listener was added, before any other touch processing
10152          * by the RecyclerView itself or child views occurs.</p>
10153          *
10154          * @param e MotionEvent describing the touch event. All coordinates are in
10155          *          the RecyclerView's coordinate system.
10156          * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
10157          *         to continue with the current behavior and continue observing future events in
10158          *         the gesture.
10159          */
onInterceptTouchEvent(RecyclerView rv, MotionEvent e)10160         boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
10161 
10162         /**
10163          * Process a touch event as part of a gesture that was claimed by returning true from
10164          * a previous call to {@link #onInterceptTouchEvent}.
10165          *
10166          * @param e MotionEvent describing the touch event. All coordinates are in
10167          *          the RecyclerView's coordinate system.
10168          */
onTouchEvent(RecyclerView rv, MotionEvent e)10169         void onTouchEvent(RecyclerView rv, MotionEvent e);
10170 
10171         /**
10172          * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
10173          * intercept touch events with
10174          * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
10175          *
10176          * @param disallowIntercept True if the child does not want the parent to
10177          *            intercept touch events.
10178          * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
10179          */
onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)10180         void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
10181     }
10182 
10183     /**
10184      * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies
10185      * and default return values.
10186      * <p>
10187      * You may prefer to extend this class if you don't need to override all methods. Another
10188      * benefit of using this class is future compatibility. As the interface may change, we'll
10189      * always provide a default implementation on this class so that your code won't break when
10190      * you update to a new version of the support library.
10191      */
10192     public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
10193         @Override
onInterceptTouchEvent(RecyclerView rv, MotionEvent e)10194         public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
10195             return false;
10196         }
10197 
10198         @Override
onTouchEvent(RecyclerView rv, MotionEvent e)10199         public void onTouchEvent(RecyclerView rv, MotionEvent e) {
10200         }
10201 
10202         @Override
onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)10203         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
10204         }
10205     }
10206 
10207 
10208     /**
10209      * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
10210      * has occurred on that RecyclerView.
10211      * <p>
10212      * @see RecyclerView#addOnScrollListener(OnScrollListener)
10213      * @see RecyclerView#clearOnChildAttachStateChangeListeners()
10214      *
10215      */
10216     public abstract static class OnScrollListener {
10217         /**
10218          * Callback method to be invoked when RecyclerView's scroll state changes.
10219          *
10220          * @param recyclerView The RecyclerView whose scroll state has changed.
10221          * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
10222          *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
10223          */
onScrollStateChanged(RecyclerView recyclerView, int newState)10224         public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
10225 
10226         /**
10227          * Callback method to be invoked when the RecyclerView has been scrolled. This will be
10228          * called after the scroll has completed.
10229          * <p>
10230          * This callback will also be called if visible item range changes after a layout
10231          * calculation. In that case, dx and dy will be 0.
10232          *
10233          * @param recyclerView The RecyclerView which scrolled.
10234          * @param dx The amount of horizontal scroll.
10235          * @param dy The amount of vertical scroll.
10236          */
onScrolled(RecyclerView recyclerView, int dx, int dy)10237         public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
10238     }
10239 
10240     /**
10241      * A RecyclerListener can be set on a RecyclerView to receive messages whenever
10242      * a view is recycled.
10243      *
10244      * @see RecyclerView#setRecyclerListener(RecyclerListener)
10245      */
10246     public interface RecyclerListener {
10247 
10248         /**
10249          * This method is called whenever the view in the ViewHolder is recycled.
10250          *
10251          * RecyclerView calls this method right before clearing ViewHolder's internal data and
10252          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
10253          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
10254          * its adapter position.
10255          *
10256          * @param holder The ViewHolder containing the view that was recycled
10257          */
onViewRecycled(ViewHolder holder)10258         void onViewRecycled(ViewHolder holder);
10259     }
10260 
10261     /**
10262      * A Listener interface that can be attached to a RecylcerView to get notified
10263      * whenever a ViewHolder is attached to or detached from RecyclerView.
10264      */
10265     public interface OnChildAttachStateChangeListener {
10266 
10267         /**
10268          * Called when a view is attached to the RecyclerView.
10269          *
10270          * @param view The View which is attached to the RecyclerView
10271          */
onChildViewAttachedToWindow(View view)10272         void onChildViewAttachedToWindow(View view);
10273 
10274         /**
10275          * Called when a view is detached from RecyclerView.
10276          *
10277          * @param view The View which is being detached from the RecyclerView
10278          */
onChildViewDetachedFromWindow(View view)10279         void onChildViewDetachedFromWindow(View view);
10280     }
10281 
10282     /**
10283      * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
10284      *
10285      * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
10286      * potentially expensive {@link View#findViewById(int)} results.</p>
10287      *
10288      * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
10289      * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
10290      * their own custom ViewHolder implementations to store data that makes binding view contents
10291      * easier. Implementations should assume that individual item views will hold strong references
10292      * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
10293      * strong references to extra off-screen item views for caching purposes</p>
10294      */
10295     public abstract static class ViewHolder {
10296         public final View itemView;
10297         WeakReference<RecyclerView> mNestedRecyclerView;
10298         int mPosition = NO_POSITION;
10299         int mOldPosition = NO_POSITION;
10300         long mItemId = NO_ID;
10301         int mItemViewType = INVALID_TYPE;
10302         int mPreLayoutPosition = NO_POSITION;
10303 
10304         // The item that this holder is shadowing during an item change event/animation
10305         ViewHolder mShadowedHolder = null;
10306         // The item that is shadowing this holder during an item change event/animation
10307         ViewHolder mShadowingHolder = null;
10308 
10309         /**
10310          * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
10311          * are all valid.
10312          */
10313         static final int FLAG_BOUND = 1 << 0;
10314 
10315         /**
10316          * The data this ViewHolder's view reflects is stale and needs to be rebound
10317          * by the adapter. mPosition and mItemId are consistent.
10318          */
10319         static final int FLAG_UPDATE = 1 << 1;
10320 
10321         /**
10322          * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
10323          * are not to be trusted and may no longer match the item view type.
10324          * This ViewHolder must be fully rebound to different data.
10325          */
10326         static final int FLAG_INVALID = 1 << 2;
10327 
10328         /**
10329          * This ViewHolder points at data that represents an item previously removed from the
10330          * data set. Its view may still be used for things like outgoing animations.
10331          */
10332         static final int FLAG_REMOVED = 1 << 3;
10333 
10334         /**
10335          * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
10336          * and is intended to keep views around during animations.
10337          */
10338         static final int FLAG_NOT_RECYCLABLE = 1 << 4;
10339 
10340         /**
10341          * This ViewHolder is returned from scrap which means we are expecting an addView call
10342          * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
10343          * the end of the layout pass and then recycled by RecyclerView if it is not added back to
10344          * the RecyclerView.
10345          */
10346         static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
10347 
10348         /**
10349          * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
10350          * it unless LayoutManager is replaced.
10351          * It is still fully visible to the LayoutManager.
10352          */
10353         static final int FLAG_IGNORE = 1 << 7;
10354 
10355         /**
10356          * When the View is detached form the parent, we set this flag so that we can take correct
10357          * action when we need to remove it or add it back.
10358          */
10359         static final int FLAG_TMP_DETACHED = 1 << 8;
10360 
10361         /**
10362          * Set when we can no longer determine the adapter position of this ViewHolder until it is
10363          * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
10364          * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
10365          * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
10366          * re-calculated.
10367          */
10368         static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
10369 
10370         /**
10371          * Set when a addChangePayload(null) is called
10372          */
10373         static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
10374 
10375         /**
10376          * Used by ItemAnimator when a ViewHolder's position changes
10377          */
10378         static final int FLAG_MOVED = 1 << 11;
10379 
10380         /**
10381          * Used by ItemAnimator when a ViewHolder appears in pre-layout
10382          */
10383         static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
10384 
10385         static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
10386 
10387         /**
10388          * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
10389          * hidden list (as if it was scrap) without being recycled in between.
10390          *
10391          * When a ViewHolder is hidden, there are 2 paths it can be re-used:
10392          *   a) Animation ends, view is recycled and used from the recycle pool.
10393          *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
10394          *
10395          * This flag is used to represent "case b" where the ViewHolder is reused without being
10396          * recycled (thus "bounced" from the hidden list). This state requires special handling
10397          * because the ViewHolder must be added to pre layout maps for animations as if it was
10398          * already there.
10399          */
10400         static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
10401 
10402         /**
10403          * Flags that RecyclerView assigned {@link RecyclerViewAccessibilityDelegate
10404          * #getItemDelegate()} in onBindView when app does not provide a delegate.
10405          */
10406         static final int FLAG_SET_A11Y_ITEM_DELEGATE = 1 << 14;
10407 
10408         private int mFlags;
10409 
10410         private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
10411 
10412         List<Object> mPayloads = null;
10413         List<Object> mUnmodifiedPayloads = null;
10414 
10415         private int mIsRecyclableCount = 0;
10416 
10417         // If non-null, view is currently considered scrap and may be reused for other data by the
10418         // scrap container.
10419         private Recycler mScrapContainer = null;
10420         // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
10421         private boolean mInChangeScrap = false;
10422 
10423         // Saves isImportantForAccessibility value for the view item while it's in hidden state and
10424         // marked as unimportant for accessibility.
10425         private int mWasImportantForAccessibilityBeforeHidden =
10426                 ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10427         // set if we defer the accessibility state change of the view holder
10428         @VisibleForTesting
10429         int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
10430 
10431         /**
10432          * Is set when VH is bound from the adapter and cleaned right before it is sent to
10433          * {@link RecycledViewPool}.
10434          */
10435         RecyclerView mOwnerRecyclerView;
10436 
ViewHolder(View itemView)10437         public ViewHolder(View itemView) {
10438             if (itemView == null) {
10439                 throw new IllegalArgumentException("itemView may not be null");
10440             }
10441             this.itemView = itemView;
10442         }
10443 
flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout)10444         void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
10445             addFlags(ViewHolder.FLAG_REMOVED);
10446             offsetPosition(offset, applyToPreLayout);
10447             mPosition = mNewPosition;
10448         }
10449 
offsetPosition(int offset, boolean applyToPreLayout)10450         void offsetPosition(int offset, boolean applyToPreLayout) {
10451             if (mOldPosition == NO_POSITION) {
10452                 mOldPosition = mPosition;
10453             }
10454             if (mPreLayoutPosition == NO_POSITION) {
10455                 mPreLayoutPosition = mPosition;
10456             }
10457             if (applyToPreLayout) {
10458                 mPreLayoutPosition += offset;
10459             }
10460             mPosition += offset;
10461             if (itemView.getLayoutParams() != null) {
10462                 ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
10463             }
10464         }
10465 
clearOldPosition()10466         void clearOldPosition() {
10467             mOldPosition = NO_POSITION;
10468             mPreLayoutPosition = NO_POSITION;
10469         }
10470 
saveOldPosition()10471         void saveOldPosition() {
10472             if (mOldPosition == NO_POSITION) {
10473                 mOldPosition = mPosition;
10474             }
10475         }
10476 
shouldIgnore()10477         boolean shouldIgnore() {
10478             return (mFlags & FLAG_IGNORE) != 0;
10479         }
10480 
10481         /**
10482          * @deprecated This method is deprecated because its meaning is ambiguous due to the async
10483          * handling of adapter updates. Please use {@link #getLayoutPosition()} or
10484          * {@link #getAdapterPosition()} depending on your use case.
10485          *
10486          * @see #getLayoutPosition()
10487          * @see #getAdapterPosition()
10488          */
10489         @Deprecated
getPosition()10490         public final int getPosition() {
10491             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10492         }
10493 
10494         /**
10495          * Returns the position of the ViewHolder in terms of the latest layout pass.
10496          * <p>
10497          * This position is mostly used by RecyclerView components to be consistent while
10498          * RecyclerView lazily processes adapter updates.
10499          * <p>
10500          * For performance and animation reasons, RecyclerView batches all adapter updates until the
10501          * next layout pass. This may cause mismatches between the Adapter position of the item and
10502          * the position it had in the latest layout calculations.
10503          * <p>
10504          * LayoutManagers should always call this method while doing calculations based on item
10505          * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
10506          * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
10507          * of the item.
10508          * <p>
10509          * If LayoutManager needs to call an external method that requires the adapter position of
10510          * the item, it can use {@link #getAdapterPosition()} or
10511          * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
10512          *
10513          * @return Returns the adapter position of the ViewHolder in the latest layout pass.
10514          * @see #getAdapterPosition()
10515          */
getLayoutPosition()10516         public final int getLayoutPosition() {
10517             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10518         }
10519 
10520         /**
10521          * Returns the Adapter position of the item represented by this ViewHolder.
10522          * <p>
10523          * Note that this might be different than the {@link #getLayoutPosition()} if there are
10524          * pending adapter updates but a new layout pass has not happened yet.
10525          * <p>
10526          * RecyclerView does not handle any adapter updates until the next layout traversal. This
10527          * may create temporary inconsistencies between what user sees on the screen and what
10528          * adapter contents have. This inconsistency is not important since it will be less than
10529          * 16ms but it might be a problem if you want to use ViewHolder position to access the
10530          * adapter. Sometimes, you may need to get the exact adapter position to do
10531          * some actions in response to user events. In that case, you should use this method which
10532          * will calculate the Adapter position of the ViewHolder.
10533          * <p>
10534          * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
10535          * next layout pass, the return value of this method will be {@link #NO_POSITION}.
10536          *
10537          * @return The adapter position of the item if it still exists in the adapter.
10538          * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
10539          * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
10540          * layout pass or the ViewHolder has already been recycled.
10541          */
getAdapterPosition()10542         public final int getAdapterPosition() {
10543             if (mOwnerRecyclerView == null) {
10544                 return NO_POSITION;
10545             }
10546             return mOwnerRecyclerView.getAdapterPositionFor(this);
10547         }
10548 
10549         /**
10550          * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
10551          * to perform animations.
10552          * <p>
10553          * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
10554          * adapter index in the previous layout.
10555          *
10556          * @return The previous adapter index of the Item represented by this ViewHolder or
10557          * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
10558          * complete).
10559          */
getOldPosition()10560         public final int getOldPosition() {
10561             return mOldPosition;
10562         }
10563 
10564         /**
10565          * Returns The itemId represented by this ViewHolder.
10566          *
10567          * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
10568          * otherwise
10569          */
getItemId()10570         public final long getItemId() {
10571             return mItemId;
10572         }
10573 
10574         /**
10575          * @return The view type of this ViewHolder.
10576          */
getItemViewType()10577         public final int getItemViewType() {
10578             return mItemViewType;
10579         }
10580 
isScrap()10581         boolean isScrap() {
10582             return mScrapContainer != null;
10583         }
10584 
unScrap()10585         void unScrap() {
10586             mScrapContainer.unscrapView(this);
10587         }
10588 
wasReturnedFromScrap()10589         boolean wasReturnedFromScrap() {
10590             return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
10591         }
10592 
clearReturnedFromScrapFlag()10593         void clearReturnedFromScrapFlag() {
10594             mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
10595         }
10596 
clearTmpDetachFlag()10597         void clearTmpDetachFlag() {
10598             mFlags = mFlags & ~FLAG_TMP_DETACHED;
10599         }
10600 
stopIgnoring()10601         void stopIgnoring() {
10602             mFlags = mFlags & ~FLAG_IGNORE;
10603         }
10604 
setScrapContainer(Recycler recycler, boolean isChangeScrap)10605         void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
10606             mScrapContainer = recycler;
10607             mInChangeScrap = isChangeScrap;
10608         }
10609 
isInvalid()10610         boolean isInvalid() {
10611             return (mFlags & FLAG_INVALID) != 0;
10612         }
10613 
needsUpdate()10614         boolean needsUpdate() {
10615             return (mFlags & FLAG_UPDATE) != 0;
10616         }
10617 
isBound()10618         boolean isBound() {
10619             return (mFlags & FLAG_BOUND) != 0;
10620         }
10621 
isRemoved()10622         boolean isRemoved() {
10623             return (mFlags & FLAG_REMOVED) != 0;
10624         }
10625 
hasAnyOfTheFlags(int flags)10626         boolean hasAnyOfTheFlags(int flags) {
10627             return (mFlags & flags) != 0;
10628         }
10629 
isTmpDetached()10630         boolean isTmpDetached() {
10631             return (mFlags & FLAG_TMP_DETACHED) != 0;
10632         }
10633 
isAdapterPositionUnknown()10634         boolean isAdapterPositionUnknown() {
10635             return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
10636         }
10637 
setFlags(int flags, int mask)10638         void setFlags(int flags, int mask) {
10639             mFlags = (mFlags & ~mask) | (flags & mask);
10640         }
10641 
addFlags(int flags)10642         void addFlags(int flags) {
10643             mFlags |= flags;
10644         }
10645 
addChangePayload(Object payload)10646         void addChangePayload(Object payload) {
10647             if (payload == null) {
10648                 addFlags(FLAG_ADAPTER_FULLUPDATE);
10649             } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10650                 createPayloadsIfNeeded();
10651                 mPayloads.add(payload);
10652             }
10653         }
10654 
createPayloadsIfNeeded()10655         private void createPayloadsIfNeeded() {
10656             if (mPayloads == null) {
10657                 mPayloads = new ArrayList<Object>();
10658                 mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
10659             }
10660         }
10661 
clearPayload()10662         void clearPayload() {
10663             if (mPayloads != null) {
10664                 mPayloads.clear();
10665             }
10666             mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
10667         }
10668 
getUnmodifiedPayloads()10669         List<Object> getUnmodifiedPayloads() {
10670             if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10671                 if (mPayloads == null || mPayloads.size() == 0) {
10672                     // Initial state,  no update being called.
10673                     return FULLUPDATE_PAYLOADS;
10674                 }
10675                 // there are none-null payloads
10676                 return mUnmodifiedPayloads;
10677             } else {
10678                 // a full update has been called.
10679                 return FULLUPDATE_PAYLOADS;
10680             }
10681         }
10682 
resetInternal()10683         void resetInternal() {
10684             mFlags = 0;
10685             mPosition = NO_POSITION;
10686             mOldPosition = NO_POSITION;
10687             mItemId = NO_ID;
10688             mPreLayoutPosition = NO_POSITION;
10689             mIsRecyclableCount = 0;
10690             mShadowedHolder = null;
10691             mShadowingHolder = null;
10692             clearPayload();
10693             mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10694             mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
10695             clearNestedRecyclerViewIfNotNested(this);
10696         }
10697 
10698         /**
10699          * Called when the child view enters the hidden state
10700          */
onEnteredHiddenState(RecyclerView parent)10701         private void onEnteredHiddenState(RecyclerView parent) {
10702             // While the view item is in hidden state, make it invisible for the accessibility.
10703             mWasImportantForAccessibilityBeforeHidden =
10704                     ViewCompat.getImportantForAccessibility(itemView);
10705             parent.setChildImportantForAccessibilityInternal(this,
10706                     ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
10707         }
10708 
10709         /**
10710          * Called when the child view leaves the hidden state
10711          */
onLeftHiddenState(RecyclerView parent)10712         private void onLeftHiddenState(RecyclerView parent) {
10713             parent.setChildImportantForAccessibilityInternal(this,
10714                     mWasImportantForAccessibilityBeforeHidden);
10715             mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10716         }
10717 
10718         @Override
toString()10719         public String toString() {
10720             final StringBuilder sb = new StringBuilder("ViewHolder{"
10721                     + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
10722                     + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
10723             if (isScrap()) {
10724                 sb.append(" scrap ")
10725                         .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
10726             }
10727             if (isInvalid()) sb.append(" invalid");
10728             if (!isBound()) sb.append(" unbound");
10729             if (needsUpdate()) sb.append(" update");
10730             if (isRemoved()) sb.append(" removed");
10731             if (shouldIgnore()) sb.append(" ignored");
10732             if (isTmpDetached()) sb.append(" tmpDetached");
10733             if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
10734             if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
10735 
10736             if (itemView.getParent() == null) sb.append(" no parent");
10737             sb.append("}");
10738             return sb.toString();
10739         }
10740 
10741         /**
10742          * Informs the recycler whether this item can be recycled. Views which are not
10743          * recyclable will not be reused for other items until setIsRecyclable() is
10744          * later set to true. Calls to setIsRecyclable() should always be paired (one
10745          * call to setIsRecyclabe(false) should always be matched with a later call to
10746          * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
10747          * reference-counted.
10748          *
10749          * @param recyclable Whether this item is available to be recycled. Default value
10750          * is true.
10751          *
10752          * @see #isRecyclable()
10753          */
setIsRecyclable(boolean recyclable)10754         public final void setIsRecyclable(boolean recyclable) {
10755             mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
10756             if (mIsRecyclableCount < 0) {
10757                 mIsRecyclableCount = 0;
10758                 if (DEBUG) {
10759                     throw new RuntimeException("isRecyclable decremented below 0: "
10760                             + "unmatched pair of setIsRecyable() calls for " + this);
10761                 }
10762                 Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: "
10763                         + "unmatched pair of setIsRecyable() calls for " + this);
10764             } else if (!recyclable && mIsRecyclableCount == 1) {
10765                 mFlags |= FLAG_NOT_RECYCLABLE;
10766             } else if (recyclable && mIsRecyclableCount == 0) {
10767                 mFlags &= ~FLAG_NOT_RECYCLABLE;
10768             }
10769             if (DEBUG) {
10770                 Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
10771             }
10772         }
10773 
10774         /**
10775          * @return true if this item is available to be recycled, false otherwise.
10776          *
10777          * @see #setIsRecyclable(boolean)
10778          */
isRecyclable()10779         public final boolean isRecyclable() {
10780             return (mFlags & FLAG_NOT_RECYCLABLE) == 0
10781                     && !ViewCompat.hasTransientState(itemView);
10782         }
10783 
10784         /**
10785          * Returns whether we have animations referring to this view holder or not.
10786          * This is similar to isRecyclable flag but does not check transient state.
10787          */
shouldBeKeptAsChild()10788         private boolean shouldBeKeptAsChild() {
10789             return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
10790         }
10791 
10792         /**
10793          * @return True if ViewHolder is not referenced by RecyclerView animations but has
10794          * transient state which will prevent it from being recycled.
10795          */
doesTransientStatePreventRecycling()10796         private boolean doesTransientStatePreventRecycling() {
10797             return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView);
10798         }
10799 
isUpdated()10800         boolean isUpdated() {
10801             return (mFlags & FLAG_UPDATE) != 0;
10802         }
10803     }
10804 
10805     /**
10806      * This method is here so that we can control the important for a11y changes and test it.
10807      */
10808     @VisibleForTesting
setChildImportantForAccessibilityInternal(ViewHolder viewHolder, int importantForAccessibility)10809     boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
10810             int importantForAccessibility) {
10811         if (isComputingLayout()) {
10812             viewHolder.mPendingAccessibilityState = importantForAccessibility;
10813             mPendingAccessibilityImportanceChange.add(viewHolder);
10814             return false;
10815         }
10816         ViewCompat.setImportantForAccessibility(viewHolder.itemView, importantForAccessibility);
10817         return true;
10818     }
10819 
dispatchPendingImportantForAccessibilityChanges()10820     void dispatchPendingImportantForAccessibilityChanges() {
10821         for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
10822             ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
10823             if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
10824                 continue;
10825             }
10826             int state = viewHolder.mPendingAccessibilityState;
10827             if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
10828                 //noinspection WrongConstant
10829                 ViewCompat.setImportantForAccessibility(viewHolder.itemView, state);
10830                 viewHolder.mPendingAccessibilityState =
10831                         ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
10832             }
10833         }
10834         mPendingAccessibilityImportanceChange.clear();
10835     }
10836 
getAdapterPositionFor(ViewHolder viewHolder)10837     int getAdapterPositionFor(ViewHolder viewHolder) {
10838         if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
10839                 | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
10840                 || !viewHolder.isBound()) {
10841             return RecyclerView.NO_POSITION;
10842         }
10843         return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
10844     }
10845 
10846     @VisibleForTesting
initFastScroller(StateListDrawable verticalThumbDrawable, Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable, Drawable horizontalTrackDrawable)10847     void initFastScroller(StateListDrawable verticalThumbDrawable,
10848             Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable,
10849             Drawable horizontalTrackDrawable) {
10850         if (verticalThumbDrawable == null || verticalTrackDrawable == null
10851                 || horizontalThumbDrawable == null || horizontalTrackDrawable == null) {
10852             throw new IllegalArgumentException(
10853                 "Trying to set fast scroller without both required drawables." + exceptionLabel());
10854         }
10855 
10856         Resources resources = getContext().getResources();
10857         new FastScroller(this, verticalThumbDrawable, verticalTrackDrawable,
10858                 horizontalThumbDrawable, horizontalTrackDrawable,
10859                 resources.getDimensionPixelSize(R.dimen.fastscroll_default_thickness),
10860                 resources.getDimensionPixelSize(R.dimen.fastscroll_minimum_range),
10861                 resources.getDimensionPixelOffset(R.dimen.fastscroll_margin));
10862     }
10863 
10864     // NestedScrollingChild
10865 
10866     @Override
setNestedScrollingEnabled(boolean enabled)10867     public void setNestedScrollingEnabled(boolean enabled) {
10868         getScrollingChildHelper().setNestedScrollingEnabled(enabled);
10869     }
10870 
10871     @Override
isNestedScrollingEnabled()10872     public boolean isNestedScrollingEnabled() {
10873         return getScrollingChildHelper().isNestedScrollingEnabled();
10874     }
10875 
10876     @Override
startNestedScroll(int axes)10877     public boolean startNestedScroll(int axes) {
10878         return getScrollingChildHelper().startNestedScroll(axes);
10879     }
10880 
10881     @Override
startNestedScroll(int axes, int type)10882     public boolean startNestedScroll(int axes, int type) {
10883         return getScrollingChildHelper().startNestedScroll(axes, type);
10884     }
10885 
10886     @Override
stopNestedScroll()10887     public void stopNestedScroll() {
10888         getScrollingChildHelper().stopNestedScroll();
10889     }
10890 
10891     @Override
stopNestedScroll(int type)10892     public void stopNestedScroll(int type) {
10893         getScrollingChildHelper().stopNestedScroll(type);
10894     }
10895 
10896     @Override
hasNestedScrollingParent()10897     public boolean hasNestedScrollingParent() {
10898         return getScrollingChildHelper().hasNestedScrollingParent();
10899     }
10900 
10901     @Override
hasNestedScrollingParent(int type)10902     public boolean hasNestedScrollingParent(int type) {
10903         return getScrollingChildHelper().hasNestedScrollingParent(type);
10904     }
10905 
10906     @Override
dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow)10907     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
10908             int dyUnconsumed, int[] offsetInWindow) {
10909         return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
10910                 dxUnconsumed, dyUnconsumed, offsetInWindow);
10911     }
10912 
10913     @Override
dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow, int type)10914     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
10915             int dyUnconsumed, int[] offsetInWindow, int type) {
10916         return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
10917                 dxUnconsumed, dyUnconsumed, offsetInWindow, type);
10918     }
10919 
10920     @Override
dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow)10921     public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
10922         return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
10923     }
10924 
10925     @Override
dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow, int type)10926     public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
10927             int type) {
10928         return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow,
10929                 type);
10930     }
10931 
10932     @Override
dispatchNestedFling(float velocityX, float velocityY, boolean consumed)10933     public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
10934         return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
10935     }
10936 
10937     @Override
dispatchNestedPreFling(float velocityX, float velocityY)10938     public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
10939         return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
10940     }
10941 
10942     /**
10943      * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
10944      * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
10945      * to create their own subclass of this <code>LayoutParams</code> class
10946      * to store any additional required per-child view metadata about the layout.
10947      */
10948     public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
10949         ViewHolder mViewHolder;
10950         final Rect mDecorInsets = new Rect();
10951         boolean mInsetsDirty = true;
10952         // Flag is set to true if the view is bound while it is detached from RV.
10953         // In this case, we need to manually call invalidate after view is added to guarantee that
10954         // invalidation is populated through the View hierarchy
10955         boolean mPendingInvalidate = false;
10956 
LayoutParams(Context c, AttributeSet attrs)10957         public LayoutParams(Context c, AttributeSet attrs) {
10958             super(c, attrs);
10959         }
10960 
LayoutParams(int width, int height)10961         public LayoutParams(int width, int height) {
10962             super(width, height);
10963         }
10964 
LayoutParams(MarginLayoutParams source)10965         public LayoutParams(MarginLayoutParams source) {
10966             super(source);
10967         }
10968 
LayoutParams(ViewGroup.LayoutParams source)10969         public LayoutParams(ViewGroup.LayoutParams source) {
10970             super(source);
10971         }
10972 
LayoutParams(LayoutParams source)10973         public LayoutParams(LayoutParams source) {
10974             super((ViewGroup.LayoutParams) source);
10975         }
10976 
10977         /**
10978          * Returns true if the view this LayoutParams is attached to needs to have its content
10979          * updated from the corresponding adapter.
10980          *
10981          * @return true if the view should have its content updated
10982          */
viewNeedsUpdate()10983         public boolean viewNeedsUpdate() {
10984             return mViewHolder.needsUpdate();
10985         }
10986 
10987         /**
10988          * Returns true if the view this LayoutParams is attached to is now representing
10989          * potentially invalid data. A LayoutManager should scrap/recycle it.
10990          *
10991          * @return true if the view is invalid
10992          */
isViewInvalid()10993         public boolean isViewInvalid() {
10994             return mViewHolder.isInvalid();
10995         }
10996 
10997         /**
10998          * Returns true if the adapter data item corresponding to the view this LayoutParams
10999          * is attached to has been removed from the data set. A LayoutManager may choose to
11000          * treat it differently in order to animate its outgoing or disappearing state.
11001          *
11002          * @return true if the item the view corresponds to was removed from the data set
11003          */
isItemRemoved()11004         public boolean isItemRemoved() {
11005             return mViewHolder.isRemoved();
11006         }
11007 
11008         /**
11009          * Returns true if the adapter data item corresponding to the view this LayoutParams
11010          * is attached to has been changed in the data set. A LayoutManager may choose to
11011          * treat it differently in order to animate its changing state.
11012          *
11013          * @return true if the item the view corresponds to was changed in the data set
11014          */
isItemChanged()11015         public boolean isItemChanged() {
11016             return mViewHolder.isUpdated();
11017         }
11018 
11019         /**
11020          * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
11021          */
11022         @Deprecated
getViewPosition()11023         public int getViewPosition() {
11024             return mViewHolder.getPosition();
11025         }
11026 
11027         /**
11028          * Returns the adapter position that the view this LayoutParams is attached to corresponds
11029          * to as of latest layout calculation.
11030          *
11031          * @return the adapter position this view as of latest layout pass
11032          */
getViewLayoutPosition()11033         public int getViewLayoutPosition() {
11034             return mViewHolder.getLayoutPosition();
11035         }
11036 
11037         /**
11038          * Returns the up-to-date adapter position that the view this LayoutParams is attached to
11039          * corresponds to.
11040          *
11041          * @return the up-to-date adapter position this view. It may return
11042          * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
11043          * its up-to-date position cannot be calculated.
11044          */
getViewAdapterPosition()11045         public int getViewAdapterPosition() {
11046             return mViewHolder.getAdapterPosition();
11047         }
11048     }
11049 
11050     /**
11051      * Observer base class for watching changes to an {@link Adapter}.
11052      * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
11053      */
11054     public abstract static class AdapterDataObserver {
onChanged()11055         public void onChanged() {
11056             // Do nothing
11057         }
11058 
onItemRangeChanged(int positionStart, int itemCount)11059         public void onItemRangeChanged(int positionStart, int itemCount) {
11060             // do nothing
11061         }
11062 
onItemRangeChanged(int positionStart, int itemCount, Object payload)11063         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
11064             // fallback to onItemRangeChanged(positionStart, itemCount) if app
11065             // does not override this method.
11066             onItemRangeChanged(positionStart, itemCount);
11067         }
11068 
onItemRangeInserted(int positionStart, int itemCount)11069         public void onItemRangeInserted(int positionStart, int itemCount) {
11070             // do nothing
11071         }
11072 
onItemRangeRemoved(int positionStart, int itemCount)11073         public void onItemRangeRemoved(int positionStart, int itemCount) {
11074             // do nothing
11075         }
11076 
onItemRangeMoved(int fromPosition, int toPosition, int itemCount)11077         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
11078             // do nothing
11079         }
11080     }
11081 
11082     /**
11083      * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
11084      * provides methods to trigger a programmatic scroll.</p>
11085      *
11086      * @see LinearSmoothScroller
11087      */
11088     public abstract static class SmoothScroller {
11089 
11090         private int mTargetPosition = RecyclerView.NO_POSITION;
11091 
11092         private RecyclerView mRecyclerView;
11093 
11094         private LayoutManager mLayoutManager;
11095 
11096         private boolean mPendingInitialRun;
11097 
11098         private boolean mRunning;
11099 
11100         private View mTargetView;
11101 
11102         private final Action mRecyclingAction;
11103 
SmoothScroller()11104         public SmoothScroller() {
11105             mRecyclingAction = new Action(0, 0);
11106         }
11107 
11108         /**
11109          * Starts a smooth scroll for the given target position.
11110          * <p>In each animation step, {@link RecyclerView} will check
11111          * for the target view and call either
11112          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
11113          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
11114          * SmoothScroller is stopped.</p>
11115          *
11116          * <p>Note that if RecyclerView finds the target view, it will automatically stop the
11117          * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
11118          * stop calling SmoothScroller in each animation step.</p>
11119          */
start(RecyclerView recyclerView, LayoutManager layoutManager)11120         void start(RecyclerView recyclerView, LayoutManager layoutManager) {
11121             mRecyclerView = recyclerView;
11122             mLayoutManager = layoutManager;
11123             if (mTargetPosition == RecyclerView.NO_POSITION) {
11124                 throw new IllegalArgumentException("Invalid target position");
11125             }
11126             mRecyclerView.mState.mTargetPosition = mTargetPosition;
11127             mRunning = true;
11128             mPendingInitialRun = true;
11129             mTargetView = findViewByPosition(getTargetPosition());
11130             onStart();
11131             mRecyclerView.mViewFlinger.postOnAnimation();
11132         }
11133 
setTargetPosition(int targetPosition)11134         public void setTargetPosition(int targetPosition) {
11135             mTargetPosition = targetPosition;
11136         }
11137 
11138         /**
11139          * @return The LayoutManager to which this SmoothScroller is attached. Will return
11140          * <code>null</code> after the SmoothScroller is stopped.
11141          */
11142         @Nullable
getLayoutManager()11143         public LayoutManager getLayoutManager() {
11144             return mLayoutManager;
11145         }
11146 
11147         /**
11148          * Stops running the SmoothScroller in each animation callback. Note that this does not
11149          * cancel any existing {@link Action} updated by
11150          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
11151          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
11152          */
stop()11153         protected final void stop() {
11154             if (!mRunning) {
11155                 return;
11156             }
11157             onStop();
11158             mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
11159             mTargetView = null;
11160             mTargetPosition = RecyclerView.NO_POSITION;
11161             mPendingInitialRun = false;
11162             mRunning = false;
11163             // trigger a cleanup
11164             mLayoutManager.onSmoothScrollerStopped(this);
11165             // clear references to avoid any potential leak by a custom smooth scroller
11166             mLayoutManager = null;
11167             mRecyclerView = null;
11168         }
11169 
11170         /**
11171          * Returns true if SmoothScroller has been started but has not received the first
11172          * animation
11173          * callback yet.
11174          *
11175          * @return True if this SmoothScroller is waiting to start
11176          */
isPendingInitialRun()11177         public boolean isPendingInitialRun() {
11178             return mPendingInitialRun;
11179         }
11180 
11181 
11182         /**
11183          * @return True if SmoothScroller is currently active
11184          */
isRunning()11185         public boolean isRunning() {
11186             return mRunning;
11187         }
11188 
11189         /**
11190          * Returns the adapter position of the target item
11191          *
11192          * @return Adapter position of the target item or
11193          * {@link RecyclerView#NO_POSITION} if no target view is set.
11194          */
getTargetPosition()11195         public int getTargetPosition() {
11196             return mTargetPosition;
11197         }
11198 
onAnimation(int dx, int dy)11199         private void onAnimation(int dx, int dy) {
11200             final RecyclerView recyclerView = mRecyclerView;
11201             if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
11202                 stop();
11203             }
11204             mPendingInitialRun = false;
11205             if (mTargetView != null) {
11206                 // verify target position
11207                 if (getChildPosition(mTargetView) == mTargetPosition) {
11208                     onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
11209                     mRecyclingAction.runIfNecessary(recyclerView);
11210                     stop();
11211                 } else {
11212                     Log.e(TAG, "Passed over target position while smooth scrolling.");
11213                     mTargetView = null;
11214                 }
11215             }
11216             if (mRunning) {
11217                 onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
11218                 boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
11219                 mRecyclingAction.runIfNecessary(recyclerView);
11220                 if (hadJumpTarget) {
11221                     // It is not stopped so needs to be restarted
11222                     if (mRunning) {
11223                         mPendingInitialRun = true;
11224                         recyclerView.mViewFlinger.postOnAnimation();
11225                     } else {
11226                         stop(); // done
11227                     }
11228                 }
11229             }
11230         }
11231 
11232         /**
11233          * @see RecyclerView#getChildLayoutPosition(android.view.View)
11234          */
getChildPosition(View view)11235         public int getChildPosition(View view) {
11236             return mRecyclerView.getChildLayoutPosition(view);
11237         }
11238 
11239         /**
11240          * @see RecyclerView.LayoutManager#getChildCount()
11241          */
getChildCount()11242         public int getChildCount() {
11243             return mRecyclerView.mLayout.getChildCount();
11244         }
11245 
11246         /**
11247          * @see RecyclerView.LayoutManager#findViewByPosition(int)
11248          */
findViewByPosition(int position)11249         public View findViewByPosition(int position) {
11250             return mRecyclerView.mLayout.findViewByPosition(position);
11251         }
11252 
11253         /**
11254          * @see RecyclerView#scrollToPosition(int)
11255          * @deprecated Use {@link Action#jumpTo(int)}.
11256          */
11257         @Deprecated
instantScrollToPosition(int position)11258         public void instantScrollToPosition(int position) {
11259             mRecyclerView.scrollToPosition(position);
11260         }
11261 
onChildAttachedToWindow(View child)11262         protected void onChildAttachedToWindow(View child) {
11263             if (getChildPosition(child) == getTargetPosition()) {
11264                 mTargetView = child;
11265                 if (DEBUG) {
11266                     Log.d(TAG, "smooth scroll target view has been attached");
11267                 }
11268             }
11269         }
11270 
11271         /**
11272          * Normalizes the vector.
11273          * @param scrollVector The vector that points to the target scroll position
11274          */
normalize(PointF scrollVector)11275         protected void normalize(PointF scrollVector) {
11276             final float magnitude = (float) Math.sqrt(scrollVector.x * scrollVector.x
11277                     + scrollVector.y * scrollVector.y);
11278             scrollVector.x /= magnitude;
11279             scrollVector.y /= magnitude;
11280         }
11281 
11282         /**
11283          * Called when smooth scroll is started. This might be a good time to do setup.
11284          */
onStart()11285         protected abstract void onStart();
11286 
11287         /**
11288          * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
11289          * @see #stop()
11290          */
onStop()11291         protected abstract void onStop();
11292 
11293         /**
11294          * <p>RecyclerView will call this method each time it scrolls until it can find the target
11295          * position in the layout.</p>
11296          * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
11297          * provided {@link Action} to define the next scroll.</p>
11298          *
11299          * @param dx        Last scroll amount horizontally
11300          * @param dy        Last scroll amount vertically
11301          * @param state     Transient state of RecyclerView
11302          * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
11303          *                  update this object.
11304          */
onSeekTargetStep(int dx, int dy, State state, Action action)11305         protected abstract void onSeekTargetStep(int dx, int dy, State state, Action action);
11306 
11307         /**
11308          * Called when the target position is laid out. This is the last callback SmoothScroller
11309          * will receive and it should update the provided {@link Action} to define the scroll
11310          * details towards the target view.
11311          * @param targetView    The view element which render the target position.
11312          * @param state         Transient state of RecyclerView
11313          * @param action        Action instance that you should update to define final scroll action
11314          *                      towards the targetView
11315          */
onTargetFound(View targetView, State state, Action action)11316         protected abstract void onTargetFound(View targetView, State state, Action action);
11317 
11318         /**
11319          * Holds information about a smooth scroll request by a {@link SmoothScroller}.
11320          */
11321         public static class Action {
11322 
11323             public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
11324 
11325             private int mDx;
11326 
11327             private int mDy;
11328 
11329             private int mDuration;
11330 
11331             private int mJumpToPosition = NO_POSITION;
11332 
11333             private Interpolator mInterpolator;
11334 
11335             private boolean mChanged = false;
11336 
11337             // we track this variable to inform custom implementer if they are updating the action
11338             // in every animation callback
11339             private int mConsecutiveUpdates = 0;
11340 
11341             /**
11342              * @param dx Pixels to scroll horizontally
11343              * @param dy Pixels to scroll vertically
11344              */
Action(int dx, int dy)11345             public Action(int dx, int dy) {
11346                 this(dx, dy, UNDEFINED_DURATION, null);
11347             }
11348 
11349             /**
11350              * @param dx       Pixels to scroll horizontally
11351              * @param dy       Pixels to scroll vertically
11352              * @param duration Duration of the animation in milliseconds
11353              */
Action(int dx, int dy, int duration)11354             public Action(int dx, int dy, int duration) {
11355                 this(dx, dy, duration, null);
11356             }
11357 
11358             /**
11359              * @param dx           Pixels to scroll horizontally
11360              * @param dy           Pixels to scroll vertically
11361              * @param duration     Duration of the animation in milliseconds
11362              * @param interpolator Interpolator to be used when calculating scroll position in each
11363              *                     animation step
11364              */
Action(int dx, int dy, int duration, Interpolator interpolator)11365             public Action(int dx, int dy, int duration, Interpolator interpolator) {
11366                 mDx = dx;
11367                 mDy = dy;
11368                 mDuration = duration;
11369                 mInterpolator = interpolator;
11370             }
11371 
11372             /**
11373              * Instead of specifying pixels to scroll, use the target position to jump using
11374              * {@link RecyclerView#scrollToPosition(int)}.
11375              * <p>
11376              * You may prefer using this method if scroll target is really far away and you prefer
11377              * to jump to a location and smooth scroll afterwards.
11378              * <p>
11379              * Note that calling this method takes priority over other update methods such as
11380              * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
11381              * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
11382              * {@link #jumpTo(int)}, the other changes will not be considered for this animation
11383              * frame.
11384              *
11385              * @param targetPosition The target item position to scroll to using instant scrolling.
11386              */
jumpTo(int targetPosition)11387             public void jumpTo(int targetPosition) {
11388                 mJumpToPosition = targetPosition;
11389             }
11390 
hasJumpTarget()11391             boolean hasJumpTarget() {
11392                 return mJumpToPosition >= 0;
11393             }
11394 
runIfNecessary(RecyclerView recyclerView)11395             void runIfNecessary(RecyclerView recyclerView) {
11396                 if (mJumpToPosition >= 0) {
11397                     final int position = mJumpToPosition;
11398                     mJumpToPosition = NO_POSITION;
11399                     recyclerView.jumpToPositionForSmoothScroller(position);
11400                     mChanged = false;
11401                     return;
11402                 }
11403                 if (mChanged) {
11404                     validate();
11405                     if (mInterpolator == null) {
11406                         if (mDuration == UNDEFINED_DURATION) {
11407                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
11408                         } else {
11409                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
11410                         }
11411                     } else {
11412                         recyclerView.mViewFlinger.smoothScrollBy(
11413                                 mDx, mDy, mDuration, mInterpolator);
11414                     }
11415                     mConsecutiveUpdates++;
11416                     if (mConsecutiveUpdates > 10) {
11417                         // A new action is being set in every animation step. This looks like a bad
11418                         // implementation. Inform developer.
11419                         Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
11420                                 + " you are not changing it unless necessary");
11421                     }
11422                     mChanged = false;
11423                 } else {
11424                     mConsecutiveUpdates = 0;
11425                 }
11426             }
11427 
validate()11428             private void validate() {
11429                 if (mInterpolator != null && mDuration < 1) {
11430                     throw new IllegalStateException("If you provide an interpolator, you must"
11431                             + " set a positive duration");
11432                 } else if (mDuration < 1) {
11433                     throw new IllegalStateException("Scroll duration must be a positive number");
11434                 }
11435             }
11436 
getDx()11437             public int getDx() {
11438                 return mDx;
11439             }
11440 
setDx(int dx)11441             public void setDx(int dx) {
11442                 mChanged = true;
11443                 mDx = dx;
11444             }
11445 
getDy()11446             public int getDy() {
11447                 return mDy;
11448             }
11449 
setDy(int dy)11450             public void setDy(int dy) {
11451                 mChanged = true;
11452                 mDy = dy;
11453             }
11454 
getDuration()11455             public int getDuration() {
11456                 return mDuration;
11457             }
11458 
setDuration(int duration)11459             public void setDuration(int duration) {
11460                 mChanged = true;
11461                 mDuration = duration;
11462             }
11463 
getInterpolator()11464             public Interpolator getInterpolator() {
11465                 return mInterpolator;
11466             }
11467 
11468             /**
11469              * Sets the interpolator to calculate scroll steps
11470              * @param interpolator The interpolator to use. If you specify an interpolator, you must
11471              *                     also set the duration.
11472              * @see #setDuration(int)
11473              */
setInterpolator(Interpolator interpolator)11474             public void setInterpolator(Interpolator interpolator) {
11475                 mChanged = true;
11476                 mInterpolator = interpolator;
11477             }
11478 
11479             /**
11480              * Updates the action with given parameters.
11481              * @param dx Pixels to scroll horizontally
11482              * @param dy Pixels to scroll vertically
11483              * @param duration Duration of the animation in milliseconds
11484              * @param interpolator Interpolator to be used when calculating scroll position in each
11485              *                     animation step
11486              */
update(int dx, int dy, int duration, Interpolator interpolator)11487             public void update(int dx, int dy, int duration, Interpolator interpolator) {
11488                 mDx = dx;
11489                 mDy = dy;
11490                 mDuration = duration;
11491                 mInterpolator = interpolator;
11492                 mChanged = true;
11493             }
11494         }
11495 
11496         /**
11497          * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
11498          * to provide a hint to a {@link SmoothScroller} about the location of the target position.
11499          */
11500         public interface ScrollVectorProvider {
11501             /**
11502              * Should calculate the vector that points to the direction where the target position
11503              * can be found.
11504              * <p>
11505              * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
11506              * the target position.
11507              * <p>
11508              * The magnitude of the vector is not important. It is always normalized before being
11509              * used by the {@link LinearSmoothScroller}.
11510              * <p>
11511              * LayoutManager should not check whether the position exists in the adapter or not.
11512              *
11513              * @param targetPosition the target position to which the returned vector should point
11514              *
11515              * @return the scroll vector for a given position.
11516              */
computeScrollVectorForPosition(int targetPosition)11517             PointF computeScrollVectorForPosition(int targetPosition);
11518         }
11519     }
11520 
11521     static class AdapterDataObservable extends Observable<AdapterDataObserver> {
hasObservers()11522         public boolean hasObservers() {
11523             return !mObservers.isEmpty();
11524         }
11525 
notifyChanged()11526         public void notifyChanged() {
11527             // since onChanged() is implemented by the app, it could do anything, including
11528             // removing itself from {@link mObservers} - and that could cause problems if
11529             // an iterator is used on the ArrayList {@link mObservers}.
11530             // to avoid such problems, just march thru the list in the reverse order.
11531             for (int i = mObservers.size() - 1; i >= 0; i--) {
11532                 mObservers.get(i).onChanged();
11533             }
11534         }
11535 
notifyItemRangeChanged(int positionStart, int itemCount)11536         public void notifyItemRangeChanged(int positionStart, int itemCount) {
11537             notifyItemRangeChanged(positionStart, itemCount, null);
11538         }
11539 
notifyItemRangeChanged(int positionStart, int itemCount, Object payload)11540         public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
11541             // since onItemRangeChanged() is implemented by the app, it could do anything, including
11542             // removing itself from {@link mObservers} - and that could cause problems if
11543             // an iterator is used on the ArrayList {@link mObservers}.
11544             // to avoid such problems, just march thru the list in the reverse order.
11545             for (int i = mObservers.size() - 1; i >= 0; i--) {
11546                 mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
11547             }
11548         }
11549 
notifyItemRangeInserted(int positionStart, int itemCount)11550         public void notifyItemRangeInserted(int positionStart, int itemCount) {
11551             // since onItemRangeInserted() is implemented by the app, it could do anything,
11552             // including removing itself from {@link mObservers} - and that could cause problems if
11553             // an iterator is used on the ArrayList {@link mObservers}.
11554             // to avoid such problems, just march thru the list in the reverse order.
11555             for (int i = mObservers.size() - 1; i >= 0; i--) {
11556                 mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
11557             }
11558         }
11559 
notifyItemRangeRemoved(int positionStart, int itemCount)11560         public void notifyItemRangeRemoved(int positionStart, int itemCount) {
11561             // since onItemRangeRemoved() is implemented by the app, it could do anything, including
11562             // removing itself from {@link mObservers} - and that could cause problems if
11563             // an iterator is used on the ArrayList {@link mObservers}.
11564             // to avoid such problems, just march thru the list in the reverse order.
11565             for (int i = mObservers.size() - 1; i >= 0; i--) {
11566                 mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
11567             }
11568         }
11569 
notifyItemMoved(int fromPosition, int toPosition)11570         public void notifyItemMoved(int fromPosition, int toPosition) {
11571             for (int i = mObservers.size() - 1; i >= 0; i--) {
11572                 mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
11573             }
11574         }
11575     }
11576 
11577     /**
11578      * This is public so that the CREATOR can be accessed on cold launch.
11579      * @hide
11580      */
11581     @RestrictTo(LIBRARY_GROUP)
11582     public static class SavedState extends AbsSavedState {
11583 
11584         Parcelable mLayoutState;
11585 
11586         /**
11587          * called by CREATOR
11588          */
SavedState(Parcel in, ClassLoader loader)11589         SavedState(Parcel in, ClassLoader loader) {
11590             super(in, loader);
11591             mLayoutState = in.readParcelable(
11592                     loader != null ? loader : LayoutManager.class.getClassLoader());
11593         }
11594 
11595         /**
11596          * Called by onSaveInstanceState
11597          */
SavedState(Parcelable superState)11598         SavedState(Parcelable superState) {
11599             super(superState);
11600         }
11601 
11602         @Override
writeToParcel(Parcel dest, int flags)11603         public void writeToParcel(Parcel dest, int flags) {
11604             super.writeToParcel(dest, flags);
11605             dest.writeParcelable(mLayoutState, 0);
11606         }
11607 
copyFrom(SavedState other)11608         void copyFrom(SavedState other) {
11609             mLayoutState = other.mLayoutState;
11610         }
11611 
11612         public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
11613             @Override
11614             public SavedState createFromParcel(Parcel in, ClassLoader loader) {
11615                 return new SavedState(in, loader);
11616             }
11617 
11618             @Override
11619             public SavedState createFromParcel(Parcel in) {
11620                 return new SavedState(in, null);
11621             }
11622 
11623             @Override
11624             public SavedState[] newArray(int size) {
11625                 return new SavedState[size];
11626             }
11627         };
11628     }
11629 
11630     /**
11631      * <p>Contains useful information about the current RecyclerView state like target scroll
11632      * position or view focus. State object can also keep arbitrary data, identified by resource
11633      * ids.</p>
11634      * <p>Often times, RecyclerView components will need to pass information between each other.
11635      * To provide a well defined data bus between components, RecyclerView passes the same State
11636      * object to component callbacks and these components can use it to exchange data.</p>
11637      * <p>If you implement custom components, you can use State's put/get/remove methods to pass
11638      * data between your components without needing to manage their lifecycles.</p>
11639      */
11640     public static class State {
11641         static final int STEP_START = 1;
11642         static final int STEP_LAYOUT = 1 << 1;
11643         static final int STEP_ANIMATIONS = 1 << 2;
11644 
assertLayoutStep(int accepted)11645         void assertLayoutStep(int accepted) {
11646             if ((accepted & mLayoutStep) == 0) {
11647                 throw new IllegalStateException("Layout state should be one of "
11648                         + Integer.toBinaryString(accepted) + " but it is "
11649                         + Integer.toBinaryString(mLayoutStep));
11650             }
11651         }
11652 
11653 
11654         /** Owned by SmoothScroller */
11655         private int mTargetPosition = RecyclerView.NO_POSITION;
11656 
11657         private SparseArray<Object> mData;
11658 
11659         ////////////////////////////////////////////////////////////////////////////////////////////
11660         // Fields below are carried from one layout pass to the next
11661         ////////////////////////////////////////////////////////////////////////////////////////////
11662 
11663         /**
11664          * Number of items adapter had in the previous layout.
11665          */
11666         int mPreviousLayoutItemCount = 0;
11667 
11668         /**
11669          * Number of items that were NOT laid out but has been deleted from the adapter after the
11670          * previous layout.
11671          */
11672         int mDeletedInvisibleItemCountSincePreviousLayout = 0;
11673 
11674         ////////////////////////////////////////////////////////////////////////////////////////////
11675         // Fields below must be updated or cleared before they are used (generally before a pass)
11676         ////////////////////////////////////////////////////////////////////////////////////////////
11677 
11678         @IntDef(flag = true, value = {
11679                 STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
11680         })
11681         @Retention(RetentionPolicy.SOURCE)
11682         @interface LayoutState {}
11683 
11684         @LayoutState
11685         int mLayoutStep = STEP_START;
11686 
11687         /**
11688          * Number of items adapter has.
11689          */
11690         int mItemCount = 0;
11691 
11692         boolean mStructureChanged = false;
11693 
11694         boolean mInPreLayout = false;
11695 
11696         boolean mTrackOldChangeHolders = false;
11697 
11698         boolean mIsMeasuring = false;
11699 
11700         ////////////////////////////////////////////////////////////////////////////////////////////
11701         // Fields below are always reset outside of the pass (or passes) that use them
11702         ////////////////////////////////////////////////////////////////////////////////////////////
11703 
11704         boolean mRunSimpleAnimations = false;
11705 
11706         boolean mRunPredictiveAnimations = false;
11707 
11708         /**
11709          * This data is saved before a layout calculation happens. After the layout is finished,
11710          * if the previously focused view has been replaced with another view for the same item, we
11711          * move the focus to the new item automatically.
11712          */
11713         int mFocusedItemPosition;
11714         long mFocusedItemId;
11715         // when a sub child has focus, record its id and see if we can directly request focus on
11716         // that one instead
11717         int mFocusedSubChildId;
11718 
11719         int mRemainingScrollHorizontal;
11720         int mRemainingScrollVertical;
11721 
11722         ////////////////////////////////////////////////////////////////////////////////////////////
11723 
reset()11724         State reset() {
11725             mTargetPosition = RecyclerView.NO_POSITION;
11726             if (mData != null) {
11727                 mData.clear();
11728             }
11729             mItemCount = 0;
11730             mStructureChanged = false;
11731             mIsMeasuring = false;
11732             return this;
11733         }
11734 
11735         /**
11736          * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
11737          * prior to any layout passes.
11738          *
11739          * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
11740          * that Recycler#getViewForPosition() can function safely.</p>
11741          */
prepareForNestedPrefetch(Adapter adapter)11742         void prepareForNestedPrefetch(Adapter adapter) {
11743             mLayoutStep = STEP_START;
11744             mItemCount = adapter.getItemCount();
11745             mInPreLayout = false;
11746             mTrackOldChangeHolders = false;
11747             mIsMeasuring = false;
11748         }
11749 
11750         /**
11751          * Returns true if the RecyclerView is currently measuring the layout. This value is
11752          * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
11753          * has non-exact measurement specs.
11754          * <p>
11755          * Note that if the LayoutManager supports predictive animations and it is calculating the
11756          * pre-layout step, this value will be {@code false} even if the RecyclerView is in
11757          * {@code onMeasure} call. This is because pre-layout means the previous state of the
11758          * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
11759          * LayoutManager is always guaranteed to receive another call to
11760          * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
11761          *
11762          * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
11763          */
isMeasuring()11764         public boolean isMeasuring() {
11765             return mIsMeasuring;
11766         }
11767 
11768         /**
11769          * Returns true if
11770          * @return
11771          */
isPreLayout()11772         public boolean isPreLayout() {
11773             return mInPreLayout;
11774         }
11775 
11776         /**
11777          * Returns whether RecyclerView will run predictive animations in this layout pass
11778          * or not.
11779          *
11780          * @return true if RecyclerView is calculating predictive animations to be run at the end
11781          *         of the layout pass.
11782          */
willRunPredictiveAnimations()11783         public boolean willRunPredictiveAnimations() {
11784             return mRunPredictiveAnimations;
11785         }
11786 
11787         /**
11788          * Returns whether RecyclerView will run simple animations in this layout pass
11789          * or not.
11790          *
11791          * @return true if RecyclerView is calculating simple animations to be run at the end of
11792          *         the layout pass.
11793          */
willRunSimpleAnimations()11794         public boolean willRunSimpleAnimations() {
11795             return mRunSimpleAnimations;
11796         }
11797 
11798         /**
11799          * Removes the mapping from the specified id, if there was any.
11800          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
11801          *                   preserve cross functionality and avoid conflicts.
11802          */
remove(int resourceId)11803         public void remove(int resourceId) {
11804             if (mData == null) {
11805                 return;
11806             }
11807             mData.remove(resourceId);
11808         }
11809 
11810         /**
11811          * Gets the Object mapped from the specified id, or <code>null</code>
11812          * if no such data exists.
11813          *
11814          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
11815          *                   to
11816          *                   preserve cross functionality and avoid conflicts.
11817          */
11818         @SuppressWarnings("TypeParameterUnusedInFormals")
get(int resourceId)11819         public <T> T get(int resourceId) {
11820             if (mData == null) {
11821                 return null;
11822             }
11823             return (T) mData.get(resourceId);
11824         }
11825 
11826         /**
11827          * Adds a mapping from the specified id to the specified value, replacing the previous
11828          * mapping from the specified key if there was one.
11829          *
11830          * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
11831          *                   preserve cross functionality and avoid conflicts.
11832          * @param data       The data you want to associate with the resourceId.
11833          */
put(int resourceId, Object data)11834         public void put(int resourceId, Object data) {
11835             if (mData == null) {
11836                 mData = new SparseArray<Object>();
11837             }
11838             mData.put(resourceId, data);
11839         }
11840 
11841         /**
11842          * If scroll is triggered to make a certain item visible, this value will return the
11843          * adapter index of that item.
11844          * @return Adapter index of the target item or
11845          * {@link RecyclerView#NO_POSITION} if there is no target
11846          * position.
11847          */
getTargetScrollPosition()11848         public int getTargetScrollPosition() {
11849             return mTargetPosition;
11850         }
11851 
11852         /**
11853          * Returns if current scroll has a target position.
11854          * @return true if scroll is being triggered to make a certain position visible
11855          * @see #getTargetScrollPosition()
11856          */
hasTargetScrollPosition()11857         public boolean hasTargetScrollPosition() {
11858             return mTargetPosition != RecyclerView.NO_POSITION;
11859         }
11860 
11861         /**
11862          * @return true if the structure of the data set has changed since the last call to
11863          *         onLayoutChildren, false otherwise
11864          */
didStructureChange()11865         public boolean didStructureChange() {
11866             return mStructureChanged;
11867         }
11868 
11869         /**
11870          * Returns the total number of items that can be laid out. Note that this number is not
11871          * necessarily equal to the number of items in the adapter, so you should always use this
11872          * number for your position calculations and never access the adapter directly.
11873          * <p>
11874          * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
11875          * data changes on existing Views. These calculations are used to decide which animations
11876          * should be run.
11877          * <p>
11878          * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
11879          * present the correct state to LayoutManager in pre-layout pass.
11880          * <p>
11881          * For example, a newly added item is not included in pre-layout item count because
11882          * pre-layout reflects the contents of the adapter before the item is added. Behind the
11883          * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
11884          * LayoutManager does not know about the new item's existence in pre-layout. The item will
11885          * be available in second layout pass and will be included in the item count. Similar
11886          * adjustments are made for moved and removed items as well.
11887          * <p>
11888          * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
11889          *
11890          * @return The number of items currently available
11891          * @see LayoutManager#getItemCount()
11892          */
getItemCount()11893         public int getItemCount() {
11894             return mInPreLayout
11895                     ? (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout)
11896                     : mItemCount;
11897         }
11898 
11899         /**
11900          * Returns remaining horizontal scroll distance of an ongoing scroll animation(fling/
11901          * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
11902          * other than {@link #SCROLL_STATE_SETTLING}.
11903          *
11904          * @return Remaining horizontal scroll distance
11905          */
getRemainingScrollHorizontal()11906         public int getRemainingScrollHorizontal() {
11907             return mRemainingScrollHorizontal;
11908         }
11909 
11910         /**
11911          * Returns remaining vertical scroll distance of an ongoing scroll animation(fling/
11912          * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
11913          * other than {@link #SCROLL_STATE_SETTLING}.
11914          *
11915          * @return Remaining vertical scroll distance
11916          */
getRemainingScrollVertical()11917         public int getRemainingScrollVertical() {
11918             return mRemainingScrollVertical;
11919         }
11920 
11921         @Override
toString()11922         public String toString() {
11923             return "State{"
11924                     + "mTargetPosition=" + mTargetPosition
11925                     + ", mData=" + mData
11926                     + ", mItemCount=" + mItemCount
11927                     + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
11928                     + ", mDeletedInvisibleItemCountSincePreviousLayout="
11929                     + mDeletedInvisibleItemCountSincePreviousLayout
11930                     + ", mStructureChanged=" + mStructureChanged
11931                     + ", mInPreLayout=" + mInPreLayout
11932                     + ", mRunSimpleAnimations=" + mRunSimpleAnimations
11933                     + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
11934                     + '}';
11935         }
11936     }
11937 
11938     /**
11939      * This class defines the behavior of fling if the developer wishes to handle it.
11940      * <p>
11941      * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
11942      *
11943      * @see #setOnFlingListener(OnFlingListener)
11944      */
11945     public abstract static class OnFlingListener {
11946 
11947         /**
11948          * Override this to handle a fling given the velocities in both x and y directions.
11949          * Note that this method will only be called if the associated {@link LayoutManager}
11950          * supports scrolling and the fling is not handled by nested scrolls first.
11951          *
11952          * @param velocityX the fling velocity on the X axis
11953          * @param velocityY the fling velocity on the Y axis
11954          *
11955          * @return true if the fling was handled, false otherwise.
11956          */
onFling(int velocityX, int velocityY)11957         public abstract boolean onFling(int velocityX, int velocityY);
11958     }
11959 
11960     /**
11961      * Internal listener that manages items after animations finish. This is how items are
11962      * retained (not recycled) during animations, but allowed to be recycled afterwards.
11963      * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
11964      * method on the animator's listener when it is done animating any item.
11965      */
11966     private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
11967 
ItemAnimatorRestoreListener()11968         ItemAnimatorRestoreListener() {
11969         }
11970 
11971         @Override
onAnimationFinished(ViewHolder item)11972         public void onAnimationFinished(ViewHolder item) {
11973             item.setIsRecyclable(true);
11974             if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
11975                 item.mShadowedHolder = null;
11976             }
11977             // always null this because an OldViewHolder can never become NewViewHolder w/o being
11978             // recycled.
11979             item.mShadowingHolder = null;
11980             if (!item.shouldBeKeptAsChild()) {
11981                 if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
11982                     removeDetachedView(item.itemView, false);
11983                 }
11984             }
11985         }
11986     }
11987 
11988     /**
11989      * This class defines the animations that take place on items as changes are made
11990      * to the adapter.
11991      *
11992      * Subclasses of ItemAnimator can be used to implement custom animations for actions on
11993      * ViewHolder items. The RecyclerView will manage retaining these items while they
11994      * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
11995      * when a ViewHolder's animation is finished. In other words, there must be a matching
11996      * {@link #dispatchAnimationFinished(ViewHolder)} call for each
11997      * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
11998      * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11999      * animateChange()}
12000      * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
12001      * and
12002      * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12003      * animateDisappearance()} call.
12004      *
12005      * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
12006      *
12007      * @see #setItemAnimator(ItemAnimator)
12008      */
12009     @SuppressWarnings("UnusedParameters")
12010     public abstract static class ItemAnimator {
12011 
12012         /**
12013          * The Item represented by this ViewHolder is updated.
12014          * <p>
12015          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12016          */
12017         public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
12018 
12019         /**
12020          * The Item represented by this ViewHolder is removed from the adapter.
12021          * <p>
12022          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12023          */
12024         public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
12025 
12026         /**
12027          * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
12028          * represented by this ViewHolder is invalid.
12029          * <p>
12030          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12031          */
12032         public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
12033 
12034         /**
12035          * The position of the Item represented by this ViewHolder has been changed. This flag is
12036          * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
12037          * any adapter change that may have a side effect on this item. (e.g. The item before this
12038          * one has been removed from the Adapter).
12039          * <p>
12040          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12041          */
12042         public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
12043 
12044         /**
12045          * This ViewHolder was not laid out but has been added to the layout in pre-layout state
12046          * by the {@link LayoutManager}. This means that the item was already in the Adapter but
12047          * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
12048          * to add new items in pre-layout to specify their virtual location when they are invisible
12049          * (e.g. to specify the item should <i>animate in</i> from below the visible area).
12050          * <p>
12051          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12052          */
12053         public static final int FLAG_APPEARED_IN_PRE_LAYOUT =
12054                 ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
12055 
12056         /**
12057          * The set of flags that might be passed to
12058          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12059          */
12060         @IntDef(flag = true, value = {
12061                 FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
12062                 FLAG_APPEARED_IN_PRE_LAYOUT
12063         })
12064         @Retention(RetentionPolicy.SOURCE)
12065         public @interface AdapterChanges {}
12066         private ItemAnimatorListener mListener = null;
12067         private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
12068                 new ArrayList<ItemAnimatorFinishedListener>();
12069 
12070         private long mAddDuration = 120;
12071         private long mRemoveDuration = 120;
12072         private long mMoveDuration = 250;
12073         private long mChangeDuration = 250;
12074 
12075         /**
12076          * Gets the current duration for which all move animations will run.
12077          *
12078          * @return The current move duration
12079          */
getMoveDuration()12080         public long getMoveDuration() {
12081             return mMoveDuration;
12082         }
12083 
12084         /**
12085          * Sets the duration for which all move animations will run.
12086          *
12087          * @param moveDuration The move duration
12088          */
setMoveDuration(long moveDuration)12089         public void setMoveDuration(long moveDuration) {
12090             mMoveDuration = moveDuration;
12091         }
12092 
12093         /**
12094          * Gets the current duration for which all add animations will run.
12095          *
12096          * @return The current add duration
12097          */
getAddDuration()12098         public long getAddDuration() {
12099             return mAddDuration;
12100         }
12101 
12102         /**
12103          * Sets the duration for which all add animations will run.
12104          *
12105          * @param addDuration The add duration
12106          */
setAddDuration(long addDuration)12107         public void setAddDuration(long addDuration) {
12108             mAddDuration = addDuration;
12109         }
12110 
12111         /**
12112          * Gets the current duration for which all remove animations will run.
12113          *
12114          * @return The current remove duration
12115          */
getRemoveDuration()12116         public long getRemoveDuration() {
12117             return mRemoveDuration;
12118         }
12119 
12120         /**
12121          * Sets the duration for which all remove animations will run.
12122          *
12123          * @param removeDuration The remove duration
12124          */
setRemoveDuration(long removeDuration)12125         public void setRemoveDuration(long removeDuration) {
12126             mRemoveDuration = removeDuration;
12127         }
12128 
12129         /**
12130          * Gets the current duration for which all change animations will run.
12131          *
12132          * @return The current change duration
12133          */
getChangeDuration()12134         public long getChangeDuration() {
12135             return mChangeDuration;
12136         }
12137 
12138         /**
12139          * Sets the duration for which all change animations will run.
12140          *
12141          * @param changeDuration The change duration
12142          */
setChangeDuration(long changeDuration)12143         public void setChangeDuration(long changeDuration) {
12144             mChangeDuration = changeDuration;
12145         }
12146 
12147         /**
12148          * Internal only:
12149          * Sets the listener that must be called when the animator is finished
12150          * animating the item (or immediately if no animation happens). This is set
12151          * internally and is not intended to be set by external code.
12152          *
12153          * @param listener The listener that must be called.
12154          */
setListener(ItemAnimatorListener listener)12155         void setListener(ItemAnimatorListener listener) {
12156             mListener = listener;
12157         }
12158 
12159         /**
12160          * Called by the RecyclerView before the layout begins. Item animator should record
12161          * necessary information about the View before it is potentially rebound, moved or removed.
12162          * <p>
12163          * The data returned from this method will be passed to the related <code>animate**</code>
12164          * methods.
12165          * <p>
12166          * Note that this method may be called after pre-layout phase if LayoutManager adds new
12167          * Views to the layout in pre-layout pass.
12168          * <p>
12169          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
12170          * the View and the adapter change flags.
12171          *
12172          * @param state       The current State of RecyclerView which includes some useful data
12173          *                    about the layout that will be calculated.
12174          * @param viewHolder  The ViewHolder whose information should be recorded.
12175          * @param changeFlags Additional information about what changes happened in the Adapter
12176          *                    about the Item represented by this ViewHolder. For instance, if
12177          *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
12178          * @param payloads    The payload list that was previously passed to
12179          *                    {@link Adapter#notifyItemChanged(int, Object)} or
12180          *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
12181          *
12182          * @return An ItemHolderInfo instance that preserves necessary information about the
12183          * ViewHolder. This object will be passed back to related <code>animate**</code> methods
12184          * after layout is complete.
12185          *
12186          * @see #recordPostLayoutInformation(State, ViewHolder)
12187          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12188          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12189          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12190          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12191          */
recordPreLayoutInformation(@onNull State state, @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags, @NonNull List<Object> payloads)12192         public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
12193                 @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
12194                 @NonNull List<Object> payloads) {
12195             return obtainHolderInfo().setFrom(viewHolder);
12196         }
12197 
12198         /**
12199          * Called by the RecyclerView after the layout is complete. Item animator should record
12200          * necessary information about the View's final state.
12201          * <p>
12202          * The data returned from this method will be passed to the related <code>animate**</code>
12203          * methods.
12204          * <p>
12205          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
12206          * the View.
12207          *
12208          * @param state      The current State of RecyclerView which includes some useful data about
12209          *                   the layout that will be calculated.
12210          * @param viewHolder The ViewHolder whose information should be recorded.
12211          *
12212          * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
12213          * This object will be passed back to related <code>animate**</code> methods when
12214          * RecyclerView decides how items should be animated.
12215          *
12216          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12217          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12218          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12219          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12220          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12221          */
recordPostLayoutInformation(@onNull State state, @NonNull ViewHolder viewHolder)12222         public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
12223                 @NonNull ViewHolder viewHolder) {
12224             return obtainHolderInfo().setFrom(viewHolder);
12225         }
12226 
12227         /**
12228          * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
12229          * <p>
12230          * This means that the View was a child of the LayoutManager when layout started but has
12231          * been removed by the LayoutManager. It might have been removed from the adapter or simply
12232          * become invisible due to other factors. You can distinguish these two cases by checking
12233          * the change flags that were passed to
12234          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12235          * <p>
12236          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
12237          * animation callback method which will be called by the RecyclerView depends on the
12238          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
12239          * LayoutManager's decision whether to layout the changed version of a disappearing
12240          * ViewHolder or not. RecyclerView will call
12241          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12242          * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
12243          * returns {@code false} from
12244          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
12245          * LayoutManager lays out a new disappearing view that holds the updated information.
12246          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
12247          * <p>
12248          * If LayoutManager supports predictive animations, it might provide a target disappear
12249          * location for the View by laying it out in that location. When that happens,
12250          * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
12251          * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
12252          * <p>
12253          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12254          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12255          * decides not to animate the view).
12256          *
12257          * @param viewHolder    The ViewHolder which should be animated
12258          * @param preLayoutInfo The information that was returned from
12259          *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12260          * @param postLayoutInfo The information that was returned from
12261          *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
12262          *                       null if the LayoutManager did not layout the item.
12263          *
12264          * @return true if a later call to {@link #runPendingAnimations()} is requested,
12265          * false otherwise.
12266          */
animateDisappearance(@onNull ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo)12267         public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
12268                 @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
12269 
12270         /**
12271          * Called by the RecyclerView when a ViewHolder is added to the layout.
12272          * <p>
12273          * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
12274          * but has  been added by the LayoutManager. It might be newly added to the adapter or
12275          * simply become visible due to other factors.
12276          * <p>
12277          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12278          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12279          * decides not to animate the view).
12280          *
12281          * @param viewHolder     The ViewHolder which should be animated
12282          * @param preLayoutInfo  The information that was returned from
12283          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12284          *                       Might be null if Item was just added to the adapter or
12285          *                       LayoutManager does not support predictive animations or it could
12286          *                       not predict that this ViewHolder will become visible.
12287          * @param postLayoutInfo The information that was returned from {@link
12288          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12289          *
12290          * @return true if a later call to {@link #runPendingAnimations()} is requested,
12291          * false otherwise.
12292          */
animateAppearance(@onNull ViewHolder viewHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)12293         public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
12294                 @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12295 
12296         /**
12297          * Called by the RecyclerView when a ViewHolder is present in both before and after the
12298          * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
12299          * for it or a {@link Adapter#notifyDataSetChanged()} call.
12300          * <p>
12301          * This ViewHolder still represents the same data that it was representing when the layout
12302          * started but its position / size may be changed by the LayoutManager.
12303          * <p>
12304          * If the Item's layout position didn't change, RecyclerView still calls this method because
12305          * it does not track this information (or does not necessarily know that an animation is
12306          * not required). Your ItemAnimator should handle this case and if there is nothing to
12307          * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
12308          * <code>false</code>.
12309          * <p>
12310          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12311          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12312          * decides not to animate the view).
12313          *
12314          * @param viewHolder     The ViewHolder which should be animated
12315          * @param preLayoutInfo  The information that was returned from
12316          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12317          * @param postLayoutInfo The information that was returned from {@link
12318          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12319          *
12320          * @return true if a later call to {@link #runPendingAnimations()} is requested,
12321          * false otherwise.
12322          */
animatePersistence(@onNull ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)12323         public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
12324                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12325 
12326         /**
12327          * Called by the RecyclerView when an adapter item is present both before and after the
12328          * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
12329          * for it. This method may also be called when
12330          * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
12331          * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
12332          * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
12333          * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
12334          * called for the new ViewHolder and the old one will be recycled.
12335          * <p>
12336          * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
12337          * a good possibility that item contents didn't really change but it is rebound from the
12338          * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
12339          * screen didn't change and your animator should handle this case as well and avoid creating
12340          * unnecessary animations.
12341          * <p>
12342          * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
12343          * previous presentation of the item as-is and supply a new ViewHolder for the updated
12344          * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
12345          * This is useful if you don't know the contents of the Item and would like
12346          * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
12347          * <p>
12348          * When you are writing a custom item animator for your layout, it might be more performant
12349          * and elegant to re-use the same ViewHolder and animate the content changes manually.
12350          * <p>
12351          * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
12352          * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
12353          * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
12354          * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
12355          * which represent the same Item. In that case, only the new ViewHolder is visible
12356          * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
12357          * <p>
12358          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
12359          * ViewHolder when their animation is complete
12360          * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
12361          * animate the view).
12362          * <p>
12363          *  If oldHolder and newHolder are the same instance, you should call
12364          * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
12365          * <p>
12366          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
12367          * animation callback method which will be called by the RecyclerView depends on the
12368          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
12369          * LayoutManager's decision whether to layout the changed version of a disappearing
12370          * ViewHolder or not. RecyclerView will call
12371          * {@code animateChange} instead of
12372          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12373          * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
12374          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
12375          * LayoutManager lays out a new disappearing view that holds the updated information.
12376          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
12377          *
12378          * @param oldHolder     The ViewHolder before the layout is started, might be the same
12379          *                      instance with newHolder.
12380          * @param newHolder     The ViewHolder after the layout is finished, might be the same
12381          *                      instance with oldHolder.
12382          * @param preLayoutInfo  The information that was returned from
12383          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12384          * @param postLayoutInfo The information that was returned from {@link
12385          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12386          *
12387          * @return true if a later call to {@link #runPendingAnimations()} is requested,
12388          * false otherwise.
12389          */
animateChange(@onNull ViewHolder oldHolder, @NonNull ViewHolder newHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)12390         public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
12391                 @NonNull ViewHolder newHolder,
12392                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12393 
buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder)12394         @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
12395             int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
12396             if (viewHolder.isInvalid()) {
12397                 return FLAG_INVALIDATED;
12398             }
12399             if ((flags & FLAG_INVALIDATED) == 0) {
12400                 final int oldPos = viewHolder.getOldPosition();
12401                 final int pos = viewHolder.getAdapterPosition();
12402                 if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
12403                     flags |= FLAG_MOVED;
12404                 }
12405             }
12406             return flags;
12407         }
12408 
12409         /**
12410          * Called when there are pending animations waiting to be started. This state
12411          * is governed by the return values from
12412          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12413          * animateAppearance()},
12414          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12415          * animateChange()}
12416          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12417          * animatePersistence()}, and
12418          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12419          * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
12420          * called later to start the associated animations. runPendingAnimations() will be scheduled
12421          * to be run on the next frame.
12422          */
runPendingAnimations()12423         public abstract void runPendingAnimations();
12424 
12425         /**
12426          * Method called when an animation on a view should be ended immediately.
12427          * This could happen when other events, like scrolling, occur, so that
12428          * animating views can be quickly put into their proper end locations.
12429          * Implementations should ensure that any animations running on the item
12430          * are canceled and affected properties are set to their end values.
12431          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
12432          * animation since the animations are effectively done when this method is called.
12433          *
12434          * @param item The item for which an animation should be stopped.
12435          */
endAnimation(ViewHolder item)12436         public abstract void endAnimation(ViewHolder item);
12437 
12438         /**
12439          * Method called when all item animations should be ended immediately.
12440          * This could happen when other events, like scrolling, occur, so that
12441          * animating views can be quickly put into their proper end locations.
12442          * Implementations should ensure that any animations running on any items
12443          * are canceled and affected properties are set to their end values.
12444          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
12445          * animation since the animations are effectively done when this method is called.
12446          */
endAnimations()12447         public abstract void endAnimations();
12448 
12449         /**
12450          * Method which returns whether there are any item animations currently running.
12451          * This method can be used to determine whether to delay other actions until
12452          * animations end.
12453          *
12454          * @return true if there are any item animations currently running, false otherwise.
12455          */
isRunning()12456         public abstract boolean isRunning();
12457 
12458         /**
12459          * Method to be called by subclasses when an animation is finished.
12460          * <p>
12461          * For each call RecyclerView makes to
12462          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12463          * animateAppearance()},
12464          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12465          * animatePersistence()}, or
12466          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12467          * animateDisappearance()}, there
12468          * should
12469          * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
12470          * <p>
12471          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12472          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
12473          * and <code>newHolder</code>  (if they are not the same instance).
12474          *
12475          * @param viewHolder The ViewHolder whose animation is finished.
12476          * @see #onAnimationFinished(ViewHolder)
12477          */
dispatchAnimationFinished(ViewHolder viewHolder)12478         public final void dispatchAnimationFinished(ViewHolder viewHolder) {
12479             onAnimationFinished(viewHolder);
12480             if (mListener != null) {
12481                 mListener.onAnimationFinished(viewHolder);
12482             }
12483         }
12484 
12485         /**
12486          * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
12487          * ItemAnimator.
12488          *
12489          * @param viewHolder The ViewHolder whose animation is finished. There might still be other
12490          *                   animations running on this ViewHolder.
12491          * @see #dispatchAnimationFinished(ViewHolder)
12492          */
onAnimationFinished(ViewHolder viewHolder)12493         public void onAnimationFinished(ViewHolder viewHolder) {
12494         }
12495 
12496         /**
12497          * Method to be called by subclasses when an animation is started.
12498          * <p>
12499          * For each call RecyclerView makes to
12500          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12501          * animateAppearance()},
12502          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12503          * animatePersistence()}, or
12504          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12505          * animateDisappearance()}, there should be a matching
12506          * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
12507          * <p>
12508          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12509          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
12510          * and <code>newHolder</code> (if they are not the same instance).
12511          * <p>
12512          * If your ItemAnimator decides not to animate a ViewHolder, it should call
12513          * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
12514          * {@link #dispatchAnimationStarted(ViewHolder)}.
12515          *
12516          * @param viewHolder The ViewHolder whose animation is starting.
12517          * @see #onAnimationStarted(ViewHolder)
12518          */
dispatchAnimationStarted(ViewHolder viewHolder)12519         public final void dispatchAnimationStarted(ViewHolder viewHolder) {
12520             onAnimationStarted(viewHolder);
12521         }
12522 
12523         /**
12524          * Called when a new animation is started on the given ViewHolder.
12525          *
12526          * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
12527          *                   might already be animating and this might be another animation.
12528          * @see #dispatchAnimationStarted(ViewHolder)
12529          */
onAnimationStarted(ViewHolder viewHolder)12530         public void onAnimationStarted(ViewHolder viewHolder) {
12531 
12532         }
12533 
12534         /**
12535          * Like {@link #isRunning()}, this method returns whether there are any item
12536          * animations currently running. Additionally, the listener passed in will be called
12537          * when there are no item animations running, either immediately (before the method
12538          * returns) if no animations are currently running, or when the currently running
12539          * animations are {@link #dispatchAnimationsFinished() finished}.
12540          *
12541          * <p>Note that the listener is transient - it is either called immediately and not
12542          * stored at all, or stored only until it is called when running animations
12543          * are finished sometime later.</p>
12544          *
12545          * @param listener A listener to be called immediately if no animations are running
12546          * or later when currently-running animations have finished. A null listener is
12547          * equivalent to calling {@link #isRunning()}.
12548          * @return true if there are any item animations currently running, false otherwise.
12549          */
isRunning(ItemAnimatorFinishedListener listener)12550         public final boolean isRunning(ItemAnimatorFinishedListener listener) {
12551             boolean running = isRunning();
12552             if (listener != null) {
12553                 if (!running) {
12554                     listener.onAnimationsFinished();
12555                 } else {
12556                     mFinishedListeners.add(listener);
12557                 }
12558             }
12559             return running;
12560         }
12561 
12562         /**
12563          * When an item is changed, ItemAnimator can decide whether it wants to re-use
12564          * the same ViewHolder for animations or RecyclerView should create a copy of the
12565          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12566          * <p>
12567          * Note that this method will only be called if the {@link ViewHolder} still has the same
12568          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12569          * both {@link ViewHolder}s in the
12570          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12571          * <p>
12572          * If your application is using change payloads, you can override
12573          * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
12574          *
12575          * @param viewHolder The ViewHolder which represents the changed item's old content.
12576          *
12577          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
12578          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
12579          *         ItemAnimator to animate. Default implementation returns <code>true</code>.
12580          *
12581          * @see #canReuseUpdatedViewHolder(ViewHolder, List)
12582          */
canReuseUpdatedViewHolder(@onNull ViewHolder viewHolder)12583         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
12584             return true;
12585         }
12586 
12587         /**
12588          * When an item is changed, ItemAnimator can decide whether it wants to re-use
12589          * the same ViewHolder for animations or RecyclerView should create a copy of the
12590          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12591          * <p>
12592          * Note that this method will only be called if the {@link ViewHolder} still has the same
12593          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12594          * both {@link ViewHolder}s in the
12595          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12596          *
12597          * @param viewHolder The ViewHolder which represents the changed item's old content.
12598          * @param payloads A non-null list of merged payloads that were sent with change
12599          *                 notifications. Can be empty if the adapter is invalidated via
12600          *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
12601          *                 payloads will be passed into
12602          *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
12603          *                 method <b>if</b> this method returns <code>true</code>.
12604          *
12605          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
12606          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
12607          *         ItemAnimator to animate. Default implementation calls
12608          *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
12609          *
12610          * @see #canReuseUpdatedViewHolder(ViewHolder)
12611          */
canReuseUpdatedViewHolder(@onNull ViewHolder viewHolder, @NonNull List<Object> payloads)12612         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
12613                 @NonNull List<Object> payloads) {
12614             return canReuseUpdatedViewHolder(viewHolder);
12615         }
12616 
12617         /**
12618          * This method should be called by ItemAnimator implementations to notify
12619          * any listeners that all pending and active item animations are finished.
12620          */
dispatchAnimationsFinished()12621         public final void dispatchAnimationsFinished() {
12622             final int count = mFinishedListeners.size();
12623             for (int i = 0; i < count; ++i) {
12624                 mFinishedListeners.get(i).onAnimationsFinished();
12625             }
12626             mFinishedListeners.clear();
12627         }
12628 
12629         /**
12630          * Returns a new {@link ItemHolderInfo} which will be used to store information about the
12631          * ViewHolder. This information will later be passed into <code>animate**</code> methods.
12632          * <p>
12633          * You can override this method if you want to extend {@link ItemHolderInfo} and provide
12634          * your own instances.
12635          *
12636          * @return A new {@link ItemHolderInfo}.
12637          */
obtainHolderInfo()12638         public ItemHolderInfo obtainHolderInfo() {
12639             return new ItemHolderInfo();
12640         }
12641 
12642         /**
12643          * The interface to be implemented by listeners to animation events from this
12644          * ItemAnimator. This is used internally and is not intended for developers to
12645          * create directly.
12646          */
12647         interface ItemAnimatorListener {
onAnimationFinished(ViewHolder item)12648             void onAnimationFinished(ViewHolder item);
12649         }
12650 
12651         /**
12652          * This interface is used to inform listeners when all pending or running animations
12653          * in an ItemAnimator are finished. This can be used, for example, to delay an action
12654          * in a data set until currently-running animations are complete.
12655          *
12656          * @see #isRunning(ItemAnimatorFinishedListener)
12657          */
12658         public interface ItemAnimatorFinishedListener {
12659             /**
12660              * Notifies when all pending or running animations in an ItemAnimator are finished.
12661              */
onAnimationsFinished()12662             void onAnimationsFinished();
12663         }
12664 
12665         /**
12666          * A simple data structure that holds information about an item's bounds.
12667          * This information is used in calculating item animations. Default implementation of
12668          * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
12669          * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
12670          * structure. You can extend this class if you would like to keep more information about
12671          * the Views.
12672          * <p>
12673          * If you want to provide your own implementation but still use `super` methods to record
12674          * basic information, you can override {@link #obtainHolderInfo()} to provide your own
12675          * instances.
12676          */
12677         public static class ItemHolderInfo {
12678 
12679             /**
12680              * The left edge of the View (excluding decorations)
12681              */
12682             public int left;
12683 
12684             /**
12685              * The top edge of the View (excluding decorations)
12686              */
12687             public int top;
12688 
12689             /**
12690              * The right edge of the View (excluding decorations)
12691              */
12692             public int right;
12693 
12694             /**
12695              * The bottom edge of the View (excluding decorations)
12696              */
12697             public int bottom;
12698 
12699             /**
12700              * The change flags that were passed to
12701              * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
12702              */
12703             @AdapterChanges
12704             public int changeFlags;
12705 
ItemHolderInfo()12706             public ItemHolderInfo() {
12707             }
12708 
12709             /**
12710              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
12711              * the given ViewHolder. Clears all {@link #changeFlags}.
12712              *
12713              * @param holder The ViewHolder whose bounds should be copied.
12714              * @return This {@link ItemHolderInfo}
12715              */
setFrom(RecyclerView.ViewHolder holder)12716             public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder) {
12717                 return setFrom(holder, 0);
12718             }
12719 
12720             /**
12721              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
12722              * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
12723              *
12724              * @param holder The ViewHolder whose bounds should be copied.
12725              * @param flags  The adapter change flags that were passed into
12726              *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
12727              *               List)}.
12728              * @return This {@link ItemHolderInfo}
12729              */
setFrom(RecyclerView.ViewHolder holder, @AdapterChanges int flags)12730             public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder,
12731                     @AdapterChanges int flags) {
12732                 final View view = holder.itemView;
12733                 this.left = view.getLeft();
12734                 this.top = view.getTop();
12735                 this.right = view.getRight();
12736                 this.bottom = view.getBottom();
12737                 return this;
12738             }
12739         }
12740     }
12741 
12742     @Override
getChildDrawingOrder(int childCount, int i)12743     protected int getChildDrawingOrder(int childCount, int i) {
12744         if (mChildDrawingOrderCallback == null) {
12745             return super.getChildDrawingOrder(childCount, i);
12746         } else {
12747             return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
12748         }
12749     }
12750 
12751     /**
12752      * A callback interface that can be used to alter the drawing order of RecyclerView children.
12753      * <p>
12754      * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
12755      * that applies to that method also applies to this callback. For example, changing the drawing
12756      * order of two views will not have any effect if their elevation values are different since
12757      * elevation overrides the result of this callback.
12758      */
12759     public interface ChildDrawingOrderCallback {
12760         /**
12761          * Returns the index of the child to draw for this iteration. Override this
12762          * if you want to change the drawing order of children. By default, it
12763          * returns i.
12764          *
12765          * @param i The current iteration.
12766          * @return The index of the child to draw this iteration.
12767          *
12768          * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
12769          */
onGetChildDrawingOrder(int childCount, int i)12770         int onGetChildDrawingOrder(int childCount, int i);
12771     }
12772 
getScrollingChildHelper()12773     private NestedScrollingChildHelper getScrollingChildHelper() {
12774         if (mScrollingChildHelper == null) {
12775             mScrollingChildHelper = new NestedScrollingChildHelper(this);
12776         }
12777         return mScrollingChildHelper;
12778     }
12779 }
12780