• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.widget;
18 
19 import android.annotation.CallSuper;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.compat.annotation.UnsupportedAppUsage;
24 import android.content.Context;
25 import android.content.res.TypedArray;
26 import android.database.Observable;
27 import android.graphics.Canvas;
28 import android.graphics.Matrix;
29 import android.graphics.PointF;
30 import android.graphics.Rect;
31 import android.graphics.RectF;
32 import android.os.Build;
33 import android.os.Bundle;
34 import android.os.Parcel;
35 import android.os.Parcelable;
36 import android.os.SystemClock;
37 import android.os.Trace;
38 import android.util.AttributeSet;
39 import android.util.Log;
40 import android.util.SparseArray;
41 import android.util.TypedValue;
42 import android.view.AbsSavedState;
43 import android.view.Display;
44 import android.view.FocusFinder;
45 import android.view.InputDevice;
46 import android.view.MotionEvent;
47 import android.view.VelocityTracker;
48 import android.view.View;
49 import android.view.ViewConfiguration;
50 import android.view.ViewGroup;
51 import android.view.ViewParent;
52 import android.view.accessibility.AccessibilityEvent;
53 import android.view.accessibility.AccessibilityManager;
54 import android.view.accessibility.AccessibilityNodeInfo;
55 import android.view.animation.Interpolator;
56 import android.widget.EdgeEffect;
57 import android.widget.OverScroller;
58 
59 import com.android.internal.R;
60 import com.android.internal.annotations.VisibleForTesting;
61 import com.android.internal.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
62 
63 import java.lang.annotation.Retention;
64 import java.lang.annotation.RetentionPolicy;
65 import java.lang.ref.WeakReference;
66 import java.lang.reflect.Constructor;
67 import java.lang.reflect.InvocationTargetException;
68 import java.util.ArrayList;
69 import java.util.Collections;
70 import java.util.List;
71 
72 /**
73  * A flexible view for providing a limited window into a large data set.
74  *
75  * <h3>Glossary of terms:</h3>
76  *
77  * <ul>
78  *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
79  *     that represent items in a data set.</li>
80  *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
81  *     <li><em>Index:</em> The index of an attached child view as used in a call to
82  *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
83  *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
84  *     to a <em>position</em> within the adapter.</li>
85  *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
86  *     position may be placed in a cache for later reuse to display the same type of data again
87  *     later. This can drastically improve performance by skipping initial layout inflation
88  *     or construction.</li>
89  *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
90  *     state during layout. Scrap views may be reused without becoming fully detached
91  *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
92  *     by the adapter if the view was considered <em>dirty</em>.</li>
93  *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
94  *     being displayed.</li>
95  * </ul>
96  *
97  * <h4>Positions in RecyclerView:</h4>
98  * <p>
99  * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
100  * {@link LayoutManager} to be able to detect data set changes in batches during a layout
101  * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
102  * It also helps with performance because all view bindings happen at the same time and unnecessary
103  * bindings are avoided.
104  * <p>
105  * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
106  * <ul>
107  *     <li>layout position: Position of an item in the latest layout calculation. This is the
108  *     position from the LayoutManager's perspective.</li>
109  *     <li>adapter position: Position of an item in the adapter. This is the position from
110  *     the Adapter's perspective.</li>
111  * </ul>
112  * <p>
113  * These two positions are the same except the time between dispatching <code>adapter.notify*
114  * </code> events and calculating the updated layout.
115  * <p>
116  * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
117  * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
118  * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
119  * last layout calculation. You can rely on these positions to be consistent with what user is
120  * currently seeing on the screen. For example, if you have a list of items on the screen and user
121  * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
122  * is seeing.
123  * <p>
124  * The other set of position related methods are in the form of
125  * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
126  * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
127  * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
128  * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
129  * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
130  * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
131  * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
132  * <code>null</code> results from these methods.
133  * <p>
134  * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
135  * writing an {@link Adapter}, you probably want to use adapter positions.
136  */
137 public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
138 
139     static final String TAG = "RecyclerView";
140 
141     static final boolean DEBUG = false;
142 
143     private static final int[]  NESTED_SCROLLING_ATTRS = { android.R.attr.nestedScrollingEnabled };
144 
145     private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
146 
147     /**
148      * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
149      * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
150      * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
151      * recursively traverses itemView and invalidates display list for each ViewGroup that matches
152      * this criteria.
153      */
154     static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
155             || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
156     /**
157      * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
158      * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
159      * 0 when mode is unspecified.
160      */
161     static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
162 
163     static final boolean POST_UPDATES_ON_ANIMATION = Build.VERSION.SDK_INT >= 16;
164 
165     /**
166      * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
167      * RenderThread but before the next frame begins. We schedule prefetch work in this window.
168      */
169     private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
170 
171     /**
172      * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
173      * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
174      */
175     private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
176 
177     /**
178      * on API 15-, a focused child can still be considered a focused child of RV even after
179      * it's being removed or its focusable flag is set to false. This is because when this focused
180      * child is detached, the reference to this child is not removed in clearFocus. API 16 and above
181      * properly handle this case by calling ensureInputFocusOnFirstFocusable or rootViewRequestFocus
182      * to request focus on a new child, which will clear the focus on the old (detached) child as a
183      * side-effect.
184      */
185     private static final boolean IGNORE_DETACHED_FOCUSED_CHILD = Build.VERSION.SDK_INT <= 15;
186 
187     static final boolean DISPATCH_TEMP_DETACH = false;
188     public static final int HORIZONTAL = 0;
189     public static final int VERTICAL = 1;
190 
191     public static final int NO_POSITION = -1;
192     public static final long NO_ID = -1;
193     public static final int INVALID_TYPE = -1;
194 
195     /**
196      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
197      * that the RecyclerView should use the standard touch slop for smooth,
198      * continuous scrolling.
199      */
200     public static final int TOUCH_SLOP_DEFAULT = 0;
201 
202     /**
203      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
204      * that the RecyclerView should use the standard touch slop for scrolling
205      * widgets that snap to a page or other coarse-grained barrier.
206      */
207     public static final int TOUCH_SLOP_PAGING = 1;
208 
209     static final int MAX_SCROLL_DURATION = 2000;
210 
211     /**
212      * RecyclerView is calculating a scroll.
213      * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
214      * it. Try to avoid using EditText, focusable views or handle them with care.
215      */
216     static final String TRACE_SCROLL_TAG = "RV Scroll";
217 
218     /**
219      * OnLayout has been called by the View system.
220      * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
221      * update themselves directly. This will cause a full re-layout but when it happens via the
222      * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
223      */
224     private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
225 
226     /**
227      * NotifyDataSetChanged or equal has been called.
228      * If this is taking a long time, try sending granular notify adapter changes instead of just
229      * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
230      * might help.
231      */
232     private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
233 
234     /**
235      * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
236      * If this is taking a long time, you may have dispatched too many Adapter updates causing too
237      * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
238      * methods.
239      */
240     private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
241 
242     /**
243      * RecyclerView is rebinding a View.
244      * If this is taking a lot of time, consider optimizing your layout or make sure you are not
245      * doing extra operations in onBindViewHolder call.
246      */
247     static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
248 
249     /**
250      * RecyclerView is attempting to pre-populate off screen views.
251      */
252     static final String TRACE_PREFETCH_TAG = "RV Prefetch";
253 
254     /**
255      * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
256      * RecyclerView.
257      */
258     static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
259 
260     /**
261      * RecyclerView is creating a new View.
262      * If too many of these present in Systrace:
263      * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
264      * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
265      * > Adapter#onFailedToRecycleView(ViewHolder)})
266      *
267      * - There might be too many item view types.
268      * > Try merging them
269      *
270      * - There might be too many itemChange animations and not enough space in RecyclerPool.
271      * >Try increasing your pool size and item cache size.
272      */
273     static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
274     private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
275             new Class[]{Context.class, AttributeSet.class, int.class, int.class};
276 
277     private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
278 
279     final Recycler mRecycler = new Recycler();
280 
281     private SavedState mPendingSavedState;
282 
283     /**
284      * Handles adapter updates
285      */
286     AdapterHelper mAdapterHelper;
287 
288     /**
289      * Handles abstraction between LayoutManager children and RecyclerView children
290      */
291     ChildHelper mChildHelper;
292 
293     /**
294      * Keeps data about views to be used for animations
295      */
296     final ViewInfoStore mViewInfoStore = new ViewInfoStore();
297 
298     /**
299      * Prior to L, there is no way to query this variable which is why we override the setter and
300      * track it here.
301      */
302     boolean mClipToPadding;
303 
304     /**
305      * Note: this Runnable is only ever posted if:
306      * 1) We've been through first layout
307      * 2) We know we have a fixed size (mHasFixedSize)
308      * 3) We're attached
309      */
310     final Runnable mUpdateChildViewsRunnable = new Runnable() {
311         @Override
312         public void run() {
313             if (!mFirstLayoutComplete || isLayoutRequested()) {
314                 // a layout request will happen, we should not do layout here.
315                 return;
316             }
317             if (!mIsAttached) {
318                 requestLayout();
319                 // if we are not attached yet, mark us as requiring layout and skip
320                 return;
321             }
322             if (mLayoutFrozen) {
323                 mLayoutRequestEaten = true;
324                 return; //we'll process updates when ice age ends.
325             }
326             consumePendingUpdateOperations();
327         }
328     };
329 
330     final Rect mTempRect = new Rect();
331     private final Rect mTempRect2 = new Rect();
332     final RectF mTempRectF = new RectF();
333     Adapter mAdapter;
334     @VisibleForTesting LayoutManager mLayout;
335     RecyclerListener mRecyclerListener;
336     final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
337     private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
338             new ArrayList<>();
339     private OnItemTouchListener mActiveOnItemTouchListener;
340     boolean mIsAttached;
341     boolean mHasFixedSize;
342     @VisibleForTesting boolean mFirstLayoutComplete;
343 
344     // Counting lock to control whether we should ignore requestLayout calls from children or not.
345     private int mEatRequestLayout = 0;
346 
347     boolean mLayoutRequestEaten;
348     boolean mLayoutFrozen;
349     private boolean mIgnoreMotionEventTillDown;
350 
351     // binary OR of change events that were eaten during a layout or scroll.
352     private int mEatenAccessibilityChangeFlags;
353     boolean mAdapterUpdateDuringMeasure;
354 
355     private final AccessibilityManager mAccessibilityManager;
356     private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
357 
358     /**
359      * Set to true when an adapter data set changed notification is received.
360      * In that case, we cannot run any animations since we don't know what happened until layout.
361      *
362      * Attached items are invalid until next layout, at which point layout will animate/replace
363      * items as necessary, building up content from the (effectively) new adapter from scratch.
364      *
365      * Cached items must be discarded when setting this to true, so that the cache may be freely
366      * used by prefetching until the next layout occurs.
367      *
368      * @see #setDataSetChangedAfterLayout()
369      */
370     boolean mDataSetHasChangedAfterLayout = false;
371 
372     /**
373      * This variable is incremented during a dispatchLayout and/or scroll.
374      * Some methods should not be called during these periods (e.g. adapter data change).
375      * Doing so will create hard to find bugs so we better check it and throw an exception.
376      *
377      * @see #assertInLayoutOrScroll(String)
378      * @see #assertNotInLayoutOrScroll(String)
379      */
380     private int mLayoutOrScrollCounter = 0;
381 
382     /**
383      * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
384      * (for API compatibility).
385      * <p>
386      * It is a bad practice for a developer to update the data in a scroll callback since it is
387      * potentially called during a layout.
388      */
389     private int mDispatchScrollCounter = 0;
390 
391     private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
392 
393     ItemAnimator mItemAnimator = new DefaultItemAnimator();
394 
395     private static final int INVALID_POINTER = -1;
396 
397     /**
398      * The RecyclerView is not currently scrolling.
399      * @see #getScrollState()
400      */
401     public static final int SCROLL_STATE_IDLE = 0;
402 
403     /**
404      * The RecyclerView is currently being dragged by outside input such as user touch input.
405      * @see #getScrollState()
406      */
407     public static final int SCROLL_STATE_DRAGGING = 1;
408 
409     /**
410      * The RecyclerView is currently animating to a final position while not under
411      * outside control.
412      * @see #getScrollState()
413      */
414     public static final int SCROLL_STATE_SETTLING = 2;
415 
416     static final long FOREVER_NS = Long.MAX_VALUE;
417 
418     // Touch/scrolling handling
419 
420     private int mScrollState = SCROLL_STATE_IDLE;
421     private int mScrollPointerId = INVALID_POINTER;
422     private VelocityTracker mVelocityTracker;
423     private int mInitialTouchX;
424     private int mInitialTouchY;
425     private int mLastTouchX;
426     private int mLastTouchY;
427     private int mTouchSlop;
428     private OnFlingListener mOnFlingListener;
429     private final int mMinFlingVelocity;
430     private final int mMaxFlingVelocity;
431     // This value is used when handling generic motion events.
432     private float mScrollFactor = Float.MIN_VALUE;
433     private boolean mPreserveFocusAfterLayout = true;
434 
435     final ViewFlinger mViewFlinger = new ViewFlinger();
436 
437     GapWorker mGapWorker;
438     GapWorker.LayoutPrefetchRegistryImpl mPrefetchRegistry =
439             ALLOW_THREAD_GAP_WORK ? new GapWorker.LayoutPrefetchRegistryImpl() : null;
440 
441     final State mState = new State();
442 
443     private OnScrollListener mScrollListener;
444     private List<OnScrollListener> mScrollListeners;
445 
446     // For use in item animations
447     boolean mItemsAddedOrRemoved = false;
448     boolean mItemsChanged = false;
449     private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
450             new ItemAnimatorRestoreListener();
451     boolean mPostedAnimatorRunner = false;
452     RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
453     private ChildDrawingOrderCallback mChildDrawingOrderCallback;
454 
455     // simple array to keep min and max child position during a layout calculation
456     // preserved not to create a new one in each layout pass
457     private final int[] mMinMaxLayoutPositions = new int[2];
458 
459     private final int[] mScrollOffset = new int[2];
460     private final int[] mScrollConsumed = new int[2];
461     private final int[] mNestedOffsets = new int[2];
462 
463     /**
464      * These are views that had their a11y importance changed during a layout. We defer these events
465      * until the end of the layout because a11y service may make sync calls back to the RV while
466      * the View's state is undefined.
467      */
468     @VisibleForTesting
469     final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList();
470 
471     private Runnable mItemAnimatorRunner = new Runnable() {
472         @Override
473         public void run() {
474             if (mItemAnimator != null) {
475                 mItemAnimator.runPendingAnimations();
476             }
477             mPostedAnimatorRunner = false;
478         }
479     };
480 
481     static final Interpolator sQuinticInterpolator = new Interpolator() {
482         @Override
483         public float getInterpolation(float t) {
484             t -= 1.0f;
485             return t * t * t * t * t + 1.0f;
486         }
487     };
488 
489     /**
490      * The callback to convert view info diffs into animations.
491      */
492     private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
493             new ViewInfoStore.ProcessCallback() {
494         @Override
495         public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
496                 @Nullable ItemHolderInfo postInfo) {
497             mRecycler.unscrapView(viewHolder);
498             animateDisappearance(viewHolder, info, postInfo);
499         }
500         @Override
501         public void processAppeared(ViewHolder viewHolder,
502                 ItemHolderInfo preInfo, ItemHolderInfo info) {
503             animateAppearance(viewHolder, preInfo, info);
504         }
505 
506         @Override
507         public void processPersistent(ViewHolder viewHolder,
508                 @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
509             viewHolder.setIsRecyclable(false);
510             if (mDataSetHasChangedAfterLayout) {
511                 // since it was rebound, use change instead as we'll be mapping them from
512                 // stable ids. If stable ids were false, we would not be running any
513                 // animations
514                 if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo, postInfo)) {
515                     postAnimationRunner();
516                 }
517             } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
518                 postAnimationRunner();
519             }
520         }
521         @Override
522         public void unused(ViewHolder viewHolder) {
523             mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
524         }
525     };
526 
RecyclerView(Context context)527     public RecyclerView(Context context) {
528         this(context, null);
529     }
530 
RecyclerView(Context context, @Nullable AttributeSet attrs)531     public RecyclerView(Context context, @Nullable AttributeSet attrs) {
532         this(context, attrs, 0);
533     }
534 
RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle)535     public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
536         super(context, attrs, defStyle);
537         if (attrs != null) {
538             TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
539             mClipToPadding = a.getBoolean(0, true);
540             a.recycle();
541         } else {
542             mClipToPadding = true;
543         }
544         setScrollContainer(true);
545         setFocusableInTouchMode(true);
546 
547         final ViewConfiguration vc = ViewConfiguration.get(context);
548         mTouchSlop = vc.getScaledTouchSlop();
549         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
550         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
551         setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
552 
553         mItemAnimator.setListener(mItemAnimatorListener);
554         initAdapterManager();
555         initChildrenHelper();
556         // If not explicitly specified this view is important for accessibility.
557         if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
558             setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
559         }
560         mAccessibilityManager = (AccessibilityManager) getContext()
561                 .getSystemService(Context.ACCESSIBILITY_SERVICE);
562         setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
563         // Create the layoutManager if specified.
564 
565         boolean nestedScrollingEnabled = true;
566 
567         if (attrs != null) {
568             int defStyleRes = 0;
569             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
570                     defStyle, defStyleRes);
571             String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
572             int descendantFocusability = a.getInt(
573                     R.styleable.RecyclerView_descendantFocusability, -1);
574             if (descendantFocusability == -1) {
575                 setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
576             }
577             a.recycle();
578             createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
579 
580             if (Build.VERSION.SDK_INT >= 21) {
581                 a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
582                         defStyle, defStyleRes);
583                 nestedScrollingEnabled = a.getBoolean(0, true);
584                 a.recycle();
585             }
586         } else {
587             setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
588         }
589 
590         TypedArray a = context.obtainStyledAttributes(attrs,
591                 com.android.internal.R.styleable.EdgeEffect);
592         a.recycle();
593 
594         // Re-set whether nested scrolling is enabled so that it is set on all API levels
595         setNestedScrollingEnabled(nestedScrollingEnabled);
596     }
597 
598     /**
599      * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
600      * @return An instance of AccessibilityDelegateCompat used by RecyclerView
601      */
getCompatAccessibilityDelegate()602     public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
603         return mAccessibilityDelegate;
604     }
605 
606     /**
607      * Sets the accessibility delegate compatibility implementation used by RecyclerView.
608      * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
609      */
setAccessibilityDelegateCompat( RecyclerViewAccessibilityDelegate accessibilityDelegate)610     public void setAccessibilityDelegateCompat(
611             RecyclerViewAccessibilityDelegate accessibilityDelegate) {
612         mAccessibilityDelegate = accessibilityDelegate;
613         setAccessibilityDelegate(mAccessibilityDelegate);
614     }
615 
616     /**
617      * Instantiate and set a LayoutManager, if specified in the attributes.
618      */
createLayoutManager(Context context, String className, AttributeSet attrs, int defStyleAttr, int defStyleRes)619     private void createLayoutManager(Context context, String className, AttributeSet attrs,
620             int defStyleAttr, int defStyleRes) {
621         if (className != null) {
622             className = className.trim();
623             if (className.length() != 0) {  // Can't use isEmpty since it was added in API 9.
624                 className = getFullClassName(context, className);
625                 try {
626                     ClassLoader classLoader;
627                     if (isInEditMode()) {
628                         // Stupid layoutlib cannot handle simple class loaders.
629                         classLoader = this.getClass().getClassLoader();
630                     } else {
631                         classLoader = context.getClassLoader();
632                     }
633                     Class<? extends LayoutManager> layoutManagerClass =
634                             classLoader.loadClass(className).asSubclass(LayoutManager.class);
635                     Constructor<? extends LayoutManager> constructor;
636                     Object[] constructorArgs = null;
637                     try {
638                         constructor = layoutManagerClass
639                                 .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
640                         constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
641                     } catch (NoSuchMethodException e) {
642                         try {
643                             constructor = layoutManagerClass.getConstructor();
644                         } catch (NoSuchMethodException e1) {
645                             e1.initCause(e);
646                             throw new IllegalStateException(attrs.getPositionDescription()
647                                     + ": Error creating LayoutManager " + className, e1);
648                         }
649                     }
650                     constructor.setAccessible(true);
651                     setLayoutManager(constructor.newInstance(constructorArgs));
652                 } catch (ClassNotFoundException e) {
653                     throw new IllegalStateException(attrs.getPositionDescription()
654                             + ": Unable to find LayoutManager " + className, e);
655                 } catch (InvocationTargetException e) {
656                     throw new IllegalStateException(attrs.getPositionDescription()
657                             + ": Could not instantiate the LayoutManager: " + className, e);
658                 } catch (InstantiationException e) {
659                     throw new IllegalStateException(attrs.getPositionDescription()
660                             + ": Could not instantiate the LayoutManager: " + className, e);
661                 } catch (IllegalAccessException e) {
662                     throw new IllegalStateException(attrs.getPositionDescription()
663                             + ": Cannot access non-public constructor " + className, e);
664                 } catch (ClassCastException e) {
665                     throw new IllegalStateException(attrs.getPositionDescription()
666                             + ": Class is not a LayoutManager " + className, e);
667                 }
668             }
669         }
670     }
671 
getFullClassName(Context context, String className)672     private String getFullClassName(Context context, String className) {
673         if (className.charAt(0) == '.') {
674             return context.getPackageName() + className;
675         }
676         if (className.contains(".")) {
677             return className;
678         }
679         return RecyclerView.class.getPackage().getName() + '.' + className;
680     }
681 
initChildrenHelper()682     private void initChildrenHelper() {
683         mChildHelper = new ChildHelper(new ChildHelper.Callback() {
684             @Override
685             public int getChildCount() {
686                 return RecyclerView.this.getChildCount();
687             }
688 
689             @Override
690             public void addView(View child, int index) {
691                 RecyclerView.this.addView(child, index);
692                 dispatchChildAttached(child);
693             }
694 
695             @Override
696             public int indexOfChild(View view) {
697                 return RecyclerView.this.indexOfChild(view);
698             }
699 
700             @Override
701             public void removeViewAt(int index) {
702                 final View child = RecyclerView.this.getChildAt(index);
703                 if (child != null) {
704                     dispatchChildDetached(child);
705                 }
706                 RecyclerView.this.removeViewAt(index);
707             }
708 
709             @Override
710             public View getChildAt(int offset) {
711                 return RecyclerView.this.getChildAt(offset);
712             }
713 
714             @Override
715             public void removeAllViews() {
716                 final int count = getChildCount();
717                 for (int i = 0; i < count; i++) {
718                     dispatchChildDetached(getChildAt(i));
719                 }
720                 RecyclerView.this.removeAllViews();
721             }
722 
723             @Override
724             public ViewHolder getChildViewHolder(View view) {
725                 return getChildViewHolderInt(view);
726             }
727 
728             @Override
729             public void attachViewToParent(View child, int index,
730                     ViewGroup.LayoutParams layoutParams) {
731                 final ViewHolder vh = getChildViewHolderInt(child);
732                 if (vh != null) {
733                     if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
734                         throw new IllegalArgumentException("Called attach on a child which is not"
735                                 + " detached: " + vh);
736                     }
737                     if (DEBUG) {
738                         Log.d(TAG, "reAttach " + vh);
739                     }
740                     vh.clearTmpDetachFlag();
741                 }
742                 RecyclerView.this.attachViewToParent(child, index, layoutParams);
743             }
744 
745             @Override
746             public void detachViewFromParent(int offset) {
747                 final View view = getChildAt(offset);
748                 if (view != null) {
749                     final ViewHolder vh = getChildViewHolderInt(view);
750                     if (vh != null) {
751                         if (vh.isTmpDetached() && !vh.shouldIgnore()) {
752                             throw new IllegalArgumentException("called detach on an already"
753                                     + " detached child " + vh);
754                         }
755                         if (DEBUG) {
756                             Log.d(TAG, "tmpDetach " + vh);
757                         }
758                         vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
759                     }
760                 }
761                 RecyclerView.this.detachViewFromParent(offset);
762             }
763 
764             @Override
765             public void onEnteredHiddenState(View child) {
766                 final ViewHolder vh = getChildViewHolderInt(child);
767                 if (vh != null) {
768                     vh.onEnteredHiddenState(RecyclerView.this);
769                 }
770             }
771 
772             @Override
773             public void onLeftHiddenState(View child) {
774                 final ViewHolder vh = getChildViewHolderInt(child);
775                 if (vh != null) {
776                     vh.onLeftHiddenState(RecyclerView.this);
777                 }
778             }
779         });
780     }
781 
initAdapterManager()782     void initAdapterManager() {
783         mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
784             @Override
785             public ViewHolder findViewHolder(int position) {
786                 final ViewHolder vh = findViewHolderForPosition(position, true);
787                 if (vh == null) {
788                     return null;
789                 }
790                 // ensure it is not hidden because for adapter helper, the only thing matter is that
791                 // LM thinks view is a child.
792                 if (mChildHelper.isHidden(vh.itemView)) {
793                     if (DEBUG) {
794                         Log.d(TAG, "assuming view holder cannot be find because it is hidden");
795                     }
796                     return null;
797                 }
798                 return vh;
799             }
800 
801             @Override
802             public void offsetPositionsForRemovingInvisible(int start, int count) {
803                 offsetPositionRecordsForRemove(start, count, true);
804                 mItemsAddedOrRemoved = true;
805                 mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
806             }
807 
808             @Override
809             public void offsetPositionsForRemovingLaidOutOrNewView(
810                     int positionStart, int itemCount) {
811                 offsetPositionRecordsForRemove(positionStart, itemCount, false);
812                 mItemsAddedOrRemoved = true;
813             }
814 
815             @Override
816             public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
817                 viewRangeUpdate(positionStart, itemCount, payload);
818                 mItemsChanged = true;
819             }
820 
821             @Override
822             public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
823                 dispatchUpdate(op);
824             }
825 
826             void dispatchUpdate(AdapterHelper.UpdateOp op) {
827                 switch (op.cmd) {
828                     case AdapterHelper.UpdateOp.ADD:
829                         mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
830                         break;
831                     case AdapterHelper.UpdateOp.REMOVE:
832                         mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
833                         break;
834                     case AdapterHelper.UpdateOp.UPDATE:
835                         mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
836                                 op.payload);
837                         break;
838                     case AdapterHelper.UpdateOp.MOVE:
839                         mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
840                         break;
841                 }
842             }
843 
844             @Override
845             public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
846                 dispatchUpdate(op);
847             }
848 
849             @Override
850             public void offsetPositionsForAdd(int positionStart, int itemCount) {
851                 offsetPositionRecordsForInsert(positionStart, itemCount);
852                 mItemsAddedOrRemoved = true;
853             }
854 
855             @Override
856             public void offsetPositionsForMove(int from, int to) {
857                 offsetPositionRecordsForMove(from, to);
858                 // should we create mItemsMoved ?
859                 mItemsAddedOrRemoved = true;
860             }
861         });
862     }
863 
864     /**
865      * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
866      * size is not affected by the adapter contents. RecyclerView can still change its size based
867      * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
868      * size of its children or contents of its adapter (except the number of items in the adapter).
869      * <p>
870      * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
871      * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
872      *
873      * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
874      */
setHasFixedSize(boolean hasFixedSize)875     public void setHasFixedSize(boolean hasFixedSize) {
876         mHasFixedSize = hasFixedSize;
877     }
878 
879     /**
880      * @return true if the app has specified that changes in adapter content cannot change
881      * the size of the RecyclerView itself.
882      */
hasFixedSize()883     public boolean hasFixedSize() {
884         return mHasFixedSize;
885     }
886 
887     @Override
setClipToPadding(boolean clipToPadding)888     public void setClipToPadding(boolean clipToPadding) {
889         if (clipToPadding != mClipToPadding) {
890             invalidateGlows();
891         }
892         mClipToPadding = clipToPadding;
893         super.setClipToPadding(clipToPadding);
894         if (mFirstLayoutComplete) {
895             requestLayout();
896         }
897     }
898 
899     /**
900      * Returns whether this RecyclerView will clip its children to its padding, and resize (but
901      * not clip) any EdgeEffect to the padded region, if padding is present.
902      * <p>
903      * By default, children are clipped to the padding of their parent
904      * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
905      *
906      * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
907      *         clip) any EdgeEffect to the padded region, false otherwise.
908      *
909      * @attr name android:clipToPadding
910      */
911     @Override
getClipToPadding()912     public boolean getClipToPadding() {
913         return mClipToPadding;
914     }
915 
916     /**
917      * Configure the scrolling touch slop for a specific use case.
918      *
919      * Set up the RecyclerView's scrolling motion threshold based on common usages.
920      * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
921      *
922      * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
923      *                     the intended usage of this RecyclerView
924      */
setScrollingTouchSlop(int slopConstant)925     public void setScrollingTouchSlop(int slopConstant) {
926         final ViewConfiguration vc = ViewConfiguration.get(getContext());
927         switch (slopConstant) {
928             default:
929                 Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
930                         + slopConstant + "; using default value");
931                 // fall-through
932             case TOUCH_SLOP_DEFAULT:
933                 mTouchSlop = vc.getScaledTouchSlop();
934                 break;
935 
936             case TOUCH_SLOP_PAGING:
937                 mTouchSlop = vc.getScaledPagingTouchSlop();
938                 break;
939         }
940     }
941 
942     /**
943      * Swaps the current adapter with the provided one. It is similar to
944      * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
945      * {@link ViewHolder} and does not clear the RecycledViewPool.
946      * <p>
947      * Note that it still calls onAdapterChanged callbacks.
948      *
949      * @param adapter The new adapter to set, or null to set no adapter.
950      * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
951      *                                      Views. If adapters have stable ids and/or you want to
952      *                                      animate the disappearing views, you may prefer to set
953      *                                      this to false.
954      * @see #setAdapter(Adapter)
955      */
swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews)956     public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
957         // bail out if layout is frozen
958         setLayoutFrozen(false);
959         setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
960         setDataSetChangedAfterLayout();
961         requestLayout();
962     }
963     /**
964      * Set a new adapter to provide child views on demand.
965      * <p>
966      * When adapter is changed, all existing views are recycled back to the pool. If the pool has
967      * only one adapter, it will be cleared.
968      *
969      * @param adapter The new adapter to set, or null to set no adapter.
970      * @see #swapAdapter(Adapter, boolean)
971      */
setAdapter(Adapter adapter)972     public void setAdapter(Adapter adapter) {
973         // bail out if layout is frozen
974         setLayoutFrozen(false);
975         setAdapterInternal(adapter, false, true);
976         requestLayout();
977     }
978 
979     /**
980      * Removes and recycles all views - both those currently attached, and those in the Recycler.
981      */
removeAndRecycleViews()982     void removeAndRecycleViews() {
983         // end all running animations
984         if (mItemAnimator != null) {
985             mItemAnimator.endAnimations();
986         }
987         // Since animations are ended, mLayout.children should be equal to
988         // recyclerView.children. This may not be true if item animator's end does not work as
989         // expected. (e.g. not release children instantly). It is safer to use mLayout's child
990         // count.
991         if (mLayout != null) {
992             mLayout.removeAndRecycleAllViews(mRecycler);
993             mLayout.removeAndRecycleScrapInt(mRecycler);
994         }
995         // we should clear it here before adapters are swapped to ensure correct callbacks.
996         mRecycler.clear();
997     }
998 
999     /**
1000      * Replaces the current adapter with the new one and triggers listeners.
1001      * @param adapter The new adapter
1002      * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
1003      *                               item types with the current adapter (helps us avoid cache
1004      *                               invalidation).
1005      * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
1006      *                               compatibleWithPrevious is false, this parameter is ignored.
1007      */
setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews)1008     private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
1009             boolean removeAndRecycleViews) {
1010         if (mAdapter != null) {
1011             mAdapter.unregisterAdapterDataObserver(mObserver);
1012             mAdapter.onDetachedFromRecyclerView(this);
1013         }
1014         if (!compatibleWithPrevious || removeAndRecycleViews) {
1015             removeAndRecycleViews();
1016         }
1017         mAdapterHelper.reset();
1018         final Adapter oldAdapter = mAdapter;
1019         mAdapter = adapter;
1020         if (adapter != null) {
1021             adapter.registerAdapterDataObserver(mObserver);
1022             adapter.onAttachedToRecyclerView(this);
1023         }
1024         if (mLayout != null) {
1025             mLayout.onAdapterChanged(oldAdapter, mAdapter);
1026         }
1027         mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
1028         mState.mStructureChanged = true;
1029         markKnownViewsInvalid();
1030     }
1031 
1032     /**
1033      * Retrieves the previously set adapter or null if no adapter is set.
1034      *
1035      * @return The previously set adapter
1036      * @see #setAdapter(Adapter)
1037      */
getAdapter()1038     public Adapter getAdapter() {
1039         return mAdapter;
1040     }
1041 
1042     /**
1043      * Register a listener that will be notified whenever a child view is recycled.
1044      *
1045      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1046      * that a child view is no longer needed. If an application associates expensive
1047      * or heavyweight data with item views, this may be a good place to release
1048      * or free those resources.</p>
1049      *
1050      * @param listener Listener to register, or null to clear
1051      */
setRecyclerListener(RecyclerListener listener)1052     public void setRecyclerListener(RecyclerListener listener) {
1053         mRecyclerListener = listener;
1054     }
1055 
1056     /**
1057      * <p>Return the offset of the RecyclerView's text baseline from the its top
1058      * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
1059      * this method returns -1.</p>
1060      *
1061      * @return the offset of the baseline within the RecyclerView's bounds or -1
1062      *         if baseline alignment is not supported
1063      */
1064     @Override
getBaseline()1065     public int getBaseline() {
1066         if (mLayout != null) {
1067             return mLayout.getBaseline();
1068         } else {
1069             return super.getBaseline();
1070         }
1071     }
1072 
1073     /**
1074      * Register a listener that will be notified whenever a child view is attached to or detached
1075      * from RecyclerView.
1076      *
1077      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1078      * that a child view is no longer needed. If an application associates expensive
1079      * or heavyweight data with item views, this may be a good place to release
1080      * or free those resources.</p>
1081      *
1082      * @param listener Listener to register
1083      */
addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener)1084     public void addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1085         if (mOnChildAttachStateListeners == null) {
1086             mOnChildAttachStateListeners = new ArrayList<>();
1087         }
1088         mOnChildAttachStateListeners.add(listener);
1089     }
1090 
1091     /**
1092      * Removes the provided listener from child attached state listeners list.
1093      *
1094      * @param listener Listener to unregister
1095      */
removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener)1096     public void removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1097         if (mOnChildAttachStateListeners == null) {
1098             return;
1099         }
1100         mOnChildAttachStateListeners.remove(listener);
1101     }
1102 
1103     /**
1104      * Removes all listeners that were added via
1105      * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
1106      */
clearOnChildAttachStateChangeListeners()1107     public void clearOnChildAttachStateChangeListeners() {
1108         if (mOnChildAttachStateListeners != null) {
1109             mOnChildAttachStateListeners.clear();
1110         }
1111     }
1112 
1113     /**
1114      * Set the {@link LayoutManager} that this RecyclerView will use.
1115      *
1116      * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
1117      * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
1118      * layout arrangements for child views. These arrangements are controlled by the
1119      * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
1120      *
1121      * <p>Several default strategies are provided for common uses such as lists and grids.</p>
1122      *
1123      * @param layout LayoutManager to use
1124      */
setLayoutManager(LayoutManager layout)1125     public void setLayoutManager(LayoutManager layout) {
1126         if (layout == mLayout) {
1127             return;
1128         }
1129         stopScroll();
1130         // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
1131         // chance that LayoutManagers will re-use views.
1132         if (mLayout != null) {
1133             // end all running animations
1134             if (mItemAnimator != null) {
1135                 mItemAnimator.endAnimations();
1136             }
1137             mLayout.removeAndRecycleAllViews(mRecycler);
1138             mLayout.removeAndRecycleScrapInt(mRecycler);
1139             mRecycler.clear();
1140 
1141             if (mIsAttached) {
1142                 mLayout.dispatchDetachedFromWindow(this, mRecycler);
1143             }
1144             mLayout.setRecyclerView(null);
1145             mLayout = null;
1146         } else {
1147             mRecycler.clear();
1148         }
1149         // this is just a defensive measure for faulty item animators.
1150         mChildHelper.removeAllViewsUnfiltered();
1151         mLayout = layout;
1152         if (layout != null) {
1153             if (layout.mRecyclerView != null) {
1154                 throw new IllegalArgumentException("LayoutManager " + layout
1155                         + " is already attached to a RecyclerView: " + layout.mRecyclerView);
1156             }
1157             mLayout.setRecyclerView(this);
1158             if (mIsAttached) {
1159                 mLayout.dispatchAttachedToWindow(this);
1160             }
1161         }
1162         mRecycler.updateViewCacheSize();
1163         requestLayout();
1164     }
1165 
1166     /**
1167      * Set a {@link OnFlingListener} for this {@link RecyclerView}.
1168      * <p>
1169      * If the {@link OnFlingListener} is set then it will receive
1170      * calls to {@link #fling(int,int)} and will be able to intercept them.
1171      *
1172      * @param onFlingListener The {@link OnFlingListener} instance.
1173      */
setOnFlingListener(@ullable OnFlingListener onFlingListener)1174     public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
1175         mOnFlingListener = onFlingListener;
1176     }
1177 
1178     /**
1179      * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
1180      *
1181      * @return The {@link OnFlingListener} instance currently set (can be null).
1182      */
1183     @Nullable
getOnFlingListener()1184     public OnFlingListener getOnFlingListener() {
1185         return mOnFlingListener;
1186     }
1187 
1188     @Override
onSaveInstanceState()1189     protected Parcelable onSaveInstanceState() {
1190         SavedState state = new SavedState(super.onSaveInstanceState());
1191         if (mPendingSavedState != null) {
1192             state.copyFrom(mPendingSavedState);
1193         } else if (mLayout != null) {
1194             state.mLayoutState = mLayout.onSaveInstanceState();
1195         } else {
1196             state.mLayoutState = null;
1197         }
1198 
1199         return state;
1200     }
1201 
1202     @Override
onRestoreInstanceState(Parcelable state)1203     protected void onRestoreInstanceState(Parcelable state) {
1204         if (!(state instanceof SavedState)) {
1205             super.onRestoreInstanceState(state);
1206             return;
1207         }
1208 
1209         mPendingSavedState = (SavedState) state;
1210         super.onRestoreInstanceState(mPendingSavedState.getSuperState());
1211         if (mLayout != null && mPendingSavedState.mLayoutState != null) {
1212             mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
1213         }
1214     }
1215 
1216     /**
1217      * Override to prevent freezing of any views created by the adapter.
1218      */
1219     @Override
dispatchSaveInstanceState(SparseArray<Parcelable> container)1220     protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1221         dispatchFreezeSelfOnly(container);
1222     }
1223 
1224     /**
1225      * Override to prevent thawing of any views created by the adapter.
1226      */
1227     @Override
dispatchRestoreInstanceState(SparseArray<Parcelable> container)1228     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1229         dispatchThawSelfOnly(container);
1230     }
1231 
1232     /**
1233      * Adds a view to the animatingViews list.
1234      * mAnimatingViews holds the child views that are currently being kept around
1235      * purely for the purpose of being animated out of view. They are drawn as a regular
1236      * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
1237      * as they are managed separately from the regular child views.
1238      * @param viewHolder The ViewHolder to be removed
1239      */
addAnimatingView(ViewHolder viewHolder)1240     private void addAnimatingView(ViewHolder viewHolder) {
1241         final View view = viewHolder.itemView;
1242         final boolean alreadyParented = view.getParent() == this;
1243         mRecycler.unscrapView(getChildViewHolder(view));
1244         if (viewHolder.isTmpDetached()) {
1245             // re-attach
1246             mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
1247         } else if (!alreadyParented) {
1248             mChildHelper.addView(view, true);
1249         } else {
1250             mChildHelper.hide(view);
1251         }
1252     }
1253 
1254     /**
1255      * Removes a view from the animatingViews list.
1256      * @param view The view to be removed
1257      * @see #addAnimatingView(RecyclerView.ViewHolder)
1258      * @return true if an animating view is removed
1259      */
removeAnimatingView(View view)1260     boolean removeAnimatingView(View view) {
1261         eatRequestLayout();
1262         final boolean removed = mChildHelper.removeViewIfHidden(view);
1263         if (removed) {
1264             final ViewHolder viewHolder = getChildViewHolderInt(view);
1265             mRecycler.unscrapView(viewHolder);
1266             mRecycler.recycleViewHolderInternal(viewHolder);
1267             if (DEBUG) {
1268                 Log.d(TAG, "after removing animated view: " + view + ", " + this);
1269             }
1270         }
1271         // only clear request eaten flag if we removed the view.
1272         resumeRequestLayout(!removed);
1273         return removed;
1274     }
1275 
1276     /**
1277      * Return the {@link LayoutManager} currently responsible for
1278      * layout policy for this RecyclerView.
1279      *
1280      * @return The currently bound LayoutManager
1281      */
getLayoutManager()1282     public LayoutManager getLayoutManager() {
1283         return mLayout;
1284     }
1285 
1286     /**
1287      * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
1288      * if no pool is set for this view a new one will be created. See
1289      * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
1290      *
1291      * @return The pool used to store recycled item views for reuse.
1292      * @see #setRecycledViewPool(RecycledViewPool)
1293      */
getRecycledViewPool()1294     public RecycledViewPool getRecycledViewPool() {
1295         return mRecycler.getRecycledViewPool();
1296     }
1297 
1298     /**
1299      * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
1300      * This can be useful if you have multiple RecyclerViews with adapters that use the same
1301      * view types, for example if you have several data sets with the same kinds of item views
1302      * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
1303      *
1304      * @param pool Pool to set. If this parameter is null a new pool will be created and used.
1305      */
setRecycledViewPool(RecycledViewPool pool)1306     public void setRecycledViewPool(RecycledViewPool pool) {
1307         mRecycler.setRecycledViewPool(pool);
1308     }
1309 
1310     /**
1311      * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
1312      *
1313      * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
1314      *
1315      * @see {@link ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)}
1316      */
setViewCacheExtension(ViewCacheExtension extension)1317     public void setViewCacheExtension(ViewCacheExtension extension) {
1318         mRecycler.setViewCacheExtension(extension);
1319     }
1320 
1321     /**
1322      * Set the number of offscreen views to retain before adding them to the potentially shared
1323      * {@link #getRecycledViewPool() recycled view pool}.
1324      *
1325      * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
1326      * a LayoutManager to reuse those views unmodified without needing to return to the adapter
1327      * to rebind them.</p>
1328      *
1329      * @param size Number of views to cache offscreen before returning them to the general
1330      *             recycled view pool
1331      */
setItemViewCacheSize(int size)1332     public void setItemViewCacheSize(int size) {
1333         mRecycler.setViewCacheSize(size);
1334     }
1335 
1336     /**
1337      * Return the current scrolling state of the RecyclerView.
1338      *
1339      * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
1340      * {@link #SCROLL_STATE_SETTLING}
1341      */
getScrollState()1342     public int getScrollState() {
1343         return mScrollState;
1344     }
1345 
setScrollState(int state)1346     void setScrollState(int state) {
1347         if (state == mScrollState) {
1348             return;
1349         }
1350         if (DEBUG) {
1351             Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
1352                     new Exception());
1353         }
1354         mScrollState = state;
1355         if (state != SCROLL_STATE_SETTLING) {
1356             stopScrollersInternal();
1357         }
1358         dispatchOnScrollStateChanged(state);
1359     }
1360 
1361     /**
1362      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1363      * affect both measurement and drawing of individual item views.
1364      *
1365      * <p>Item decorations are ordered. Decorations placed earlier in the list will
1366      * be run/queried/drawn first for their effects on item views. Padding added to views
1367      * will be nested; a padding added by an earlier decoration will mean further
1368      * item decorations in the list will be asked to draw/pad within the previous decoration's
1369      * given area.</p>
1370      *
1371      * @param decor Decoration to add
1372      * @param index Position in the decoration chain to insert this decoration at. If this value
1373      *              is negative the decoration will be added at the end.
1374      */
addItemDecoration(ItemDecoration decor, int index)1375     public void addItemDecoration(ItemDecoration decor, int index) {
1376         if (mLayout != null) {
1377             mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
1378                     + " layout");
1379         }
1380         if (mItemDecorations.isEmpty()) {
1381             setWillNotDraw(false);
1382         }
1383         if (index < 0) {
1384             mItemDecorations.add(decor);
1385         } else {
1386             mItemDecorations.add(index, decor);
1387         }
1388         markItemDecorInsetsDirty();
1389         requestLayout();
1390     }
1391 
1392     /**
1393      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1394      * affect both measurement and drawing of individual item views.
1395      *
1396      * <p>Item decorations are ordered. Decorations placed earlier in the list will
1397      * be run/queried/drawn first for their effects on item views. Padding added to views
1398      * will be nested; a padding added by an earlier decoration will mean further
1399      * item decorations in the list will be asked to draw/pad within the previous decoration's
1400      * given area.</p>
1401      *
1402      * @param decor Decoration to add
1403      */
addItemDecoration(ItemDecoration decor)1404     public void addItemDecoration(ItemDecoration decor) {
1405         addItemDecoration(decor, -1);
1406     }
1407 
1408     /**
1409      * Remove an {@link ItemDecoration} from this RecyclerView.
1410      *
1411      * <p>The given decoration will no longer impact the measurement and drawing of
1412      * item views.</p>
1413      *
1414      * @param decor Decoration to remove
1415      * @see #addItemDecoration(ItemDecoration)
1416      */
removeItemDecoration(ItemDecoration decor)1417     public void removeItemDecoration(ItemDecoration decor) {
1418         if (mLayout != null) {
1419             mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
1420                     + " layout");
1421         }
1422         mItemDecorations.remove(decor);
1423         if (mItemDecorations.isEmpty()) {
1424             setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
1425         }
1426         markItemDecorInsetsDirty();
1427         requestLayout();
1428     }
1429 
1430     /**
1431      * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
1432      * <p>
1433      * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
1434      * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
1435      * true if childDrawingOrderCallback is not null, false otherwise.
1436      * <p>
1437      * Note that child drawing order may be overridden by View's elevation.
1438      *
1439      * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
1440      *                                  system.
1441      */
setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback)1442     public void setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback) {
1443         if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
1444             return;
1445         }
1446         mChildDrawingOrderCallback = childDrawingOrderCallback;
1447         setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
1448     }
1449 
1450     /**
1451      * Set a listener that will be notified of any changes in scroll state or position.
1452      *
1453      * @param listener Listener to set or null to clear
1454      *
1455      * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
1456      *             {@link #removeOnScrollListener(OnScrollListener)}
1457      */
1458     @Deprecated
setOnScrollListener(OnScrollListener listener)1459     public void setOnScrollListener(OnScrollListener listener) {
1460         mScrollListener = listener;
1461     }
1462 
1463     /**
1464      * Add a listener that will be notified of any changes in scroll state or position.
1465      *
1466      * <p>Components that add a listener should take care to remove it when finished.
1467      * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
1468      * to remove all attached listeners.</p>
1469      *
1470      * @param listener listener to set or null to clear
1471      */
addOnScrollListener(OnScrollListener listener)1472     public void addOnScrollListener(OnScrollListener listener) {
1473         if (mScrollListeners == null) {
1474             mScrollListeners = new ArrayList<>();
1475         }
1476         mScrollListeners.add(listener);
1477     }
1478 
1479     /**
1480      * Remove a listener that was notified of any changes in scroll state or position.
1481      *
1482      * @param listener listener to set or null to clear
1483      */
removeOnScrollListener(OnScrollListener listener)1484     public void removeOnScrollListener(OnScrollListener listener) {
1485         if (mScrollListeners != null) {
1486             mScrollListeners.remove(listener);
1487         }
1488     }
1489 
1490     /**
1491      * Remove all secondary listener that were notified of any changes in scroll state or position.
1492      */
clearOnScrollListeners()1493     public void clearOnScrollListeners() {
1494         if (mScrollListeners != null) {
1495             mScrollListeners.clear();
1496         }
1497     }
1498 
1499     /**
1500      * Convenience method to scroll to a certain position.
1501      *
1502      * RecyclerView does not implement scrolling logic, rather forwards the call to
1503      * {@link com.android.internal.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
1504      * @param position Scroll to this adapter position
1505      * @see com.android.internal.widget.RecyclerView.LayoutManager#scrollToPosition(int)
1506      */
scrollToPosition(int position)1507     public void scrollToPosition(int position) {
1508         if (mLayoutFrozen) {
1509             return;
1510         }
1511         stopScroll();
1512         if (mLayout == null) {
1513             Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
1514                     + "Call setLayoutManager with a non-null argument.");
1515             return;
1516         }
1517         mLayout.scrollToPosition(position);
1518         awakenScrollBars();
1519     }
1520 
jumpToPositionForSmoothScroller(int position)1521     void jumpToPositionForSmoothScroller(int position) {
1522         if (mLayout == null) {
1523             return;
1524         }
1525         mLayout.scrollToPosition(position);
1526         awakenScrollBars();
1527     }
1528 
1529     /**
1530      * Starts a smooth scroll to an adapter position.
1531      * <p>
1532      * To support smooth scrolling, you must override
1533      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
1534      * {@link SmoothScroller}.
1535      * <p>
1536      * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
1537      * provide a custom smooth scroll logic, override
1538      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
1539      * LayoutManager.
1540      *
1541      * @param position The adapter position to scroll to
1542      * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
1543      */
smoothScrollToPosition(int position)1544     public void smoothScrollToPosition(int position) {
1545         if (mLayoutFrozen) {
1546             return;
1547         }
1548         if (mLayout == null) {
1549             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
1550                     + "Call setLayoutManager with a non-null argument.");
1551             return;
1552         }
1553         mLayout.smoothScrollToPosition(this, mState, position);
1554     }
1555 
1556     @Override
scrollTo(int x, int y)1557     public void scrollTo(int x, int y) {
1558         Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
1559                 + "Use scrollToPosition instead");
1560     }
1561 
1562     @Override
scrollBy(int x, int y)1563     public void scrollBy(int x, int y) {
1564         if (mLayout == null) {
1565             Log.e(TAG, "Cannot scroll without a LayoutManager set. "
1566                     + "Call setLayoutManager with a non-null argument.");
1567             return;
1568         }
1569         if (mLayoutFrozen) {
1570             return;
1571         }
1572         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1573         final boolean canScrollVertical = mLayout.canScrollVertically();
1574         if (canScrollHorizontal || canScrollVertical) {
1575             scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
1576         }
1577     }
1578 
1579     /**
1580      * Helper method reflect data changes to the state.
1581      * <p>
1582      * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
1583      * but data actually changed.
1584      * <p>
1585      * This method consumes all deferred changes to avoid that case.
1586      */
consumePendingUpdateOperations()1587     void consumePendingUpdateOperations() {
1588         if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
1589             Trace.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1590             dispatchLayout();
1591             Trace.endSection();
1592             return;
1593         }
1594         if (!mAdapterHelper.hasPendingUpdates()) {
1595             return;
1596         }
1597 
1598         // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
1599         // of the visible items is affected and if not, just ignore the change.
1600         if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
1601                 .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
1602                         | AdapterHelper.UpdateOp.MOVE)) {
1603             Trace.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
1604             eatRequestLayout();
1605             onEnterLayoutOrScroll();
1606             mAdapterHelper.preProcess();
1607             if (!mLayoutRequestEaten) {
1608                 if (hasUpdatedView()) {
1609                     dispatchLayout();
1610                 } else {
1611                     // no need to layout, clean state
1612                     mAdapterHelper.consumePostponedUpdates();
1613                 }
1614             }
1615             resumeRequestLayout(true);
1616             onExitLayoutOrScroll();
1617             Trace.endSection();
1618         } else if (mAdapterHelper.hasPendingUpdates()) {
1619             Trace.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1620             dispatchLayout();
1621             Trace.endSection();
1622         }
1623     }
1624 
1625     /**
1626      * @return True if an existing view holder needs to be updated
1627      */
hasUpdatedView()1628     private boolean hasUpdatedView() {
1629         final int childCount = mChildHelper.getChildCount();
1630         for (int i = 0; i < childCount; i++) {
1631             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
1632             if (holder == null || holder.shouldIgnore()) {
1633                 continue;
1634             }
1635             if (holder.isUpdated()) {
1636                 return true;
1637             }
1638         }
1639         return false;
1640     }
1641 
1642     /**
1643      * Does not perform bounds checking. Used by internal methods that have already validated input.
1644      * <p>
1645      * It also reports any unused scroll request to the related EdgeEffect.
1646      *
1647      * @param x The amount of horizontal scroll request
1648      * @param y The amount of vertical scroll request
1649      * @param ev The originating MotionEvent, or null if not from a touch event.
1650      *
1651      * @return Whether any scroll was consumed in either direction.
1652      */
scrollByInternal(int x, int y, MotionEvent ev)1653     boolean scrollByInternal(int x, int y, MotionEvent ev) {
1654         int unconsumedX = 0, unconsumedY = 0;
1655         int consumedX = 0, consumedY = 0;
1656 
1657         consumePendingUpdateOperations();
1658         if (mAdapter != null) {
1659             eatRequestLayout();
1660             onEnterLayoutOrScroll();
1661             Trace.beginSection(TRACE_SCROLL_TAG);
1662             if (x != 0) {
1663                 consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
1664                 unconsumedX = x - consumedX;
1665             }
1666             if (y != 0) {
1667                 consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
1668                 unconsumedY = y - consumedY;
1669             }
1670             Trace.endSection();
1671             repositionShadowingViews();
1672             onExitLayoutOrScroll();
1673             resumeRequestLayout(false);
1674         }
1675         if (!mItemDecorations.isEmpty()) {
1676             invalidate();
1677         }
1678 
1679         if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
1680             // Update the last touch co-ords, taking any scroll offset into account
1681             mLastTouchX -= mScrollOffset[0];
1682             mLastTouchY -= mScrollOffset[1];
1683             if (ev != null) {
1684                 ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
1685             }
1686             mNestedOffsets[0] += mScrollOffset[0];
1687             mNestedOffsets[1] += mScrollOffset[1];
1688         } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
1689             if (ev != null) {
1690                 pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
1691             }
1692             considerReleasingGlowsOnScroll(x, y);
1693         }
1694         if (consumedX != 0 || consumedY != 0) {
1695             dispatchOnScrolled(consumedX, consumedY);
1696         }
1697         if (!awakenScrollBars()) {
1698             invalidate();
1699         }
1700         return consumedX != 0 || consumedY != 0;
1701     }
1702 
1703     /**
1704      * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
1705      * range. This value is used to compute the length of the thumb within the scrollbar's track.
1706      * </p>
1707      *
1708      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1709      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
1710      *
1711      * <p>Default implementation returns 0.</p>
1712      *
1713      * <p>If you want to support scroll bars, override
1714      * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
1715      * LayoutManager. </p>
1716      *
1717      * @return The horizontal offset of the scrollbar's thumb
1718      * @see com.android.internal.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset
1719      * (RecyclerView.State)
1720      */
1721     @Override
computeHorizontalScrollOffset()1722     public int computeHorizontalScrollOffset() {
1723         if (mLayout == null) {
1724             return 0;
1725         }
1726         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
1727     }
1728 
1729     /**
1730      * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
1731      * horizontal range. This value is used to compute the length of the thumb within the
1732      * scrollbar's track.</p>
1733      *
1734      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1735      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
1736      *
1737      * <p>Default implementation returns 0.</p>
1738      *
1739      * <p>If you want to support scroll bars, override
1740      * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
1741      * LayoutManager.</p>
1742      *
1743      * @return The horizontal extent of the scrollbar's thumb
1744      * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
1745      */
1746     @Override
computeHorizontalScrollExtent()1747     public int computeHorizontalScrollExtent() {
1748         if (mLayout == null) {
1749             return 0;
1750         }
1751         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
1752     }
1753 
1754     /**
1755      * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
1756      *
1757      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1758      * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
1759      *
1760      * <p>Default implementation returns 0.</p>
1761      *
1762      * <p>If you want to support scroll bars, override
1763      * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
1764      * LayoutManager.</p>
1765      *
1766      * @return The total horizontal range represented by the vertical scrollbar
1767      * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
1768      */
1769     @Override
computeHorizontalScrollRange()1770     public int computeHorizontalScrollRange() {
1771         if (mLayout == null) {
1772             return 0;
1773         }
1774         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
1775     }
1776 
1777     /**
1778      * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
1779      * This value is used to compute the length of the thumb within the scrollbar's track. </p>
1780      *
1781      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1782      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
1783      *
1784      * <p>Default implementation returns 0.</p>
1785      *
1786      * <p>If you want to support scroll bars, override
1787      * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
1788      * LayoutManager.</p>
1789      *
1790      * @return The vertical offset of the scrollbar's thumb
1791      * @see com.android.internal.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset
1792      * (RecyclerView.State)
1793      */
1794     @Override
computeVerticalScrollOffset()1795     public int computeVerticalScrollOffset() {
1796         if (mLayout == null) {
1797             return 0;
1798         }
1799         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
1800     }
1801 
1802     /**
1803      * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
1804      * This value is used to compute the length of the thumb within the scrollbar's track.</p>
1805      *
1806      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1807      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
1808      *
1809      * <p>Default implementation returns 0.</p>
1810      *
1811      * <p>If you want to support scroll bars, override
1812      * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
1813      * LayoutManager.</p>
1814      *
1815      * @return The vertical extent of the scrollbar's thumb
1816      * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
1817      */
1818     @Override
computeVerticalScrollExtent()1819     public int computeVerticalScrollExtent() {
1820         if (mLayout == null) {
1821             return 0;
1822         }
1823         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
1824     }
1825 
1826     /**
1827      * <p>Compute the vertical range that the vertical scrollbar represents.</p>
1828      *
1829      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1830      * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
1831      *
1832      * <p>Default implementation returns 0.</p>
1833      *
1834      * <p>If you want to support scroll bars, override
1835      * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
1836      * LayoutManager.</p>
1837      *
1838      * @return The total vertical range represented by the vertical scrollbar
1839      * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
1840      */
1841     @Override
computeVerticalScrollRange()1842     public int computeVerticalScrollRange() {
1843         if (mLayout == null) {
1844             return 0;
1845         }
1846         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
1847     }
1848 
1849 
eatRequestLayout()1850     void eatRequestLayout() {
1851         mEatRequestLayout++;
1852         if (mEatRequestLayout == 1 && !mLayoutFrozen) {
1853             mLayoutRequestEaten = false;
1854         }
1855     }
1856 
resumeRequestLayout(boolean performLayoutChildren)1857     void resumeRequestLayout(boolean performLayoutChildren) {
1858         if (mEatRequestLayout < 1) {
1859             //noinspection PointlessBooleanExpression
1860             if (DEBUG) {
1861                 throw new IllegalStateException("invalid eat request layout count");
1862             }
1863             mEatRequestLayout = 1;
1864         }
1865         if (!performLayoutChildren) {
1866             // Reset the layout request eaten counter.
1867             // This is necessary since eatRequest calls can be nested in which case the other
1868             // call will override the inner one.
1869             // for instance:
1870             // eat layout for process adapter updates
1871             //   eat layout for dispatchLayout
1872             //     a bunch of req layout calls arrive
1873 
1874             mLayoutRequestEaten = false;
1875         }
1876         if (mEatRequestLayout == 1) {
1877             // when layout is frozen we should delay dispatchLayout()
1878             if (performLayoutChildren && mLayoutRequestEaten && !mLayoutFrozen
1879                     && mLayout != null && mAdapter != null) {
1880                 dispatchLayout();
1881             }
1882             if (!mLayoutFrozen) {
1883                 mLayoutRequestEaten = false;
1884             }
1885         }
1886         mEatRequestLayout--;
1887     }
1888 
1889     /**
1890      * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
1891      * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
1892      * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
1893      * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
1894      * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
1895      * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
1896      * called.
1897      *
1898      * <p>
1899      * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
1900      * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
1901      * RecyclerView, State, int)}.
1902      * <p>
1903      * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
1904      * stop frozen.
1905      * <p>
1906      * Note: Running ItemAnimator is not stopped automatically,  it's caller's
1907      * responsibility to call ItemAnimator.end().
1908      *
1909      * @param frozen   true to freeze layout and scroll, false to re-enable.
1910      */
setLayoutFrozen(boolean frozen)1911     public void setLayoutFrozen(boolean frozen) {
1912         if (frozen != mLayoutFrozen) {
1913             assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
1914             if (!frozen) {
1915                 mLayoutFrozen = false;
1916                 if (mLayoutRequestEaten && mLayout != null && mAdapter != null) {
1917                     requestLayout();
1918                 }
1919                 mLayoutRequestEaten = false;
1920             } else {
1921                 final long now = SystemClock.uptimeMillis();
1922                 MotionEvent cancelEvent = MotionEvent.obtain(now, now,
1923                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
1924                 onTouchEvent(cancelEvent);
1925                 mLayoutFrozen = true;
1926                 mIgnoreMotionEventTillDown = true;
1927                 stopScroll();
1928             }
1929         }
1930     }
1931 
1932     /**
1933      * Returns true if layout and scroll are frozen.
1934      *
1935      * @return true if layout and scroll are frozen
1936      * @see #setLayoutFrozen(boolean)
1937      */
isLayoutFrozen()1938     public boolean isLayoutFrozen() {
1939         return mLayoutFrozen;
1940     }
1941 
1942     /**
1943      * Animate a scroll by the given amount of pixels along either axis.
1944      *
1945      * @param dx Pixels to scroll horizontally
1946      * @param dy Pixels to scroll vertically
1947      */
smoothScrollBy(int dx, int dy)1948     public void smoothScrollBy(int dx, int dy) {
1949         smoothScrollBy(dx, dy, null);
1950     }
1951 
1952     /**
1953      * Animate a scroll by the given amount of pixels along either axis.
1954      *
1955      * @param dx Pixels to scroll horizontally
1956      * @param dy Pixels to scroll vertically
1957      * @param interpolator {@link Interpolator} to be used for scrolling. If it is
1958      *                     {@code null}, RecyclerView is going to use the default interpolator.
1959      */
smoothScrollBy(int dx, int dy, Interpolator interpolator)1960     public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
1961         if (mLayout == null) {
1962             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
1963                     + "Call setLayoutManager with a non-null argument.");
1964             return;
1965         }
1966         if (mLayoutFrozen) {
1967             return;
1968         }
1969         if (!mLayout.canScrollHorizontally()) {
1970             dx = 0;
1971         }
1972         if (!mLayout.canScrollVertically()) {
1973             dy = 0;
1974         }
1975         if (dx != 0 || dy != 0) {
1976             mViewFlinger.smoothScrollBy(dx, dy, interpolator);
1977         }
1978     }
1979 
1980     /**
1981      * Begin a standard fling with an initial velocity along each axis in pixels per second.
1982      * If the velocity given is below the system-defined minimum this method will return false
1983      * and no fling will occur.
1984      *
1985      * @param velocityX Initial horizontal velocity in pixels per second
1986      * @param velocityY Initial vertical velocity in pixels per second
1987      * @return true if the fling was started, false if the velocity was too low to fling or
1988      * LayoutManager does not support scrolling in the axis fling is issued.
1989      *
1990      * @see LayoutManager#canScrollVertically()
1991      * @see LayoutManager#canScrollHorizontally()
1992      */
fling(int velocityX, int velocityY)1993     public boolean fling(int velocityX, int velocityY) {
1994         if (mLayout == null) {
1995             Log.e(TAG, "Cannot fling without a LayoutManager set. "
1996                     + "Call setLayoutManager with a non-null argument.");
1997             return false;
1998         }
1999         if (mLayoutFrozen) {
2000             return false;
2001         }
2002 
2003         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
2004         final boolean canScrollVertical = mLayout.canScrollVertically();
2005 
2006         if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
2007             velocityX = 0;
2008         }
2009         if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
2010             velocityY = 0;
2011         }
2012         if (velocityX == 0 && velocityY == 0) {
2013             // If we don't have any velocity, return false
2014             return false;
2015         }
2016 
2017         if (!dispatchNestedPreFling(velocityX, velocityY)) {
2018             boolean consumed = false;
2019             if (mLayout.getChildCount() > 0) {
2020                 final View firstChild = mLayout.getChildAt(0);
2021                 final View lastChild = mLayout.getChildAt(mLayout.getChildCount() - 1);
2022                 if (velocityY < 0) {
2023                     consumed = getChildAdapterPosition(firstChild) > 0
2024                             || firstChild.getTop() < getPaddingTop();
2025                 }
2026 
2027                 if (velocityY > 0) {
2028                     consumed = getChildAdapterPosition(lastChild) < mAdapter.getItemCount() - 1
2029                             || lastChild.getBottom() > getHeight() - getPaddingBottom();
2030                 }
2031             }
2032 
2033             dispatchNestedFling(velocityX, velocityY, consumed);
2034 
2035             if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
2036                 return true;
2037             }
2038 
2039             final boolean canScroll = canScrollHorizontal || canScrollVertical;
2040 
2041             if (canScroll) {
2042                 velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
2043                 velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
2044                 mViewFlinger.fling(velocityX, velocityY);
2045                 return true;
2046             }
2047         }
2048         return false;
2049     }
2050 
2051     /**
2052      * Stop any current scroll in progress, such as one started by
2053      * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
2054      */
stopScroll()2055     public void stopScroll() {
2056         setScrollState(SCROLL_STATE_IDLE);
2057         stopScrollersInternal();
2058     }
2059 
2060     /**
2061      * Similar to {@link #stopScroll()} but does not set the state.
2062      */
stopScrollersInternal()2063     private void stopScrollersInternal() {
2064         mViewFlinger.stop();
2065         if (mLayout != null) {
2066             mLayout.stopSmoothScroller();
2067         }
2068     }
2069 
2070     /**
2071      * Returns the minimum velocity to start a fling.
2072      *
2073      * @return The minimum velocity to start a fling
2074      */
getMinFlingVelocity()2075     public int getMinFlingVelocity() {
2076         return mMinFlingVelocity;
2077     }
2078 
2079 
2080     /**
2081      * Returns the maximum fling velocity used by this RecyclerView.
2082      *
2083      * @return The maximum fling velocity used by this RecyclerView.
2084      */
getMaxFlingVelocity()2085     public int getMaxFlingVelocity() {
2086         return mMaxFlingVelocity;
2087     }
2088 
2089     /**
2090      * Apply a pull to relevant overscroll glow effects
2091      */
pullGlows(float x, float overscrollX, float y, float overscrollY)2092     private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
2093         boolean invalidate = false;
2094         if (overscrollX < 0) {
2095             ensureLeftGlow();
2096             mLeftGlow.onPull(-overscrollX / getWidth(), 1f - y  / getHeight());
2097             invalidate = true;
2098         } else if (overscrollX > 0) {
2099             ensureRightGlow();
2100             mRightGlow.onPull(overscrollX / getWidth(), y / getHeight());
2101             invalidate = true;
2102         }
2103 
2104         if (overscrollY < 0) {
2105             ensureTopGlow();
2106             mTopGlow.onPull(-overscrollY / getHeight(), x / getWidth());
2107             invalidate = true;
2108         } else if (overscrollY > 0) {
2109             ensureBottomGlow();
2110             mBottomGlow.onPull(overscrollY / getHeight(), 1f - x / getWidth());
2111             invalidate = true;
2112         }
2113 
2114         if (invalidate || overscrollX != 0 || overscrollY != 0) {
2115             postInvalidateOnAnimation();
2116         }
2117     }
2118 
releaseGlows()2119     private void releaseGlows() {
2120         boolean needsInvalidate = false;
2121         if (mLeftGlow != null) {
2122             mLeftGlow.onRelease();
2123             needsInvalidate = true;
2124         }
2125         if (mTopGlow != null) {
2126             mTopGlow.onRelease();
2127             needsInvalidate = true;
2128         }
2129         if (mRightGlow != null) {
2130             mRightGlow.onRelease();
2131             needsInvalidate = true;
2132         }
2133         if (mBottomGlow != null) {
2134             mBottomGlow.onRelease();
2135             needsInvalidate = true;
2136         }
2137         if (needsInvalidate) {
2138             postInvalidateOnAnimation();
2139         }
2140     }
2141 
considerReleasingGlowsOnScroll(int dx, int dy)2142     void considerReleasingGlowsOnScroll(int dx, int dy) {
2143         boolean needsInvalidate = false;
2144         if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
2145             mLeftGlow.onRelease();
2146             needsInvalidate = true;
2147         }
2148         if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
2149             mRightGlow.onRelease();
2150             needsInvalidate = true;
2151         }
2152         if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
2153             mTopGlow.onRelease();
2154             needsInvalidate = true;
2155         }
2156         if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
2157             mBottomGlow.onRelease();
2158             needsInvalidate = true;
2159         }
2160         if (needsInvalidate) {
2161             postInvalidateOnAnimation();
2162         }
2163     }
2164 
absorbGlows(int velocityX, int velocityY)2165     void absorbGlows(int velocityX, int velocityY) {
2166         if (velocityX < 0) {
2167             ensureLeftGlow();
2168             mLeftGlow.onAbsorb(-velocityX);
2169         } else if (velocityX > 0) {
2170             ensureRightGlow();
2171             mRightGlow.onAbsorb(velocityX);
2172         }
2173 
2174         if (velocityY < 0) {
2175             ensureTopGlow();
2176             mTopGlow.onAbsorb(-velocityY);
2177         } else if (velocityY > 0) {
2178             ensureBottomGlow();
2179             mBottomGlow.onAbsorb(velocityY);
2180         }
2181 
2182         if (velocityX != 0 || velocityY != 0) {
2183             postInvalidateOnAnimation();
2184         }
2185     }
2186 
ensureLeftGlow()2187     void ensureLeftGlow() {
2188         if (mLeftGlow != null) {
2189             return;
2190         }
2191         mLeftGlow = new EdgeEffect(getContext());
2192         if (mClipToPadding) {
2193             mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2194                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2195         } else {
2196             mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2197         }
2198     }
2199 
ensureRightGlow()2200     void ensureRightGlow() {
2201         if (mRightGlow != null) {
2202             return;
2203         }
2204         mRightGlow = new EdgeEffect(getContext());
2205         if (mClipToPadding) {
2206             mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2207                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2208         } else {
2209             mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2210         }
2211     }
2212 
ensureTopGlow()2213     void ensureTopGlow() {
2214         if (mTopGlow != null) {
2215             return;
2216         }
2217         mTopGlow = new EdgeEffect(getContext());
2218         if (mClipToPadding) {
2219             mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2220                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2221         } else {
2222             mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2223         }
2224 
2225     }
2226 
ensureBottomGlow()2227     void ensureBottomGlow() {
2228         if (mBottomGlow != null) {
2229             return;
2230         }
2231         mBottomGlow = new EdgeEffect(getContext());
2232         if (mClipToPadding) {
2233             mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2234                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2235         } else {
2236             mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2237         }
2238     }
2239 
invalidateGlows()2240     void invalidateGlows() {
2241         mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
2242     }
2243 
2244     /**
2245      * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
2246      * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
2247      * that differs from other ViewGroups.
2248      * <p>
2249      * It first does a focus search within the RecyclerView. If this search finds a View that is in
2250      * the focus direction with respect to the currently focused View, RecyclerView returns that
2251      * child as the next focus target. When it cannot find such child, it calls
2252      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
2253      * in the focus search direction. If LayoutManager adds a View that matches the
2254      * focus search criteria, it will be returned as the focus search result. Otherwise,
2255      * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
2256      * <p>
2257      * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
2258      * is not in the focus direction is still valid focus target which may not be the desired
2259      * behavior if the Adapter has more children in the focus direction. To handle this case,
2260      * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
2261      * focus search in that direction. If there are no Views to gain focus, it will call
2262      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
2263      * focus search with the original (relative) direction. This allows RecyclerView to provide
2264      * better candidates to the focus search while still allowing the view system to take focus from
2265      * the RecyclerView and give it to a more suitable child if such child exists.
2266      *
2267      * @param focused The view that currently has focus
2268      * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
2269      * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
2270      * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
2271      *
2272      * @return A new View that can be the next focus after the focused View
2273      */
2274     @Override
focusSearch(View focused, int direction)2275     public View focusSearch(View focused, int direction) {
2276         View result = mLayout.onInterceptFocusSearch(focused, direction);
2277         if (result != null) {
2278             return result;
2279         }
2280         final boolean canRunFocusFailure = mAdapter != null && mLayout != null
2281                 && !isComputingLayout() && !mLayoutFrozen;
2282 
2283         final FocusFinder ff = FocusFinder.getInstance();
2284         if (canRunFocusFailure
2285                 && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
2286             // convert direction to absolute direction and see if we have a view there and if not
2287             // tell LayoutManager to add if it can.
2288             boolean needsFocusFailureLayout = false;
2289             if (mLayout.canScrollVertically()) {
2290                 final int absDir =
2291                         direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
2292                 final View found = ff.findNextFocus(this, focused, absDir);
2293                 needsFocusFailureLayout = found == null;
2294                 if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2295                     // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2296                     direction = absDir;
2297                 }
2298             }
2299             if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
2300                 boolean rtl = mLayout.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
2301                 final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
2302                         ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2303                 final View found = ff.findNextFocus(this, focused, absDir);
2304                 needsFocusFailureLayout = found == null;
2305                 if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2306                     // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2307                     direction = absDir;
2308                 }
2309             }
2310             if (needsFocusFailureLayout) {
2311                 consumePendingUpdateOperations();
2312                 final View focusedItemView = findContainingItemView(focused);
2313                 if (focusedItemView == null) {
2314                     // panic, focused view is not a child anymore, cannot call super.
2315                     return null;
2316                 }
2317                 eatRequestLayout();
2318                 mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2319                 resumeRequestLayout(false);
2320             }
2321             result = ff.findNextFocus(this, focused, direction);
2322         } else {
2323             result = ff.findNextFocus(this, focused, direction);
2324             if (result == null && canRunFocusFailure) {
2325                 consumePendingUpdateOperations();
2326                 final View focusedItemView = findContainingItemView(focused);
2327                 if (focusedItemView == null) {
2328                     // panic, focused view is not a child anymore, cannot call super.
2329                     return null;
2330                 }
2331                 eatRequestLayout();
2332                 result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2333                 resumeRequestLayout(false);
2334             }
2335         }
2336         return isPreferredNextFocus(focused, result, direction)
2337                 ? result : super.focusSearch(focused, direction);
2338     }
2339 
2340     /**
2341      * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
2342      * assign it as the next focus View instead of letting view hierarchy decide.
2343      * A good candidate means a View that is aligned in the focus direction wrt the focused View
2344      * and is not the RecyclerView itself.
2345      * When this method returns false, RecyclerView will let the parent make the decision so the
2346      * same View may still get the focus as a result of that search.
2347      */
isPreferredNextFocus(View focused, View next, int direction)2348     private boolean isPreferredNextFocus(View focused, View next, int direction) {
2349         if (next == null || next == this) {
2350             return false;
2351         }
2352         if (focused == null) {
2353             return true;
2354         }
2355 
2356         if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
2357             final boolean rtl = mLayout.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
2358             final int absHorizontal = (direction == View.FOCUS_FORWARD) ^ rtl
2359                     ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2360             if (isPreferredNextFocusAbsolute(focused, next, absHorizontal)) {
2361                 return true;
2362             }
2363             if (direction == View.FOCUS_FORWARD) {
2364                 return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_DOWN);
2365             } else {
2366                 return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_UP);
2367             }
2368         } else {
2369             return isPreferredNextFocusAbsolute(focused, next, direction);
2370         }
2371 
2372     }
2373 
2374     /**
2375      * Logic taken from FocusSearch#isCandidate
2376      */
isPreferredNextFocusAbsolute(View focused, View next, int direction)2377     private boolean isPreferredNextFocusAbsolute(View focused, View next, int direction) {
2378         mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2379         mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
2380         offsetDescendantRectToMyCoords(focused, mTempRect);
2381         offsetDescendantRectToMyCoords(next, mTempRect2);
2382         switch (direction) {
2383             case View.FOCUS_LEFT:
2384                 return (mTempRect.right > mTempRect2.right
2385                         || mTempRect.left >= mTempRect2.right)
2386                         && mTempRect.left > mTempRect2.left;
2387             case View.FOCUS_RIGHT:
2388                 return (mTempRect.left < mTempRect2.left
2389                         || mTempRect.right <= mTempRect2.left)
2390                         && mTempRect.right < mTempRect2.right;
2391             case View.FOCUS_UP:
2392                 return (mTempRect.bottom > mTempRect2.bottom
2393                         || mTempRect.top >= mTempRect2.bottom)
2394                         && mTempRect.top > mTempRect2.top;
2395             case View.FOCUS_DOWN:
2396                 return (mTempRect.top < mTempRect2.top
2397                         || mTempRect.bottom <= mTempRect2.top)
2398                         && mTempRect.bottom < mTempRect2.bottom;
2399         }
2400         throw new IllegalArgumentException("direction must be absolute. received:" + direction);
2401     }
2402 
2403     @Override
requestChildFocus(View child, View focused)2404     public void requestChildFocus(View child, View focused) {
2405         if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
2406             mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2407 
2408             // get item decor offsets w/o refreshing. If they are invalid, there will be another
2409             // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
2410             // View in viewport.
2411             final ViewGroup.LayoutParams focusedLayoutParams = focused.getLayoutParams();
2412             if (focusedLayoutParams instanceof LayoutParams) {
2413                 // if focused child has item decors, use them. Otherwise, ignore.
2414                 final LayoutParams lp = (LayoutParams) focusedLayoutParams;
2415                 if (!lp.mInsetsDirty) {
2416                     final Rect insets = lp.mDecorInsets;
2417                     mTempRect.left -= insets.left;
2418                     mTempRect.right += insets.right;
2419                     mTempRect.top -= insets.top;
2420                     mTempRect.bottom += insets.bottom;
2421                 }
2422             }
2423 
2424             offsetDescendantRectToMyCoords(focused, mTempRect);
2425             offsetRectIntoDescendantCoords(child, mTempRect);
2426             requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete);
2427         }
2428         super.requestChildFocus(child, focused);
2429     }
2430 
2431     @Override
requestChildRectangleOnScreen(View child, Rect rect, boolean immediate)2432     public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
2433         return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
2434     }
2435 
2436     @Override
addFocusables(ArrayList<View> views, int direction, int focusableMode)2437     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
2438         if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
2439             super.addFocusables(views, direction, focusableMode);
2440         }
2441     }
2442 
2443     @Override
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)2444     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
2445         if (isComputingLayout()) {
2446             // if we are in the middle of a layout calculation, don't let any child take focus.
2447             // RV will handle it after layout calculation is finished.
2448             return false;
2449         }
2450         return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
2451     }
2452 
2453     @Override
onAttachedToWindow()2454     protected void onAttachedToWindow() {
2455         super.onAttachedToWindow();
2456         mLayoutOrScrollCounter = 0;
2457         mIsAttached = true;
2458         mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
2459         if (mLayout != null) {
2460             mLayout.dispatchAttachedToWindow(this);
2461         }
2462         mPostedAnimatorRunner = false;
2463 
2464         if (ALLOW_THREAD_GAP_WORK) {
2465             // Register with gap worker
2466             mGapWorker = GapWorker.sGapWorker.get();
2467             if (mGapWorker == null) {
2468                 mGapWorker = new GapWorker();
2469 
2470                 // break 60 fps assumption if data from display appears valid
2471                 // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
2472                 Display display = getDisplay();
2473                 float refreshRate = 60.0f;
2474                 if (!isInEditMode() && display != null) {
2475                     float displayRefreshRate = display.getRefreshRate();
2476                     if (displayRefreshRate >= 30.0f) {
2477                         refreshRate = displayRefreshRate;
2478                     }
2479                 }
2480                 mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
2481                 GapWorker.sGapWorker.set(mGapWorker);
2482             }
2483             mGapWorker.add(this);
2484         }
2485     }
2486 
2487     @Override
onDetachedFromWindow()2488     protected void onDetachedFromWindow() {
2489         super.onDetachedFromWindow();
2490         if (mItemAnimator != null) {
2491             mItemAnimator.endAnimations();
2492         }
2493         stopScroll();
2494         mIsAttached = false;
2495         if (mLayout != null) {
2496             mLayout.dispatchDetachedFromWindow(this, mRecycler);
2497         }
2498         mPendingAccessibilityImportanceChange.clear();
2499         removeCallbacks(mItemAnimatorRunner);
2500         mViewInfoStore.onDetach();
2501 
2502         if (ALLOW_THREAD_GAP_WORK) {
2503             // Unregister with gap worker
2504             mGapWorker.remove(this);
2505             mGapWorker = null;
2506         }
2507     }
2508 
2509     /**
2510      * Returns true if RecyclerView is attached to window.
2511      */
2512     // @override
isAttachedToWindow()2513     public boolean isAttachedToWindow() {
2514         return mIsAttached;
2515     }
2516 
2517     /**
2518      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2519      * {@link IllegalStateException} if it <b>is not</b>.
2520      *
2521      * @param message The message for the exception. Can be null.
2522      * @see #assertNotInLayoutOrScroll(String)
2523      */
assertInLayoutOrScroll(String message)2524     void assertInLayoutOrScroll(String message) {
2525         if (!isComputingLayout()) {
2526             if (message == null) {
2527                 throw new IllegalStateException("Cannot call this method unless RecyclerView is "
2528                         + "computing a layout or scrolling");
2529             }
2530             throw new IllegalStateException(message);
2531 
2532         }
2533     }
2534 
2535     /**
2536      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2537      * {@link IllegalStateException} if it <b>is</b>.
2538      *
2539      * @param message The message for the exception. Can be null.
2540      * @see #assertInLayoutOrScroll(String)
2541      */
assertNotInLayoutOrScroll(String message)2542     void assertNotInLayoutOrScroll(String message) {
2543         if (isComputingLayout()) {
2544             if (message == null) {
2545                 throw new IllegalStateException("Cannot call this method while RecyclerView is "
2546                         + "computing a layout or scrolling");
2547             }
2548             throw new IllegalStateException(message);
2549         }
2550         if (mDispatchScrollCounter > 0) {
2551             Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might be run"
2552                     + " during a measure & layout pass where you cannot change the RecyclerView"
2553                     + " data. Any method call that might change the structure of the RecyclerView"
2554                     + " or the adapter contents should be postponed to the next frame.",
2555                     new IllegalStateException(""));
2556         }
2557     }
2558 
2559     /**
2560      * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
2561      * to child views or this view's standard scrolling behavior.
2562      *
2563      * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
2564      * returns true from
2565      * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
2566      * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
2567      * for each incoming MotionEvent until the end of the gesture.</p>
2568      *
2569      * @param listener Listener to add
2570      * @see SimpleOnItemTouchListener
2571      */
addOnItemTouchListener(OnItemTouchListener listener)2572     public void addOnItemTouchListener(OnItemTouchListener listener) {
2573         mOnItemTouchListeners.add(listener);
2574     }
2575 
2576     /**
2577      * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
2578      *
2579      * @param listener Listener to remove
2580      */
removeOnItemTouchListener(OnItemTouchListener listener)2581     public void removeOnItemTouchListener(OnItemTouchListener listener) {
2582         mOnItemTouchListeners.remove(listener);
2583         if (mActiveOnItemTouchListener == listener) {
2584             mActiveOnItemTouchListener = null;
2585         }
2586     }
2587 
dispatchOnItemTouchIntercept(MotionEvent e)2588     private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
2589         final int action = e.getAction();
2590         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
2591             mActiveOnItemTouchListener = null;
2592         }
2593 
2594         final int listenerCount = mOnItemTouchListeners.size();
2595         for (int i = 0; i < listenerCount; i++) {
2596             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2597             if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
2598                 mActiveOnItemTouchListener = listener;
2599                 return true;
2600             }
2601         }
2602         return false;
2603     }
2604 
dispatchOnItemTouch(MotionEvent e)2605     private boolean dispatchOnItemTouch(MotionEvent e) {
2606         final int action = e.getAction();
2607         if (mActiveOnItemTouchListener != null) {
2608             if (action == MotionEvent.ACTION_DOWN) {
2609                 // Stale state from a previous gesture, we're starting a new one. Clear it.
2610                 mActiveOnItemTouchListener = null;
2611             } else {
2612                 mActiveOnItemTouchListener.onTouchEvent(this, e);
2613                 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
2614                     // Clean up for the next gesture.
2615                     mActiveOnItemTouchListener = null;
2616                 }
2617                 return true;
2618             }
2619         }
2620 
2621         // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
2622         // as called from onInterceptTouchEvent; skip it.
2623         if (action != MotionEvent.ACTION_DOWN) {
2624             final int listenerCount = mOnItemTouchListeners.size();
2625             for (int i = 0; i < listenerCount; i++) {
2626                 final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2627                 if (listener.onInterceptTouchEvent(this, e)) {
2628                     mActiveOnItemTouchListener = listener;
2629                     return true;
2630                 }
2631             }
2632         }
2633         return false;
2634     }
2635 
2636     @Override
onInterceptTouchEvent(MotionEvent e)2637     public boolean onInterceptTouchEvent(MotionEvent e) {
2638         if (mLayoutFrozen) {
2639             // When layout is frozen,  RV does not intercept the motion event.
2640             // A child view e.g. a button may still get the click.
2641             return false;
2642         }
2643         if (dispatchOnItemTouchIntercept(e)) {
2644             cancelTouch();
2645             return true;
2646         }
2647 
2648         if (mLayout == null) {
2649             return false;
2650         }
2651 
2652         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2653         final boolean canScrollVertically = mLayout.canScrollVertically();
2654 
2655         if (mVelocityTracker == null) {
2656             mVelocityTracker = VelocityTracker.obtain();
2657         }
2658         mVelocityTracker.addMovement(e);
2659 
2660         final int action = e.getActionMasked();
2661         final int actionIndex = e.getActionIndex();
2662 
2663         switch (action) {
2664             case MotionEvent.ACTION_DOWN:
2665                 if (mIgnoreMotionEventTillDown) {
2666                     mIgnoreMotionEventTillDown = false;
2667                 }
2668                 mScrollPointerId = e.getPointerId(0);
2669                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2670                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2671 
2672                 if (stopGlowAnimations(e) || mScrollState == SCROLL_STATE_SETTLING) {
2673                     getParent().requestDisallowInterceptTouchEvent(true);
2674                     setScrollState(SCROLL_STATE_DRAGGING);
2675                 }
2676 
2677                 // Clear the nested offsets
2678                 mNestedOffsets[0] = mNestedOffsets[1] = 0;
2679 
2680                 int nestedScrollAxis = View.SCROLL_AXIS_NONE;
2681                 if (canScrollHorizontally) {
2682                     nestedScrollAxis |= View.SCROLL_AXIS_HORIZONTAL;
2683                 }
2684                 if (canScrollVertically) {
2685                     nestedScrollAxis |= View.SCROLL_AXIS_VERTICAL;
2686                 }
2687                 startNestedScroll(nestedScrollAxis);
2688                 break;
2689 
2690             case MotionEvent.ACTION_POINTER_DOWN:
2691                 mScrollPointerId = e.getPointerId(actionIndex);
2692                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2693                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2694                 break;
2695 
2696             case MotionEvent.ACTION_MOVE: {
2697                 final int index = e.findPointerIndex(mScrollPointerId);
2698                 if (index < 0) {
2699                     Log.e(TAG, "Error processing scroll; pointer index for id "
2700                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2701                     return false;
2702                 }
2703 
2704                 final int x = (int) (e.getX(index) + 0.5f);
2705                 final int y = (int) (e.getY(index) + 0.5f);
2706                 if (mScrollState != SCROLL_STATE_DRAGGING) {
2707                     final int dx = x - mInitialTouchX;
2708                     final int dy = y - mInitialTouchY;
2709                     boolean startScroll = false;
2710                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2711                         mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
2712                         startScroll = true;
2713                     }
2714                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2715                         mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
2716                         startScroll = true;
2717                     }
2718                     if (startScroll) {
2719                         setScrollState(SCROLL_STATE_DRAGGING);
2720                     }
2721                 }
2722             } break;
2723 
2724             case MotionEvent.ACTION_POINTER_UP: {
2725                 onPointerUp(e);
2726             } break;
2727 
2728             case MotionEvent.ACTION_UP: {
2729                 mVelocityTracker.clear();
2730                 stopNestedScroll();
2731             } break;
2732 
2733             case MotionEvent.ACTION_CANCEL: {
2734                 cancelTouch();
2735             }
2736         }
2737         return mScrollState == SCROLL_STATE_DRAGGING;
2738     }
2739 
2740     /**
2741      * This stops any edge glow animation that is currently running by applying a
2742      * 0 length pull at the displacement given by the provided MotionEvent. On pre-S devices,
2743      * this method does nothing, allowing any animating edge effect to continue animating and
2744      * returning <code>false</code> always.
2745      *
2746      * @param e The motion event to use to indicate the finger position for the displacement of
2747      *          the current pull.
2748      * @return <code>true</code> if any edge effect had an existing effect to be drawn ond the
2749      * animation was stopped or <code>false</code> if no edge effect had a value to display.
2750      */
stopGlowAnimations(MotionEvent e)2751     private boolean stopGlowAnimations(MotionEvent e) {
2752         boolean stopped = false;
2753         if (mLeftGlow != null && mLeftGlow.getDistance() != 0) {
2754             mLeftGlow.onPullDistance(0, 1 - (e.getY() / getHeight()));
2755             stopped = true;
2756         }
2757         if (mRightGlow != null && mRightGlow.getDistance() != 0) {
2758             mRightGlow.onPullDistance(0, e.getY() / getHeight());
2759             stopped = true;
2760         }
2761         if (mTopGlow != null && mTopGlow.getDistance() != 0) {
2762             mTopGlow.onPullDistance(0, e.getX() / getWidth());
2763             stopped = true;
2764         }
2765         if (mBottomGlow != null && mBottomGlow.getDistance() != 0) {
2766             mBottomGlow.onPullDistance(0, 1 - e.getX() / getWidth());
2767             stopped = true;
2768         }
2769         return stopped;
2770     }
2771 
2772     @Override
requestDisallowInterceptTouchEvent(boolean disallowIntercept)2773     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2774         final int listenerCount = mOnItemTouchListeners.size();
2775         for (int i = 0; i < listenerCount; i++) {
2776             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2777             listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
2778         }
2779         super.requestDisallowInterceptTouchEvent(disallowIntercept);
2780     }
2781 
2782     @Override
onTouchEvent(MotionEvent e)2783     public boolean onTouchEvent(MotionEvent e) {
2784         if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
2785             return false;
2786         }
2787         if (dispatchOnItemTouch(e)) {
2788             cancelTouch();
2789             return true;
2790         }
2791 
2792         if (mLayout == null) {
2793             return false;
2794         }
2795 
2796         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2797         final boolean canScrollVertically = mLayout.canScrollVertically();
2798 
2799         if (mVelocityTracker == null) {
2800             mVelocityTracker = VelocityTracker.obtain();
2801         }
2802         boolean eventAddedToVelocityTracker = false;
2803 
2804         final MotionEvent vtev = MotionEvent.obtain(e);
2805         final int action = e.getActionMasked();
2806         final int actionIndex = e.getActionIndex();
2807 
2808         if (action == MotionEvent.ACTION_DOWN) {
2809             mNestedOffsets[0] = mNestedOffsets[1] = 0;
2810         }
2811         vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
2812 
2813         switch (action) {
2814             case MotionEvent.ACTION_DOWN: {
2815                 mScrollPointerId = e.getPointerId(0);
2816                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2817                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2818 
2819                 int nestedScrollAxis = View.SCROLL_AXIS_NONE;
2820                 if (canScrollHorizontally) {
2821                     nestedScrollAxis |= View.SCROLL_AXIS_HORIZONTAL;
2822                 }
2823                 if (canScrollVertically) {
2824                     nestedScrollAxis |= View.SCROLL_AXIS_VERTICAL;
2825                 }
2826                 startNestedScroll(nestedScrollAxis);
2827             } break;
2828 
2829             case MotionEvent.ACTION_POINTER_DOWN: {
2830                 mScrollPointerId = e.getPointerId(actionIndex);
2831                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2832                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2833             } break;
2834 
2835             case MotionEvent.ACTION_MOVE: {
2836                 final int index = e.findPointerIndex(mScrollPointerId);
2837                 if (index < 0) {
2838                     Log.e(TAG, "Error processing scroll; pointer index for id "
2839                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2840                     vtev.recycle();
2841                     return false;
2842                 }
2843 
2844                 final int x = (int) (e.getX(index) + 0.5f);
2845                 final int y = (int) (e.getY(index) + 0.5f);
2846                 int dx = mLastTouchX - x;
2847                 int dy = mLastTouchY - y;
2848                 dx -= releaseHorizontalGlow(dx, e.getY());
2849                 dy -= releaseVerticalGlow(dy, e.getX());
2850 
2851                 if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
2852                     dx -= mScrollConsumed[0];
2853                     dy -= mScrollConsumed[1];
2854                     vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
2855                     // Updated the nested offsets
2856                     mNestedOffsets[0] += mScrollOffset[0];
2857                     mNestedOffsets[1] += mScrollOffset[1];
2858                 }
2859 
2860                 if (mScrollState != SCROLL_STATE_DRAGGING) {
2861                     boolean startScroll = false;
2862                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2863                         if (dx > 0) {
2864                             dx -= mTouchSlop;
2865                         } else {
2866                             dx += mTouchSlop;
2867                         }
2868                         startScroll = true;
2869                     }
2870                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2871                         if (dy > 0) {
2872                             dy -= mTouchSlop;
2873                         } else {
2874                             dy += mTouchSlop;
2875                         }
2876                         startScroll = true;
2877                     }
2878                     if (startScroll) {
2879                         setScrollState(SCROLL_STATE_DRAGGING);
2880                     }
2881                 }
2882 
2883                 if (mScrollState == SCROLL_STATE_DRAGGING) {
2884                     mLastTouchX = x - mScrollOffset[0];
2885                     mLastTouchY = y - mScrollOffset[1];
2886 
2887                     if (scrollByInternal(
2888                             canScrollHorizontally ? dx : 0,
2889                             canScrollVertically ? dy : 0,
2890                             vtev)) {
2891                         getParent().requestDisallowInterceptTouchEvent(true);
2892                     }
2893                     if (mGapWorker != null && (dx != 0 || dy != 0)) {
2894                         mGapWorker.postFromTraversal(this, dx, dy);
2895                     }
2896                 }
2897             } break;
2898 
2899             case MotionEvent.ACTION_POINTER_UP: {
2900                 onPointerUp(e);
2901             } break;
2902 
2903             case MotionEvent.ACTION_UP: {
2904                 mVelocityTracker.addMovement(vtev);
2905                 eventAddedToVelocityTracker = true;
2906                 mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
2907                 final float xvel = canScrollHorizontally
2908                         ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
2909                 final float yvel = canScrollVertically
2910                         ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
2911                 if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
2912                     setScrollState(SCROLL_STATE_IDLE);
2913                 }
2914                 resetTouch();
2915             } break;
2916 
2917             case MotionEvent.ACTION_CANCEL: {
2918                 cancelTouch();
2919             } break;
2920         }
2921 
2922         if (!eventAddedToVelocityTracker) {
2923             mVelocityTracker.addMovement(vtev);
2924         }
2925         vtev.recycle();
2926 
2927         return true;
2928     }
2929 
2930     /**
2931      * If either of the horizontal edge glows are currently active, this consumes part or all of
2932      * deltaX on the edge glow.
2933      *
2934      * @param deltaX The pointer motion, in pixels, in the horizontal direction, positive
2935      *                         for moving down and negative for moving up.
2936      * @param y The vertical position of the pointer.
2937      * @return The amount of <code>deltaX</code> that has been consumed by the
2938      * edge glow.
2939      */
releaseHorizontalGlow(int deltaX, float y)2940     private int releaseHorizontalGlow(int deltaX, float y) {
2941         // First allow releasing existing overscroll effect:
2942         float consumed = 0;
2943         float displacement = y / getHeight();
2944         float pullDistance = (float) deltaX / getWidth();
2945         if (mLeftGlow != null && mLeftGlow.getDistance() != 0) {
2946             consumed = -mLeftGlow.onPullDistance(-pullDistance, 1 - displacement);
2947             if (mLeftGlow.getDistance() == 0) {
2948                 mLeftGlow.onRelease();
2949             }
2950         } else if (mRightGlow != null && mRightGlow.getDistance() != 0) {
2951             consumed = mRightGlow.onPullDistance(pullDistance, displacement);
2952             if (mRightGlow.getDistance() == 0) {
2953                 mRightGlow.onRelease();
2954             }
2955         }
2956         int pixelsConsumed = Math.round(consumed * getWidth());
2957         if (pixelsConsumed != 0) {
2958             invalidate();
2959         }
2960         return pixelsConsumed;
2961     }
2962 
2963     /**
2964      * If either of the vertical edge glows are currently active, this consumes part or all of
2965      * deltaY on the edge glow.
2966      *
2967      * @param deltaY The pointer motion, in pixels, in the vertical direction, positive
2968      *                         for moving down and negative for moving up.
2969      * @param x The vertical position of the pointer.
2970      * @return The amount of <code>deltaY</code> that has been consumed by the
2971      * edge glow.
2972      */
releaseVerticalGlow(int deltaY, float x)2973     private int releaseVerticalGlow(int deltaY, float x) {
2974         // First allow releasing existing overscroll effect:
2975         float consumed = 0;
2976         float displacement = x / getWidth();
2977         float pullDistance = (float) deltaY / getHeight();
2978         if (mTopGlow != null && mTopGlow.getDistance() != 0) {
2979             consumed = -mTopGlow.onPullDistance(-pullDistance, displacement);
2980             if (mTopGlow.getDistance() == 0) {
2981                 mTopGlow.onRelease();
2982             }
2983         } else if (mBottomGlow != null && mBottomGlow.getDistance() != 0) {
2984             consumed = mBottomGlow.onPullDistance(pullDistance, 1 - displacement);
2985             if (mBottomGlow.getDistance() == 0) {
2986                 mBottomGlow.onRelease();
2987             }
2988         }
2989         int pixelsConsumed = Math.round(consumed * getHeight());
2990         if (pixelsConsumed != 0) {
2991             invalidate();
2992         }
2993         return pixelsConsumed;
2994     }
2995 
resetTouch()2996     private void resetTouch() {
2997         if (mVelocityTracker != null) {
2998             mVelocityTracker.clear();
2999         }
3000         stopNestedScroll();
3001         releaseGlows();
3002     }
3003 
cancelTouch()3004     private void cancelTouch() {
3005         resetTouch();
3006         setScrollState(SCROLL_STATE_IDLE);
3007     }
3008 
onPointerUp(MotionEvent e)3009     private void onPointerUp(MotionEvent e) {
3010         final int actionIndex = e.getActionIndex();
3011         if (e.getPointerId(actionIndex) == mScrollPointerId) {
3012             // Pick a new pointer to pick up the slack.
3013             final int newIndex = actionIndex == 0 ? 1 : 0;
3014             mScrollPointerId = e.getPointerId(newIndex);
3015             mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
3016             mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
3017         }
3018     }
3019 
3020     // @Override
onGenericMotionEvent(MotionEvent event)3021     public boolean onGenericMotionEvent(MotionEvent event) {
3022         if (mLayout == null) {
3023             return false;
3024         }
3025         if (mLayoutFrozen) {
3026             return false;
3027         }
3028         if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
3029             if (event.getAction() == MotionEvent.ACTION_SCROLL) {
3030                 final float vScroll, hScroll;
3031                 if (mLayout.canScrollVertically()) {
3032                     // Inverse the sign of the vertical scroll to align the scroll orientation
3033                     // with AbsListView.
3034                     vScroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
3035                 } else {
3036                     vScroll = 0f;
3037                 }
3038                 if (mLayout.canScrollHorizontally()) {
3039                     hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
3040                 } else {
3041                     hScroll = 0f;
3042                 }
3043 
3044                 if (vScroll != 0 || hScroll != 0) {
3045                     final float scrollFactor = getScrollFactor();
3046                     scrollByInternal((int) (hScroll * scrollFactor),
3047                             (int) (vScroll * scrollFactor), event);
3048                 }
3049             }
3050         }
3051         return false;
3052     }
3053 
3054     /**
3055      * Ported from View.getVerticalScrollFactor.
3056      */
getScrollFactor()3057     private float getScrollFactor() {
3058         if (mScrollFactor == Float.MIN_VALUE) {
3059             TypedValue outValue = new TypedValue();
3060             if (getContext().getTheme().resolveAttribute(
3061                     android.R.attr.listPreferredItemHeight, outValue, true)) {
3062                 mScrollFactor = outValue.getDimension(
3063                         getContext().getResources().getDisplayMetrics());
3064             } else {
3065                 return 0; //listPreferredItemHeight is not defined, no generic scrolling
3066             }
3067         }
3068         return mScrollFactor;
3069     }
3070 
3071     @Override
onMeasure(int widthSpec, int heightSpec)3072     protected void onMeasure(int widthSpec, int heightSpec) {
3073         if (mLayout == null) {
3074             defaultOnMeasure(widthSpec, heightSpec);
3075             return;
3076         }
3077         if (mLayout.mAutoMeasure) {
3078             final int widthMode = MeasureSpec.getMode(widthSpec);
3079             final int heightMode = MeasureSpec.getMode(heightSpec);
3080             final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
3081                     && heightMode == MeasureSpec.EXACTLY;
3082             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3083             if (skipMeasure || mAdapter == null) {
3084                 return;
3085             }
3086             if (mState.mLayoutStep == State.STEP_START) {
3087                 dispatchLayoutStep1();
3088             }
3089             // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
3090             // consistency
3091             mLayout.setMeasureSpecs(widthSpec, heightSpec);
3092             mState.mIsMeasuring = true;
3093             dispatchLayoutStep2();
3094 
3095             // now we can get the width and height from the children.
3096             mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
3097 
3098             // if RecyclerView has non-exact width and height and if there is at least one child
3099             // which also has non-exact width & height, we have to re-measure.
3100             if (mLayout.shouldMeasureTwice()) {
3101                 mLayout.setMeasureSpecs(
3102                         MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
3103                         MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
3104                 mState.mIsMeasuring = true;
3105                 dispatchLayoutStep2();
3106                 // now we can get the width and height from the children.
3107                 mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
3108             }
3109         } else {
3110             if (mHasFixedSize) {
3111                 mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3112                 return;
3113             }
3114             // custom onMeasure
3115             if (mAdapterUpdateDuringMeasure) {
3116                 eatRequestLayout();
3117                 onEnterLayoutOrScroll();
3118                 processAdapterUpdatesAndSetAnimationFlags();
3119                 onExitLayoutOrScroll();
3120 
3121                 if (mState.mRunPredictiveAnimations) {
3122                     mState.mInPreLayout = true;
3123                 } else {
3124                     // consume remaining updates to provide a consistent state with the layout pass.
3125                     mAdapterHelper.consumeUpdatesInOnePass();
3126                     mState.mInPreLayout = false;
3127                 }
3128                 mAdapterUpdateDuringMeasure = false;
3129                 resumeRequestLayout(false);
3130             }
3131 
3132             if (mAdapter != null) {
3133                 mState.mItemCount = mAdapter.getItemCount();
3134             } else {
3135                 mState.mItemCount = 0;
3136             }
3137             eatRequestLayout();
3138             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3139             resumeRequestLayout(false);
3140             mState.mInPreLayout = false; // clear
3141         }
3142     }
3143 
3144     /**
3145      * Used when onMeasure is called before layout manager is set
3146      */
defaultOnMeasure(int widthSpec, int heightSpec)3147     void defaultOnMeasure(int widthSpec, int heightSpec) {
3148         // calling LayoutManager here is not pretty but that API is already public and it is better
3149         // than creating another method since this is internal.
3150         final int width = LayoutManager.chooseSize(widthSpec,
3151                 getPaddingLeft() + getPaddingRight(),
3152                 getMinimumWidth());
3153         final int height = LayoutManager.chooseSize(heightSpec,
3154                 getPaddingTop() + getPaddingBottom(),
3155                 getMinimumHeight());
3156 
3157         setMeasuredDimension(width, height);
3158     }
3159 
3160     @Override
onSizeChanged(int w, int h, int oldw, int oldh)3161     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
3162         super.onSizeChanged(w, h, oldw, oldh);
3163         if (w != oldw || h != oldh) {
3164             invalidateGlows();
3165             // layout's w/h are updated during measure/layout steps.
3166         }
3167     }
3168 
3169     /**
3170      * Sets the {@link ItemAnimator} that will handle animations involving changes
3171      * to the items in this RecyclerView. By default, RecyclerView instantiates and
3172      * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
3173      * enabled for the RecyclerView depends on the ItemAnimator and whether
3174      * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
3175      * supports item animations}.
3176      *
3177      * @param animator The ItemAnimator being set. If null, no animations will occur
3178      * when changes occur to the items in this RecyclerView.
3179      */
setItemAnimator(ItemAnimator animator)3180     public void setItemAnimator(ItemAnimator animator) {
3181         if (mItemAnimator != null) {
3182             mItemAnimator.endAnimations();
3183             mItemAnimator.setListener(null);
3184         }
3185         mItemAnimator = animator;
3186         if (mItemAnimator != null) {
3187             mItemAnimator.setListener(mItemAnimatorListener);
3188         }
3189     }
3190 
onEnterLayoutOrScroll()3191     void onEnterLayoutOrScroll() {
3192         mLayoutOrScrollCounter++;
3193     }
3194 
onExitLayoutOrScroll()3195     void onExitLayoutOrScroll() {
3196         mLayoutOrScrollCounter--;
3197         if (mLayoutOrScrollCounter < 1) {
3198             if (DEBUG && mLayoutOrScrollCounter < 0) {
3199                 throw new IllegalStateException("layout or scroll counter cannot go below zero."
3200                         + "Some calls are not matching");
3201             }
3202             mLayoutOrScrollCounter = 0;
3203             dispatchContentChangedIfNecessary();
3204             dispatchPendingImportantForAccessibilityChanges();
3205         }
3206     }
3207 
isAccessibilityEnabled()3208     boolean isAccessibilityEnabled() {
3209         return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
3210     }
3211 
dispatchContentChangedIfNecessary()3212     private void dispatchContentChangedIfNecessary() {
3213         final int flags = mEatenAccessibilityChangeFlags;
3214         mEatenAccessibilityChangeFlags = 0;
3215         if (flags != 0 && isAccessibilityEnabled()) {
3216             final AccessibilityEvent event = AccessibilityEvent.obtain();
3217             event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
3218             event.setContentChangeTypes(flags);
3219             sendAccessibilityEventUnchecked(event);
3220         }
3221     }
3222 
3223     /**
3224      * Returns whether RecyclerView is currently computing a layout.
3225      * <p>
3226      * If this method returns true, it means that RecyclerView is in a lockdown state and any
3227      * attempt to update adapter contents will result in an exception because adapter contents
3228      * cannot be changed while RecyclerView is trying to compute the layout.
3229      * <p>
3230      * It is very unlikely that your code will be running during this state as it is
3231      * called by the framework when a layout traversal happens or RecyclerView starts to scroll
3232      * in response to system events (touch, accessibility etc).
3233      * <p>
3234      * This case may happen if you have some custom logic to change adapter contents in
3235      * response to a View callback (e.g. focus change callback) which might be triggered during a
3236      * layout calculation. In these cases, you should just postpone the change using a Handler or a
3237      * similar mechanism.
3238      *
3239      * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
3240      *         otherwise
3241      */
isComputingLayout()3242     public boolean isComputingLayout() {
3243         return mLayoutOrScrollCounter > 0;
3244     }
3245 
3246     /**
3247      * Returns true if an accessibility event should not be dispatched now. This happens when an
3248      * accessibility request arrives while RecyclerView does not have a stable state which is very
3249      * hard to handle for a LayoutManager. Instead, this method records necessary information about
3250      * the event and dispatches a window change event after the critical section is finished.
3251      *
3252      * @return True if the accessibility event should be postponed.
3253      */
shouldDeferAccessibilityEvent(AccessibilityEvent event)3254     boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
3255         if (isComputingLayout()) {
3256             int type = 0;
3257             if (event != null) {
3258                 type = event.getContentChangeTypes();
3259             }
3260             if (type == 0) {
3261                 type = AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
3262             }
3263             mEatenAccessibilityChangeFlags |= type;
3264             return true;
3265         }
3266         return false;
3267     }
3268 
3269     @Override
sendAccessibilityEventUnchecked(AccessibilityEvent event)3270     public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
3271         if (shouldDeferAccessibilityEvent(event)) {
3272             return;
3273         }
3274         super.sendAccessibilityEventUnchecked(event);
3275     }
3276 
3277     /**
3278      * Gets the current ItemAnimator for this RecyclerView. A null return value
3279      * indicates that there is no animator and that item changes will happen without
3280      * any animations. By default, RecyclerView instantiates and
3281      * uses an instance of {@link DefaultItemAnimator}.
3282      *
3283      * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
3284      * when changes occur to the items in this RecyclerView.
3285      */
getItemAnimator()3286     public ItemAnimator getItemAnimator() {
3287         return mItemAnimator;
3288     }
3289 
3290     /**
3291      * Post a runnable to the next frame to run pending item animations. Only the first such
3292      * request will be posted, governed by the mPostedAnimatorRunner flag.
3293      */
postAnimationRunner()3294     void postAnimationRunner() {
3295         if (!mPostedAnimatorRunner && mIsAttached) {
3296             postOnAnimation(mItemAnimatorRunner);
3297             mPostedAnimatorRunner = true;
3298         }
3299     }
3300 
predictiveItemAnimationsEnabled()3301     private boolean predictiveItemAnimationsEnabled() {
3302         return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
3303     }
3304 
3305     /**
3306      * Consumes adapter updates and calculates which type of animations we want to run.
3307      * Called in onMeasure and dispatchLayout.
3308      * <p>
3309      * This method may process only the pre-layout state of updates or all of them.
3310      */
processAdapterUpdatesAndSetAnimationFlags()3311     private void processAdapterUpdatesAndSetAnimationFlags() {
3312         if (mDataSetHasChangedAfterLayout) {
3313             // Processing these items have no value since data set changed unexpectedly.
3314             // Instead, we just reset it.
3315             mAdapterHelper.reset();
3316             mLayout.onItemsChanged(this);
3317         }
3318         // simple animations are a subset of advanced animations (which will cause a
3319         // pre-layout step)
3320         // If layout supports predictive animations, pre-process to decide if we want to run them
3321         if (predictiveItemAnimationsEnabled()) {
3322             mAdapterHelper.preProcess();
3323         } else {
3324             mAdapterHelper.consumeUpdatesInOnePass();
3325         }
3326         boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
3327         mState.mRunSimpleAnimations = mFirstLayoutComplete
3328                 && mItemAnimator != null
3329                 && (mDataSetHasChangedAfterLayout
3330                         || animationTypeSupported
3331                         || mLayout.mRequestedSimpleAnimations)
3332                 && (!mDataSetHasChangedAfterLayout
3333                         || mAdapter.hasStableIds());
3334         mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
3335                 && animationTypeSupported
3336                 && !mDataSetHasChangedAfterLayout
3337                 && predictiveItemAnimationsEnabled();
3338     }
3339 
3340     /**
3341      * Wrapper around layoutChildren() that handles animating changes caused by layout.
3342      * Animations work on the assumption that there are five different kinds of items
3343      * in play:
3344      * PERSISTENT: items are visible before and after layout
3345      * REMOVED: items were visible before layout and were removed by the app
3346      * ADDED: items did not exist before layout and were added by the app
3347      * DISAPPEARING: items exist in the data set before/after, but changed from
3348      * visible to non-visible in the process of layout (they were moved off
3349      * screen as a side-effect of other changes)
3350      * APPEARING: items exist in the data set before/after, but changed from
3351      * non-visible to visible in the process of layout (they were moved on
3352      * screen as a side-effect of other changes)
3353      * The overall approach figures out what items exist before/after layout and
3354      * infers one of the five above states for each of the items. Then the animations
3355      * are set up accordingly:
3356      * PERSISTENT views are animated via
3357      * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3358      * DISAPPEARING views are animated via
3359      * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3360      * APPEARING views are animated via
3361      * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3362      * and changed views are animated via
3363      * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
3364      */
dispatchLayout()3365     void dispatchLayout() {
3366         if (mAdapter == null) {
3367             Log.e(TAG, "No adapter attached; skipping layout");
3368             // leave the state in START
3369             return;
3370         }
3371         if (mLayout == null) {
3372             Log.e(TAG, "No layout manager attached; skipping layout");
3373             // leave the state in START
3374             return;
3375         }
3376         mState.mIsMeasuring = false;
3377         if (mState.mLayoutStep == State.STEP_START) {
3378             dispatchLayoutStep1();
3379             mLayout.setExactMeasureSpecsFrom(this);
3380             dispatchLayoutStep2();
3381         } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
3382                 || mLayout.getHeight() != getHeight()) {
3383             // First 2 steps are done in onMeasure but looks like we have to run again due to
3384             // changed size.
3385             mLayout.setExactMeasureSpecsFrom(this);
3386             dispatchLayoutStep2();
3387         } else {
3388             // always make sure we sync them (to ensure mode is exact)
3389             mLayout.setExactMeasureSpecsFrom(this);
3390         }
3391         dispatchLayoutStep3();
3392     }
3393 
saveFocusInfo()3394     private void saveFocusInfo() {
3395         View child = null;
3396         if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
3397             child = getFocusedChild();
3398         }
3399 
3400         final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
3401         if (focusedVh == null) {
3402             resetFocusInfo();
3403         } else {
3404             mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
3405             // mFocusedItemPosition should hold the current adapter position of the previously
3406             // focused item. If the item is removed, we store the previous adapter position of the
3407             // removed item.
3408             mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION
3409                     : (focusedVh.isRemoved() ? focusedVh.mOldPosition
3410                             : focusedVh.getAdapterPosition());
3411             mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
3412         }
3413     }
3414 
resetFocusInfo()3415     private void resetFocusInfo() {
3416         mState.mFocusedItemId = NO_ID;
3417         mState.mFocusedItemPosition = NO_POSITION;
3418         mState.mFocusedSubChildId = View.NO_ID;
3419     }
3420 
3421     /**
3422      * Finds the best view candidate to request focus on using mFocusedItemPosition index of the
3423      * previously focused item. It first traverses the adapter forward to find a focusable candidate
3424      * and if no such candidate is found, it reverses the focus search direction for the items
3425      * before the mFocusedItemPosition'th index;
3426      * @return The best candidate to request focus on, or null if no such candidate exists. Null
3427      * indicates all the existing adapter items are unfocusable.
3428      */
3429     @Nullable
findNextViewToFocus()3430     private View findNextViewToFocus() {
3431         int startFocusSearchIndex = mState.mFocusedItemPosition != -1 ? mState.mFocusedItemPosition
3432                 : 0;
3433         ViewHolder nextFocus;
3434         final int itemCount = mState.getItemCount();
3435         for (int i = startFocusSearchIndex; i < itemCount; i++) {
3436             nextFocus = findViewHolderForAdapterPosition(i);
3437             if (nextFocus == null) {
3438                 break;
3439             }
3440             if (nextFocus.itemView.hasFocusable()) {
3441                 return nextFocus.itemView;
3442             }
3443         }
3444         final int limit = Math.min(itemCount, startFocusSearchIndex);
3445         for (int i = limit - 1; i >= 0; i--) {
3446             nextFocus = findViewHolderForAdapterPosition(i);
3447             if (nextFocus == null) {
3448                 return null;
3449             }
3450             if (nextFocus.itemView.hasFocusable()) {
3451                 return nextFocus.itemView;
3452             }
3453         }
3454         return null;
3455     }
3456 
recoverFocusFromState()3457     private void recoverFocusFromState() {
3458         if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()
3459                 || getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
3460                 || (getDescendantFocusability() == FOCUS_BEFORE_DESCENDANTS && isFocused())) {
3461             // No-op if either of these cases happens:
3462             // 1. RV has no focus, or 2. RV blocks focus to its children, or 3. RV takes focus
3463             // before its children and is focused (i.e. it already stole the focus away from its
3464             // descendants).
3465             return;
3466         }
3467         // only recover focus if RV itself has the focus or the focused view is hidden
3468         if (!isFocused()) {
3469             final View focusedChild = getFocusedChild();
3470             if (IGNORE_DETACHED_FOCUSED_CHILD
3471                     && (focusedChild.getParent() == null || !focusedChild.hasFocus())) {
3472                 // Special handling of API 15-. A focused child can be invalid because mFocus is not
3473                 // cleared when the child is detached (mParent = null),
3474                 // This happens because clearFocus on API 15- does not invalidate mFocus of its
3475                 // parent when this child is detached.
3476                 // For API 16+, this is not an issue because requestFocus takes care of clearing the
3477                 // prior detached focused child. For API 15- the problem happens in 2 cases because
3478                 // clearChild does not call clearChildFocus on RV: 1. setFocusable(false) is called
3479                 // for the current focused item which calls clearChild or 2. when the prior focused
3480                 // child is removed, removeDetachedView called in layout step 3 which calls
3481                 // clearChild. We should ignore this invalid focused child in all our calculations
3482                 // for the next view to receive focus, and apply the focus recovery logic instead.
3483                 if (mChildHelper.getChildCount() == 0) {
3484                     // No children left. Request focus on the RV itself since one of its children
3485                     // was holding focus previously.
3486                     requestFocus();
3487                     return;
3488                 }
3489             } else if (!mChildHelper.isHidden(focusedChild)) {
3490                 // If the currently focused child is hidden, apply the focus recovery logic.
3491                 // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
3492                 return;
3493             }
3494         }
3495         ViewHolder focusTarget = null;
3496         // RV first attempts to locate the previously focused item to request focus on using
3497         // mFocusedItemId. If such an item no longer exists, it then makes a best-effort attempt to
3498         // find the next best candidate to request focus on based on mFocusedItemPosition.
3499         if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
3500             focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
3501         }
3502         View viewToFocus = null;
3503         if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView)
3504                 || !focusTarget.itemView.hasFocusable()) {
3505             if (mChildHelper.getChildCount() > 0) {
3506                 // At this point, RV has focus and either of these conditions are true:
3507                 // 1. There's no previously focused item either because RV received focused before
3508                 // layout, or the previously focused item was removed, or RV doesn't have stable IDs
3509                 // 2. Previous focus child is hidden, or 3. Previous focused child is no longer
3510                 // focusable. In either of these cases, we make sure that RV still passes down the
3511                 // focus to one of its focusable children using a best-effort algorithm.
3512                 viewToFocus = findNextViewToFocus();
3513             }
3514         } else {
3515             // looks like the focused item has been replaced with another view that represents the
3516             // same item in the adapter. Request focus on that.
3517             viewToFocus = focusTarget.itemView;
3518         }
3519 
3520         if (viewToFocus != null) {
3521             if (mState.mFocusedSubChildId != NO_ID) {
3522                 View child = viewToFocus.findViewById(mState.mFocusedSubChildId);
3523                 if (child != null && child.isFocusable()) {
3524                     viewToFocus = child;
3525                 }
3526             }
3527             viewToFocus.requestFocus();
3528         }
3529     }
3530 
getDeepestFocusedViewWithId(View view)3531     private int getDeepestFocusedViewWithId(View view) {
3532         int lastKnownId = view.getId();
3533         while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
3534             view = ((ViewGroup) view).getFocusedChild();
3535             final int id = view.getId();
3536             if (id != View.NO_ID) {
3537                 lastKnownId = view.getId();
3538             }
3539         }
3540         return lastKnownId;
3541     }
3542 
3543     /**
3544      * The first step of a layout where we;
3545      * - process adapter updates
3546      * - decide which animation should run
3547      * - save information about current views
3548      * - If necessary, run predictive layout and save its information
3549      */
dispatchLayoutStep1()3550     private void dispatchLayoutStep1() {
3551         mState.assertLayoutStep(State.STEP_START);
3552         mState.mIsMeasuring = false;
3553         eatRequestLayout();
3554         mViewInfoStore.clear();
3555         onEnterLayoutOrScroll();
3556         processAdapterUpdatesAndSetAnimationFlags();
3557         saveFocusInfo();
3558         mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
3559         mItemsAddedOrRemoved = mItemsChanged = false;
3560         mState.mInPreLayout = mState.mRunPredictiveAnimations;
3561         mState.mItemCount = mAdapter.getItemCount();
3562         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3563 
3564         if (mState.mRunSimpleAnimations) {
3565             // Step 0: Find out where all non-removed items are, pre-layout
3566             int count = mChildHelper.getChildCount();
3567             for (int i = 0; i < count; ++i) {
3568                 final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3569                 if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
3570                     continue;
3571                 }
3572                 final ItemHolderInfo animationInfo = mItemAnimator
3573                         .recordPreLayoutInformation(mState, holder,
3574                                 ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
3575                                 holder.getUnmodifiedPayloads());
3576                 mViewInfoStore.addToPreLayout(holder, animationInfo);
3577                 if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
3578                         && !holder.shouldIgnore() && !holder.isInvalid()) {
3579                     long key = getChangedHolderKey(holder);
3580                     // This is NOT the only place where a ViewHolder is added to old change holders
3581                     // list. There is another case where:
3582                     //    * A VH is currently hidden but not deleted
3583                     //    * The hidden item is changed in the adapter
3584                     //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
3585                     // When this case is detected, RV will un-hide that view and add to the old
3586                     // change holders list.
3587                     mViewInfoStore.addToOldChangeHolders(key, holder);
3588                 }
3589             }
3590         }
3591         if (mState.mRunPredictiveAnimations) {
3592             // Step 1: run prelayout: This will use the old positions of items. The layout manager
3593             // is expected to layout everything, even removed items (though not to add removed
3594             // items back to the container). This gives the pre-layout position of APPEARING views
3595             // which come into existence as part of the real layout.
3596 
3597             // Save old positions so that LayoutManager can run its mapping logic.
3598             saveOldPositions();
3599             final boolean didStructureChange = mState.mStructureChanged;
3600             mState.mStructureChanged = false;
3601             // temporarily disable flag because we are asking for previous layout
3602             mLayout.onLayoutChildren(mRecycler, mState);
3603             mState.mStructureChanged = didStructureChange;
3604 
3605             for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
3606                 final View child = mChildHelper.getChildAt(i);
3607                 final ViewHolder viewHolder = getChildViewHolderInt(child);
3608                 if (viewHolder.shouldIgnore()) {
3609                     continue;
3610                 }
3611                 if (!mViewInfoStore.isInPreLayout(viewHolder)) {
3612                     int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
3613                     boolean wasHidden = viewHolder
3614                             .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3615                     if (!wasHidden) {
3616                         flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
3617                     }
3618                     final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
3619                             mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
3620                     if (wasHidden) {
3621                         recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
3622                     } else {
3623                         mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
3624                     }
3625                 }
3626             }
3627             // we don't process disappearing list because they may re-appear in post layout pass.
3628             clearOldPositions();
3629         } else {
3630             clearOldPositions();
3631         }
3632         onExitLayoutOrScroll();
3633         resumeRequestLayout(false);
3634         mState.mLayoutStep = State.STEP_LAYOUT;
3635     }
3636 
3637     /**
3638      * The second layout step where we do the actual layout of the views for the final state.
3639      * This step might be run multiple times if necessary (e.g. measure).
3640      */
dispatchLayoutStep2()3641     private void dispatchLayoutStep2() {
3642         eatRequestLayout();
3643         onEnterLayoutOrScroll();
3644         mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
3645         mAdapterHelper.consumeUpdatesInOnePass();
3646         mState.mItemCount = mAdapter.getItemCount();
3647         mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
3648 
3649         // Step 2: Run layout
3650         mState.mInPreLayout = false;
3651         mLayout.onLayoutChildren(mRecycler, mState);
3652 
3653         mState.mStructureChanged = false;
3654         mPendingSavedState = null;
3655 
3656         // onLayoutChildren may have caused client code to disable item animations; re-check
3657         mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
3658         mState.mLayoutStep = State.STEP_ANIMATIONS;
3659         onExitLayoutOrScroll();
3660         resumeRequestLayout(false);
3661     }
3662 
3663     /**
3664      * The final step of the layout where we save the information about views for animations,
3665      * trigger animations and do any necessary cleanup.
3666      */
dispatchLayoutStep3()3667     private void dispatchLayoutStep3() {
3668         mState.assertLayoutStep(State.STEP_ANIMATIONS);
3669         eatRequestLayout();
3670         onEnterLayoutOrScroll();
3671         mState.mLayoutStep = State.STEP_START;
3672         if (mState.mRunSimpleAnimations) {
3673             // Step 3: Find out where things are now, and process change animations.
3674             // traverse list in reverse because we may call animateChange in the loop which may
3675             // remove the target view holder.
3676             for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
3677                 ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3678                 if (holder.shouldIgnore()) {
3679                     continue;
3680                 }
3681                 long key = getChangedHolderKey(holder);
3682                 final ItemHolderInfo animationInfo = mItemAnimator
3683                         .recordPostLayoutInformation(mState, holder);
3684                 ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
3685                 if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
3686                     // run a change animation
3687 
3688                     // If an Item is CHANGED but the updated version is disappearing, it creates
3689                     // a conflicting case.
3690                     // Since a view that is marked as disappearing is likely to be going out of
3691                     // bounds, we run a change animation. Both views will be cleaned automatically
3692                     // once their animations finish.
3693                     // On the other hand, if it is the same view holder instance, we run a
3694                     // disappearing animation instead because we are not going to rebind the updated
3695                     // VH unless it is enforced by the layout manager.
3696                     final boolean oldDisappearing = mViewInfoStore.isDisappearing(
3697                             oldChangeViewHolder);
3698                     final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
3699                     if (oldDisappearing && oldChangeViewHolder == holder) {
3700                         // run disappear animation instead of change
3701                         mViewInfoStore.addToPostLayout(holder, animationInfo);
3702                     } else {
3703                         final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
3704                                 oldChangeViewHolder);
3705                         // we add and remove so that any post info is merged.
3706                         mViewInfoStore.addToPostLayout(holder, animationInfo);
3707                         ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
3708                         if (preInfo == null) {
3709                             handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
3710                         } else {
3711                             animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
3712                                     oldDisappearing, newDisappearing);
3713                         }
3714                     }
3715                 } else {
3716                     mViewInfoStore.addToPostLayout(holder, animationInfo);
3717                 }
3718             }
3719 
3720             // Step 4: Process view info lists and trigger animations
3721             mViewInfoStore.process(mViewInfoProcessCallback);
3722         }
3723 
3724         mLayout.removeAndRecycleScrapInt(mRecycler);
3725         mState.mPreviousLayoutItemCount = mState.mItemCount;
3726         mDataSetHasChangedAfterLayout = false;
3727         mState.mRunSimpleAnimations = false;
3728 
3729         mState.mRunPredictiveAnimations = false;
3730         mLayout.mRequestedSimpleAnimations = false;
3731         if (mRecycler.mChangedScrap != null) {
3732             mRecycler.mChangedScrap.clear();
3733         }
3734         if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
3735             // Initial prefetch has expanded cache, so reset until next prefetch.
3736             // This prevents initial prefetches from expanding the cache permanently.
3737             mLayout.mPrefetchMaxCountObserved = 0;
3738             mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
3739             mRecycler.updateViewCacheSize();
3740         }
3741 
3742         mLayout.onLayoutCompleted(mState);
3743         onExitLayoutOrScroll();
3744         resumeRequestLayout(false);
3745         mViewInfoStore.clear();
3746         if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
3747             dispatchOnScrolled(0, 0);
3748         }
3749         recoverFocusFromState();
3750         resetFocusInfo();
3751     }
3752 
3753     /**
3754      * This handles the case where there is an unexpected VH missing in the pre-layout map.
3755      * <p>
3756      * We might be able to detect the error in the application which will help the developer to
3757      * resolve the issue.
3758      * <p>
3759      * If it is not an expected error, we at least print an error to notify the developer and ignore
3760      * the animation.
3761      *
3762      * https://code.google.com/p/android/issues/detail?id=193958
3763      *
3764      * @param key The change key
3765      * @param holder Current ViewHolder
3766      * @param oldChangeViewHolder Changed ViewHolder
3767      */
handleMissingPreInfoForChangeError(long key, ViewHolder holder, ViewHolder oldChangeViewHolder)3768     private void handleMissingPreInfoForChangeError(long key,
3769             ViewHolder holder, ViewHolder oldChangeViewHolder) {
3770         // check if two VH have the same key, if so, print that as an error
3771         final int childCount = mChildHelper.getChildCount();
3772         for (int i = 0; i < childCount; i++) {
3773             View view = mChildHelper.getChildAt(i);
3774             ViewHolder other = getChildViewHolderInt(view);
3775             if (other == holder) {
3776                 continue;
3777             }
3778             final long otherKey = getChangedHolderKey(other);
3779             if (otherKey == key) {
3780                 if (mAdapter != null && mAdapter.hasStableIds()) {
3781                     throw new IllegalStateException("Two different ViewHolders have the same stable"
3782                             + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
3783                             + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
3784                 } else {
3785                     throw new IllegalStateException("Two different ViewHolders have the same change"
3786                             + " ID. This might happen due to inconsistent Adapter update events or"
3787                             + " if the LayoutManager lays out the same View multiple times."
3788                             + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
3789                 }
3790             }
3791         }
3792         // Very unlikely to happen but if it does, notify the developer.
3793         Log.e(TAG, "Problem while matching changed view holders with the new"
3794                 + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
3795                 + " cannot be found but it is necessary for " + holder);
3796     }
3797 
3798     /**
3799      * Records the animation information for a view holder that was bounced from hidden list. It
3800      * also clears the bounce back flag.
3801      */
recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder, ItemHolderInfo animationInfo)3802     void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
3803             ItemHolderInfo animationInfo) {
3804         // looks like this view bounced back from hidden list!
3805         viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3806         if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
3807                 && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
3808             long key = getChangedHolderKey(viewHolder);
3809             mViewInfoStore.addToOldChangeHolders(key, viewHolder);
3810         }
3811         mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
3812     }
3813 
findMinMaxChildLayoutPositions(int[] into)3814     private void findMinMaxChildLayoutPositions(int[] into) {
3815         final int count = mChildHelper.getChildCount();
3816         if (count == 0) {
3817             into[0] = NO_POSITION;
3818             into[1] = NO_POSITION;
3819             return;
3820         }
3821         int minPositionPreLayout = Integer.MAX_VALUE;
3822         int maxPositionPreLayout = Integer.MIN_VALUE;
3823         for (int i = 0; i < count; ++i) {
3824             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3825             if (holder.shouldIgnore()) {
3826                 continue;
3827             }
3828             final int pos = holder.getLayoutPosition();
3829             if (pos < minPositionPreLayout) {
3830                 minPositionPreLayout = pos;
3831             }
3832             if (pos > maxPositionPreLayout) {
3833                 maxPositionPreLayout = pos;
3834             }
3835         }
3836         into[0] = minPositionPreLayout;
3837         into[1] = maxPositionPreLayout;
3838     }
3839 
didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout)3840     private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
3841         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3842         return mMinMaxLayoutPositions[0] != minPositionPreLayout
3843                 || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
3844     }
3845 
3846     @Override
removeDetachedView(View child, boolean animate)3847     protected void removeDetachedView(View child, boolean animate) {
3848         ViewHolder vh = getChildViewHolderInt(child);
3849         if (vh != null) {
3850             if (vh.isTmpDetached()) {
3851                 vh.clearTmpDetachFlag();
3852             } else if (!vh.shouldIgnore()) {
3853                 throw new IllegalArgumentException("Called removeDetachedView with a view which"
3854                         + " is not flagged as tmp detached." + vh);
3855             }
3856         }
3857         dispatchChildDetached(child);
3858         super.removeDetachedView(child, animate);
3859     }
3860 
3861     /**
3862      * Returns a unique key to be used while handling change animations.
3863      * It might be child's position or stable id depending on the adapter type.
3864      */
getChangedHolderKey(ViewHolder holder)3865     long getChangedHolderKey(ViewHolder holder) {
3866         return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
3867     }
3868 
animateAppearance(@onNull ViewHolder itemHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)3869     void animateAppearance(@NonNull ViewHolder itemHolder,
3870             @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
3871         itemHolder.setIsRecyclable(false);
3872         if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
3873             postAnimationRunner();
3874         }
3875     }
3876 
animateDisappearance(@onNull ViewHolder holder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo)3877     void animateDisappearance(@NonNull ViewHolder holder,
3878             @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
3879         addAnimatingView(holder);
3880         holder.setIsRecyclable(false);
3881         if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
3882             postAnimationRunner();
3883         }
3884     }
3885 
animateChange(@onNull ViewHolder oldHolder, @NonNull ViewHolder newHolder, @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo, boolean oldHolderDisappearing, boolean newHolderDisappearing)3886     private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
3887             @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
3888             boolean oldHolderDisappearing, boolean newHolderDisappearing) {
3889         oldHolder.setIsRecyclable(false);
3890         if (oldHolderDisappearing) {
3891             addAnimatingView(oldHolder);
3892         }
3893         if (oldHolder != newHolder) {
3894             if (newHolderDisappearing) {
3895                 addAnimatingView(newHolder);
3896             }
3897             oldHolder.mShadowedHolder = newHolder;
3898             // old holder should disappear after animation ends
3899             addAnimatingView(oldHolder);
3900             mRecycler.unscrapView(oldHolder);
3901             newHolder.setIsRecyclable(false);
3902             newHolder.mShadowingHolder = oldHolder;
3903         }
3904         if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
3905             postAnimationRunner();
3906         }
3907     }
3908 
3909     @Override
onLayout(boolean changed, int l, int t, int r, int b)3910     protected void onLayout(boolean changed, int l, int t, int r, int b) {
3911         Trace.beginSection(TRACE_ON_LAYOUT_TAG);
3912         dispatchLayout();
3913         Trace.endSection();
3914         mFirstLayoutComplete = true;
3915     }
3916 
3917     @Override
requestLayout()3918     public void requestLayout() {
3919         if (mEatRequestLayout == 0 && !mLayoutFrozen) {
3920             super.requestLayout();
3921         } else {
3922             mLayoutRequestEaten = true;
3923         }
3924     }
3925 
markItemDecorInsetsDirty()3926     void markItemDecorInsetsDirty() {
3927         final int childCount = mChildHelper.getUnfilteredChildCount();
3928         for (int i = 0; i < childCount; i++) {
3929             final View child = mChildHelper.getUnfilteredChildAt(i);
3930             ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
3931         }
3932         mRecycler.markItemDecorInsetsDirty();
3933     }
3934 
3935     @Override
draw(Canvas c)3936     public void draw(Canvas c) {
3937         super.draw(c);
3938 
3939         final int count = mItemDecorations.size();
3940         for (int i = 0; i < count; i++) {
3941             mItemDecorations.get(i).onDrawOver(c, this, mState);
3942         }
3943         // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
3944         // need find children closest to edges. Not sure if it is worth the effort.
3945         boolean needsInvalidate = false;
3946         if (mLeftGlow != null && !mLeftGlow.isFinished()) {
3947             final int restore = c.save();
3948             final int padding = mClipToPadding ? getPaddingBottom() : 0;
3949             c.rotate(270);
3950             c.translate(-getHeight() + padding, 0);
3951             needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
3952             c.restoreToCount(restore);
3953         }
3954         if (mTopGlow != null && !mTopGlow.isFinished()) {
3955             final int restore = c.save();
3956             if (mClipToPadding) {
3957                 c.translate(getPaddingLeft(), getPaddingTop());
3958             }
3959             needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
3960             c.restoreToCount(restore);
3961         }
3962         if (mRightGlow != null && !mRightGlow.isFinished()) {
3963             final int restore = c.save();
3964             final int width = getWidth();
3965             final int padding = mClipToPadding ? getPaddingTop() : 0;
3966             c.rotate(90);
3967             c.translate(-padding, -width);
3968             needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
3969             c.restoreToCount(restore);
3970         }
3971         if (mBottomGlow != null && !mBottomGlow.isFinished()) {
3972             final int restore = c.save();
3973             c.rotate(180);
3974             if (mClipToPadding) {
3975                 c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
3976             } else {
3977                 c.translate(-getWidth(), -getHeight());
3978             }
3979             needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
3980             c.restoreToCount(restore);
3981         }
3982 
3983         // If some views are animating, ItemDecorators are likely to move/change with them.
3984         // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
3985         // display lists are not invalidated.
3986         if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
3987                 && mItemAnimator.isRunning()) {
3988             needsInvalidate = true;
3989         }
3990 
3991         if (needsInvalidate) {
3992             postInvalidateOnAnimation();
3993         }
3994     }
3995 
3996     @Override
onDraw(Canvas c)3997     public void onDraw(Canvas c) {
3998         super.onDraw(c);
3999 
4000         final int count = mItemDecorations.size();
4001         for (int i = 0; i < count; i++) {
4002             mItemDecorations.get(i).onDraw(c, this, mState);
4003         }
4004     }
4005 
4006     @Override
checkLayoutParams(ViewGroup.LayoutParams p)4007     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
4008         return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
4009     }
4010 
4011     @Override
generateDefaultLayoutParams()4012     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
4013         if (mLayout == null) {
4014             throw new IllegalStateException("RecyclerView has no LayoutManager");
4015         }
4016         return mLayout.generateDefaultLayoutParams();
4017     }
4018 
4019     @Override
generateLayoutParams(AttributeSet attrs)4020     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
4021         if (mLayout == null) {
4022             throw new IllegalStateException("RecyclerView has no LayoutManager");
4023         }
4024         return mLayout.generateLayoutParams(getContext(), attrs);
4025     }
4026 
4027     @Override
generateLayoutParams(ViewGroup.LayoutParams p)4028     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
4029         if (mLayout == null) {
4030             throw new IllegalStateException("RecyclerView has no LayoutManager");
4031         }
4032         return mLayout.generateLayoutParams(p);
4033     }
4034 
4035     /**
4036      * Returns true if RecyclerView is currently running some animations.
4037      * <p>
4038      * If you want to be notified when animations are finished, use
4039      * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
4040      *
4041      * @return True if there are some item animations currently running or waiting to be started.
4042      */
isAnimating()4043     public boolean isAnimating() {
4044         return mItemAnimator != null && mItemAnimator.isRunning();
4045     }
4046 
saveOldPositions()4047     void saveOldPositions() {
4048         final int childCount = mChildHelper.getUnfilteredChildCount();
4049         for (int i = 0; i < childCount; i++) {
4050             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4051             if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
4052                 throw new IllegalStateException("view holder cannot have position -1 unless it"
4053                         + " is removed");
4054             }
4055             if (!holder.shouldIgnore()) {
4056                 holder.saveOldPosition();
4057             }
4058         }
4059     }
4060 
clearOldPositions()4061     void clearOldPositions() {
4062         final int childCount = mChildHelper.getUnfilteredChildCount();
4063         for (int i = 0; i < childCount; i++) {
4064             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4065             if (!holder.shouldIgnore()) {
4066                 holder.clearOldPosition();
4067             }
4068         }
4069         mRecycler.clearOldPositions();
4070     }
4071 
offsetPositionRecordsForMove(int from, int to)4072     void offsetPositionRecordsForMove(int from, int to) {
4073         final int childCount = mChildHelper.getUnfilteredChildCount();
4074         final int start, end, inBetweenOffset;
4075         if (from < to) {
4076             start = from;
4077             end = to;
4078             inBetweenOffset = -1;
4079         } else {
4080             start = to;
4081             end = from;
4082             inBetweenOffset = 1;
4083         }
4084 
4085         for (int i = 0; i < childCount; i++) {
4086             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4087             if (holder == null || holder.mPosition < start || holder.mPosition > end) {
4088                 continue;
4089             }
4090             if (DEBUG) {
4091                 Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
4092                         + holder);
4093             }
4094             if (holder.mPosition == from) {
4095                 holder.offsetPosition(to - from, false);
4096             } else {
4097                 holder.offsetPosition(inBetweenOffset, false);
4098             }
4099 
4100             mState.mStructureChanged = true;
4101         }
4102         mRecycler.offsetPositionRecordsForMove(from, to);
4103         requestLayout();
4104     }
4105 
offsetPositionRecordsForInsert(int positionStart, int itemCount)4106     void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
4107         final int childCount = mChildHelper.getUnfilteredChildCount();
4108         for (int i = 0; i < childCount; i++) {
4109             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4110             if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
4111                 if (DEBUG) {
4112                     Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
4113                             + holder + " now at position " + (holder.mPosition + itemCount));
4114                 }
4115                 holder.offsetPosition(itemCount, false);
4116                 mState.mStructureChanged = true;
4117             }
4118         }
4119         mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
4120         requestLayout();
4121     }
4122 
offsetPositionRecordsForRemove(int positionStart, int itemCount, boolean applyToPreLayout)4123     void offsetPositionRecordsForRemove(int positionStart, int itemCount,
4124             boolean applyToPreLayout) {
4125         final int positionEnd = positionStart + itemCount;
4126         final int childCount = mChildHelper.getUnfilteredChildCount();
4127         for (int i = 0; i < childCount; i++) {
4128             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4129             if (holder != null && !holder.shouldIgnore()) {
4130                 if (holder.mPosition >= positionEnd) {
4131                     if (DEBUG) {
4132                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4133                                 + " holder " + holder + " now at position "
4134                                 + (holder.mPosition - itemCount));
4135                     }
4136                     holder.offsetPosition(-itemCount, applyToPreLayout);
4137                     mState.mStructureChanged = true;
4138                 } else if (holder.mPosition >= positionStart) {
4139                     if (DEBUG) {
4140                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4141                                 + " holder " + holder + " now REMOVED");
4142                     }
4143                     holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
4144                             applyToPreLayout);
4145                     mState.mStructureChanged = true;
4146                 }
4147             }
4148         }
4149         mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
4150         requestLayout();
4151     }
4152 
4153     /**
4154      * Rebind existing views for the given range, or create as needed.
4155      *
4156      * @param positionStart Adapter position to start at
4157      * @param itemCount Number of views that must explicitly be rebound
4158      */
viewRangeUpdate(int positionStart, int itemCount, Object payload)4159     void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
4160         final int childCount = mChildHelper.getUnfilteredChildCount();
4161         final int positionEnd = positionStart + itemCount;
4162 
4163         for (int i = 0; i < childCount; i++) {
4164             final View child = mChildHelper.getUnfilteredChildAt(i);
4165             final ViewHolder holder = getChildViewHolderInt(child);
4166             if (holder == null || holder.shouldIgnore()) {
4167                 continue;
4168             }
4169             if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
4170                 // We re-bind these view holders after pre-processing is complete so that
4171                 // ViewHolders have their final positions assigned.
4172                 holder.addFlags(ViewHolder.FLAG_UPDATE);
4173                 holder.addChangePayload(payload);
4174                 // lp cannot be null since we get ViewHolder from it.
4175                 ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
4176             }
4177         }
4178         mRecycler.viewRangeUpdate(positionStart, itemCount);
4179     }
4180 
canReuseUpdatedViewHolder(ViewHolder viewHolder)4181     boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
4182         return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
4183                 viewHolder.getUnmodifiedPayloads());
4184     }
4185 
4186 
4187     /**
4188      * Call this method to signal that *all* adapter content has changed (generally, because of
4189      * swapAdapter, or notifyDataSetChanged), and that once layout occurs, all attached items should
4190      * be discarded or animated. Note that this work is deferred because RecyclerView requires a
4191      * layout to resolve non-incremental changes to the data set.
4192      *
4193      * Attached items are labeled as position unknown, and may no longer be cached.
4194      *
4195      * It is still possible for items to be prefetched while mDataSetHasChangedAfterLayout == true,
4196      * so calling this method *must* be associated with marking the cache invalid, so that the
4197      * only valid items that remain in the cache, once layout occurs, are prefetched items.
4198      */
setDataSetChangedAfterLayout()4199     void setDataSetChangedAfterLayout() {
4200         if (mDataSetHasChangedAfterLayout) {
4201             return;
4202         }
4203         mDataSetHasChangedAfterLayout = true;
4204         final int childCount = mChildHelper.getUnfilteredChildCount();
4205         for (int i = 0; i < childCount; i++) {
4206             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4207             if (holder != null && !holder.shouldIgnore()) {
4208                 holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
4209             }
4210         }
4211         mRecycler.setAdapterPositionsAsUnknown();
4212 
4213         // immediately mark all views as invalid, so prefetched views can be
4214         // differentiated from views bound to previous data set - both in children, and cache
4215         markKnownViewsInvalid();
4216     }
4217 
4218     /**
4219      * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
4220      * data change event.
4221      */
markKnownViewsInvalid()4222     void markKnownViewsInvalid() {
4223         final int childCount = mChildHelper.getUnfilteredChildCount();
4224         for (int i = 0; i < childCount; i++) {
4225             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4226             if (holder != null && !holder.shouldIgnore()) {
4227                 holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
4228             }
4229         }
4230         markItemDecorInsetsDirty();
4231         mRecycler.markKnownViewsInvalid();
4232     }
4233 
4234     /**
4235      * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
4236      * will trigger a {@link #requestLayout()} call.
4237      */
invalidateItemDecorations()4238     public void invalidateItemDecorations() {
4239         if (mItemDecorations.size() == 0) {
4240             return;
4241         }
4242         if (mLayout != null) {
4243             mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
4244                     + " or layout");
4245         }
4246         markItemDecorInsetsDirty();
4247         requestLayout();
4248     }
4249 
4250     /**
4251      * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
4252      * focus even if the View representing the Item is replaced during a layout calculation.
4253      * <p>
4254      * By default, this value is {@code true}.
4255      *
4256      * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
4257      * focus.
4258      *
4259      * @see #setPreserveFocusAfterLayout(boolean)
4260      */
getPreserveFocusAfterLayout()4261     public boolean getPreserveFocusAfterLayout() {
4262         return mPreserveFocusAfterLayout;
4263     }
4264 
4265     /**
4266      * Set whether the RecyclerView should try to keep the same Item focused after a layout
4267      * calculation or not.
4268      * <p>
4269      * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
4270      * views may lose focus during a layout calculation as their state changes or they are replaced
4271      * with another view due to type change or animation. In these cases, RecyclerView can request
4272      * focus on the new view automatically.
4273      *
4274      * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
4275      *                                 layout calculations. Defaults to true.
4276      *
4277      * @see #getPreserveFocusAfterLayout()
4278      */
setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout)4279     public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
4280         mPreserveFocusAfterLayout = preserveFocusAfterLayout;
4281     }
4282 
4283     /**
4284      * Retrieve the {@link ViewHolder} for the given child view.
4285      *
4286      * @param child Child of this RecyclerView to query for its ViewHolder
4287      * @return The child view's ViewHolder
4288      */
getChildViewHolder(View child)4289     public ViewHolder getChildViewHolder(View child) {
4290         final ViewParent parent = child.getParent();
4291         if (parent != null && parent != this) {
4292             throw new IllegalArgumentException("View " + child + " is not a direct child of "
4293                     + this);
4294         }
4295         return getChildViewHolderInt(child);
4296     }
4297 
4298     /**
4299      * Traverses the ancestors of the given view and returns the item view that contains it and
4300      * also a direct child of the RecyclerView. This returned view can be used to get the
4301      * ViewHolder by calling {@link #getChildViewHolder(View)}.
4302      *
4303      * @param view The view that is a descendant of the RecyclerView.
4304      *
4305      * @return The direct child of the RecyclerView which contains the given view or null if the
4306      * provided view is not a descendant of this RecyclerView.
4307      *
4308      * @see #getChildViewHolder(View)
4309      * @see #findContainingViewHolder(View)
4310      */
4311     @Nullable
findContainingItemView(View view)4312     public View findContainingItemView(View view) {
4313         ViewParent parent = view.getParent();
4314         while (parent != null && parent != this && parent instanceof View) {
4315             view = (View) parent;
4316             parent = view.getParent();
4317         }
4318         return parent == this ? view : null;
4319     }
4320 
4321     /**
4322      * Returns the ViewHolder that contains the given view.
4323      *
4324      * @param view The view that is a descendant of the RecyclerView.
4325      *
4326      * @return The ViewHolder that contains the given view or null if the provided view is not a
4327      * descendant of this RecyclerView.
4328      */
4329     @Nullable
findContainingViewHolder(View view)4330     public ViewHolder findContainingViewHolder(View view) {
4331         View itemView = findContainingItemView(view);
4332         return itemView == null ? null : getChildViewHolder(itemView);
4333     }
4334 
4335 
getChildViewHolderInt(View child)4336     static ViewHolder getChildViewHolderInt(View child) {
4337         if (child == null) {
4338             return null;
4339         }
4340         return ((LayoutParams) child.getLayoutParams()).mViewHolder;
4341     }
4342 
4343     /**
4344      * @deprecated use {@link #getChildAdapterPosition(View)} or
4345      * {@link #getChildLayoutPosition(View)}.
4346      */
4347     @Deprecated
getChildPosition(View child)4348     public int getChildPosition(View child) {
4349         return getChildAdapterPosition(child);
4350     }
4351 
4352     /**
4353      * Return the adapter position that the given child view corresponds to.
4354      *
4355      * @param child Child View to query
4356      * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
4357      */
getChildAdapterPosition(View child)4358     public int getChildAdapterPosition(View child) {
4359         final ViewHolder holder = getChildViewHolderInt(child);
4360         return holder != null ? holder.getAdapterPosition() : NO_POSITION;
4361     }
4362 
4363     /**
4364      * Return the adapter position of the given child view as of the latest completed layout pass.
4365      * <p>
4366      * This position may not be equal to Item's adapter position if there are pending changes
4367      * in the adapter which have not been reflected to the layout yet.
4368      *
4369      * @param child Child View to query
4370      * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
4371      * the View is representing a removed item.
4372      */
getChildLayoutPosition(View child)4373     public int getChildLayoutPosition(View child) {
4374         final ViewHolder holder = getChildViewHolderInt(child);
4375         return holder != null ? holder.getLayoutPosition() : NO_POSITION;
4376     }
4377 
4378     /**
4379      * Return the stable item id that the given child view corresponds to.
4380      *
4381      * @param child Child View to query
4382      * @return Item id corresponding to the given view or {@link #NO_ID}
4383      */
getChildItemId(View child)4384     public long getChildItemId(View child) {
4385         if (mAdapter == null || !mAdapter.hasStableIds()) {
4386             return NO_ID;
4387         }
4388         final ViewHolder holder = getChildViewHolderInt(child);
4389         return holder != null ? holder.getItemId() : NO_ID;
4390     }
4391 
4392     /**
4393      * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
4394      * {@link #findViewHolderForAdapterPosition(int)}
4395      */
4396     @Deprecated
findViewHolderForPosition(int position)4397     public ViewHolder findViewHolderForPosition(int position) {
4398         return findViewHolderForPosition(position, false);
4399     }
4400 
4401     /**
4402      * Return the ViewHolder for the item in the given position of the data set as of the latest
4403      * layout pass.
4404      * <p>
4405      * This method checks only the children of RecyclerView. If the item at the given
4406      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4407      * <p>
4408      * Note that when Adapter contents change, ViewHolder positions are not updated until the
4409      * next layout calculation. If there are pending adapter updates, the return value of this
4410      * method may not match your adapter contents. You can use
4411      * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
4412      * <p>
4413      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4414      * with the same layout position representing the same Item. In this case, the updated
4415      * ViewHolder will be returned.
4416      *
4417      * @param position The position of the item in the data set of the adapter
4418      * @return The ViewHolder at <code>position</code> or null if there is no such item
4419      */
findViewHolderForLayoutPosition(int position)4420     public ViewHolder findViewHolderForLayoutPosition(int position) {
4421         return findViewHolderForPosition(position, false);
4422     }
4423 
4424     /**
4425      * Return the ViewHolder for the item in the given position of the data set. Unlike
4426      * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
4427      * adapter changes that may not be reflected to the layout yet. On the other hand, if
4428      * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
4429      * calculated yet, this method will return <code>null</code> since the new positions of views
4430      * are unknown until the layout is calculated.
4431      * <p>
4432      * This method checks only the children of RecyclerView. If the item at the given
4433      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4434      * <p>
4435      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4436      * representing the same Item. In this case, the updated ViewHolder will be returned.
4437      *
4438      * @param position The position of the item in the data set of the adapter
4439      * @return The ViewHolder at <code>position</code> or null if there is no such item
4440      */
findViewHolderForAdapterPosition(int position)4441     public ViewHolder findViewHolderForAdapterPosition(int position) {
4442         if (mDataSetHasChangedAfterLayout) {
4443             return null;
4444         }
4445         final int childCount = mChildHelper.getUnfilteredChildCount();
4446         // hidden VHs are not preferred but if that is the only one we find, we rather return it
4447         ViewHolder hidden = null;
4448         for (int i = 0; i < childCount; i++) {
4449             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4450             if (holder != null && !holder.isRemoved()
4451                     && getAdapterPositionFor(holder) == position) {
4452                 if (mChildHelper.isHidden(holder.itemView)) {
4453                     hidden = holder;
4454                 } else {
4455                     return holder;
4456                 }
4457             }
4458         }
4459         return hidden;
4460     }
4461 
findViewHolderForPosition(int position, boolean checkNewPosition)4462     ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
4463         final int childCount = mChildHelper.getUnfilteredChildCount();
4464         ViewHolder hidden = null;
4465         for (int i = 0; i < childCount; i++) {
4466             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4467             if (holder != null && !holder.isRemoved()) {
4468                 if (checkNewPosition) {
4469                     if (holder.mPosition != position) {
4470                         continue;
4471                     }
4472                 } else if (holder.getLayoutPosition() != position) {
4473                     continue;
4474                 }
4475                 if (mChildHelper.isHidden(holder.itemView)) {
4476                     hidden = holder;
4477                 } else {
4478                     return holder;
4479                 }
4480             }
4481         }
4482         // This method should not query cached views. It creates a problem during adapter updates
4483         // when we are dealing with already laid out views. Also, for the public method, it is more
4484         // reasonable to return null if position is not laid out.
4485         return hidden;
4486     }
4487 
4488     /**
4489      * Return the ViewHolder for the item with the given id. The RecyclerView must
4490      * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
4491      * return a non-null value.
4492      * <p>
4493      * This method checks only the children of RecyclerView. If the item with the given
4494      * <code>id</code> is not laid out, it <em>will not</em> create a new one.
4495      *
4496      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
4497      * same id. In this case, the updated ViewHolder will be returned.
4498      *
4499      * @param id The id for the requested item
4500      * @return The ViewHolder with the given <code>id</code> or null if there is no such item
4501      */
findViewHolderForItemId(long id)4502     public ViewHolder findViewHolderForItemId(long id) {
4503         if (mAdapter == null || !mAdapter.hasStableIds()) {
4504             return null;
4505         }
4506         final int childCount = mChildHelper.getUnfilteredChildCount();
4507         ViewHolder hidden = null;
4508         for (int i = 0; i < childCount; i++) {
4509             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4510             if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
4511                 if (mChildHelper.isHidden(holder.itemView)) {
4512                     hidden = holder;
4513                 } else {
4514                     return holder;
4515                 }
4516             }
4517         }
4518         return hidden;
4519     }
4520 
4521     /**
4522      * Find the topmost view under the given point.
4523      *
4524      * @param x Horizontal position in pixels to search
4525      * @param y Vertical position in pixels to search
4526      * @return The child view under (x, y) or null if no matching child is found
4527      */
findChildViewUnder(float x, float y)4528     public View findChildViewUnder(float x, float y) {
4529         final int count = mChildHelper.getChildCount();
4530         for (int i = count - 1; i >= 0; i--) {
4531             final View child = mChildHelper.getChildAt(i);
4532             final float translationX = child.getTranslationX();
4533             final float translationY = child.getTranslationY();
4534             if (x >= child.getLeft() + translationX
4535                     && x <= child.getRight() + translationX
4536                     && y >= child.getTop() + translationY
4537                     && y <= child.getBottom() + translationY) {
4538                 return child;
4539             }
4540         }
4541         return null;
4542     }
4543 
4544     @Override
drawChild(Canvas canvas, View child, long drawingTime)4545     public boolean drawChild(Canvas canvas, View child, long drawingTime) {
4546         return super.drawChild(canvas, child, drawingTime);
4547     }
4548 
4549     /**
4550      * Offset the bounds of all child views by <code>dy</code> pixels.
4551      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4552      *
4553      * @param dy Vertical pixel offset to apply to the bounds of all child views
4554      */
offsetChildrenVertical(int dy)4555     public void offsetChildrenVertical(int dy) {
4556         final int childCount = mChildHelper.getChildCount();
4557         for (int i = 0; i < childCount; i++) {
4558             mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
4559         }
4560     }
4561 
4562     /**
4563      * Called when an item view is attached to this RecyclerView.
4564      *
4565      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4566      * of child views as they become attached. This will be called before a
4567      * {@link LayoutManager} measures or lays out the view and is a good time to perform these
4568      * changes.</p>
4569      *
4570      * @param child Child view that is now attached to this RecyclerView and its associated window
4571      */
onChildAttachedToWindow(View child)4572     public void onChildAttachedToWindow(View child) {
4573     }
4574 
4575     /**
4576      * Called when an item view is detached from this RecyclerView.
4577      *
4578      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4579      * of child views as they become detached. This will be called as a
4580      * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
4581      *
4582      * @param child Child view that is now detached from this RecyclerView and its associated window
4583      */
onChildDetachedFromWindow(View child)4584     public void onChildDetachedFromWindow(View child) {
4585     }
4586 
4587     /**
4588      * Offset the bounds of all child views by <code>dx</code> pixels.
4589      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4590      *
4591      * @param dx Horizontal pixel offset to apply to the bounds of all child views
4592      */
offsetChildrenHorizontal(int dx)4593     public void offsetChildrenHorizontal(int dx) {
4594         final int childCount = mChildHelper.getChildCount();
4595         for (int i = 0; i < childCount; i++) {
4596             mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
4597         }
4598     }
4599 
4600     /**
4601      * Returns the bounds of the view including its decoration and margins.
4602      *
4603      * @param view The view element to check
4604      * @param outBounds A rect that will receive the bounds of the element including its
4605      *                  decoration and margins.
4606      */
getDecoratedBoundsWithMargins(View view, Rect outBounds)4607     public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
4608         getDecoratedBoundsWithMarginsInt(view, outBounds);
4609     }
4610 
getDecoratedBoundsWithMarginsInt(View view, Rect outBounds)4611     static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
4612         final LayoutParams lp = (LayoutParams) view.getLayoutParams();
4613         final Rect insets = lp.mDecorInsets;
4614         outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
4615                 view.getTop() - insets.top - lp.topMargin,
4616                 view.getRight() + insets.right + lp.rightMargin,
4617                 view.getBottom() + insets.bottom + lp.bottomMargin);
4618     }
4619 
getItemDecorInsetsForChild(View child)4620     Rect getItemDecorInsetsForChild(View child) {
4621         final LayoutParams lp = (LayoutParams) child.getLayoutParams();
4622         if (!lp.mInsetsDirty) {
4623             return lp.mDecorInsets;
4624         }
4625 
4626         if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
4627             // changed/invalid items should not be updated until they are rebound.
4628             return lp.mDecorInsets;
4629         }
4630         final Rect insets = lp.mDecorInsets;
4631         insets.set(0, 0, 0, 0);
4632         final int decorCount = mItemDecorations.size();
4633         for (int i = 0; i < decorCount; i++) {
4634             mTempRect.set(0, 0, 0, 0);
4635             mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
4636             insets.left += mTempRect.left;
4637             insets.top += mTempRect.top;
4638             insets.right += mTempRect.right;
4639             insets.bottom += mTempRect.bottom;
4640         }
4641         lp.mInsetsDirty = false;
4642         return insets;
4643     }
4644 
4645     /**
4646      * Called when the scroll position of this RecyclerView changes. Subclasses should use
4647      * this method to respond to scrolling within the adapter's data set instead of an explicit
4648      * listener.
4649      *
4650      * <p>This method will always be invoked before listeners. If a subclass needs to perform
4651      * any additional upkeep or bookkeeping after scrolling but before listeners run,
4652      * this is a good place to do so.</p>
4653      *
4654      * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
4655      * the distance scrolled in either direction within the adapter's data set instead of absolute
4656      * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
4657      * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
4658      * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
4659      * do not correspond to the data set scroll position. However, some subclasses may choose
4660      * to use these fields as special offsets.</p>
4661      *
4662      * @param dx horizontal distance scrolled in pixels
4663      * @param dy vertical distance scrolled in pixels
4664      */
onScrolled(int dx, int dy)4665     public void onScrolled(int dx, int dy) {
4666         // Do nothing
4667     }
4668 
dispatchOnScrolled(int hresult, int vresult)4669     void dispatchOnScrolled(int hresult, int vresult) {
4670         mDispatchScrollCounter++;
4671         // Pass the current scrollX/scrollY values; no actual change in these properties occurred
4672         // but some general-purpose code may choose to respond to changes this way.
4673         final int scrollX = getScrollX();
4674         final int scrollY = getScrollY();
4675         onScrollChanged(scrollX, scrollY, scrollX, scrollY);
4676 
4677         // Pass the real deltas to onScrolled, the RecyclerView-specific method.
4678         onScrolled(hresult, vresult);
4679 
4680         // Invoke listeners last. Subclassed view methods always handle the event first.
4681         // All internal state is consistent by the time listeners are invoked.
4682         if (mScrollListener != null) {
4683             mScrollListener.onScrolled(this, hresult, vresult);
4684         }
4685         if (mScrollListeners != null) {
4686             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4687                 mScrollListeners.get(i).onScrolled(this, hresult, vresult);
4688             }
4689         }
4690         mDispatchScrollCounter--;
4691     }
4692 
4693     /**
4694      * Called when the scroll state of this RecyclerView changes. Subclasses should use this
4695      * method to respond to state changes instead of an explicit listener.
4696      *
4697      * <p>This method will always be invoked before listeners, but after the LayoutManager
4698      * responds to the scroll state change.</p>
4699      *
4700      * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
4701      *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
4702      */
onScrollStateChanged(int state)4703     public void onScrollStateChanged(int state) {
4704         // Do nothing
4705     }
4706 
dispatchOnScrollStateChanged(int state)4707     void dispatchOnScrollStateChanged(int state) {
4708         // Let the LayoutManager go first; this allows it to bring any properties into
4709         // a consistent state before the RecyclerView subclass responds.
4710         if (mLayout != null) {
4711             mLayout.onScrollStateChanged(state);
4712         }
4713 
4714         // Let the RecyclerView subclass handle this event next; any LayoutManager property
4715         // changes will be reflected by this time.
4716         onScrollStateChanged(state);
4717 
4718         // Listeners go last. All other internal state is consistent by this point.
4719         if (mScrollListener != null) {
4720             mScrollListener.onScrollStateChanged(this, state);
4721         }
4722         if (mScrollListeners != null) {
4723             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4724                 mScrollListeners.get(i).onScrollStateChanged(this, state);
4725             }
4726         }
4727     }
4728 
4729     /**
4730      * Returns whether there are pending adapter updates which are not yet applied to the layout.
4731      * <p>
4732      * If this method returns <code>true</code>, it means that what user is currently seeing may not
4733      * reflect them adapter contents (depending on what has changed).
4734      * You may use this information to defer or cancel some operations.
4735      * <p>
4736      * This method returns true if RecyclerView has not yet calculated the first layout after it is
4737      * attached to the Window or the Adapter has been replaced.
4738      *
4739      * @return True if there are some adapter updates which are not yet reflected to layout or false
4740      * if layout is up to date.
4741      */
hasPendingAdapterUpdates()4742     public boolean hasPendingAdapterUpdates() {
4743         return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
4744                 || mAdapterHelper.hasPendingUpdates();
4745     }
4746 
4747     class ViewFlinger implements Runnable {
4748         private int mLastFlingX;
4749         private int mLastFlingY;
4750         private OverScroller mScroller;
4751         Interpolator mInterpolator = sQuinticInterpolator;
4752 
4753 
4754         // When set to true, postOnAnimation callbacks are delayed until the run method completes
4755         private boolean mEatRunOnAnimationRequest = false;
4756 
4757         // Tracks if postAnimationCallback should be re-attached when it is done
4758         private boolean mReSchedulePostAnimationCallback = false;
4759 
ViewFlinger()4760         ViewFlinger() {
4761             mScroller = new OverScroller(getContext(), sQuinticInterpolator);
4762         }
4763 
4764         @Override
run()4765         public void run() {
4766             if (mLayout == null) {
4767                 stop();
4768                 return; // no layout, cannot scroll.
4769             }
4770             disableRunOnAnimationRequests();
4771             consumePendingUpdateOperations();
4772             // keep a local reference so that if it is changed during onAnimation method, it won't
4773             // cause unexpected behaviors
4774             final OverScroller scroller = mScroller;
4775             final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
4776             if (scroller.computeScrollOffset()) {
4777                 final int x = scroller.getCurrX();
4778                 final int y = scroller.getCurrY();
4779                 final int dx = x - mLastFlingX;
4780                 final int dy = y - mLastFlingY;
4781                 int hresult = 0;
4782                 int vresult = 0;
4783                 mLastFlingX = x;
4784                 mLastFlingY = y;
4785                 int overscrollX = 0, overscrollY = 0;
4786                 if (mAdapter != null) {
4787                     eatRequestLayout();
4788                     onEnterLayoutOrScroll();
4789                     Trace.beginSection(TRACE_SCROLL_TAG);
4790                     if (dx != 0) {
4791                         hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
4792                         overscrollX = dx - hresult;
4793                     }
4794                     if (dy != 0) {
4795                         vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
4796                         overscrollY = dy - vresult;
4797                     }
4798                     Trace.endSection();
4799                     repositionShadowingViews();
4800 
4801                     onExitLayoutOrScroll();
4802                     resumeRequestLayout(false);
4803 
4804                     if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
4805                             && smoothScroller.isRunning()) {
4806                         final int adapterSize = mState.getItemCount();
4807                         if (adapterSize == 0) {
4808                             smoothScroller.stop();
4809                         } else if (smoothScroller.getTargetPosition() >= adapterSize) {
4810                             smoothScroller.setTargetPosition(adapterSize - 1);
4811                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4812                         } else {
4813                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4814                         }
4815                     }
4816                 }
4817                 if (!mItemDecorations.isEmpty()) {
4818                     invalidate();
4819                 }
4820                 if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
4821                     considerReleasingGlowsOnScroll(dx, dy);
4822                 }
4823                 if (overscrollX != 0 || overscrollY != 0) {
4824                     final int vel = (int) scroller.getCurrVelocity();
4825 
4826                     int velX = 0;
4827                     if (overscrollX != x) {
4828                         velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
4829                     }
4830 
4831                     int velY = 0;
4832                     if (overscrollY != y) {
4833                         velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
4834                     }
4835 
4836                     if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
4837                         absorbGlows(velX, velY);
4838                     }
4839                     if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
4840                             && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
4841                         scroller.abortAnimation();
4842                     }
4843                 }
4844                 if (hresult != 0 || vresult != 0) {
4845                     dispatchOnScrolled(hresult, vresult);
4846                 }
4847 
4848                 if (!awakenScrollBars()) {
4849                     invalidate();
4850                 }
4851 
4852                 final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
4853                         && vresult == dy;
4854                 final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
4855                         && hresult == dx;
4856                 final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
4857                         || fullyConsumedVertical;
4858 
4859                 if (scroller.isFinished() || !fullyConsumedAny) {
4860                     setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this.
4861                     if (ALLOW_THREAD_GAP_WORK) {
4862                         mPrefetchRegistry.clearPrefetchPositions();
4863                     }
4864                 } else {
4865                     postOnAnimation();
4866                     if (mGapWorker != null) {
4867                         mGapWorker.postFromTraversal(RecyclerView.this, dx, dy);
4868                     }
4869                 }
4870             }
4871             // call this after the onAnimation is complete not to have inconsistent callbacks etc.
4872             if (smoothScroller != null) {
4873                 if (smoothScroller.isPendingInitialRun()) {
4874                     smoothScroller.onAnimation(0, 0);
4875                 }
4876                 if (!mReSchedulePostAnimationCallback) {
4877                     smoothScroller.stop(); //stop if it does not trigger any scroll
4878                 }
4879             }
4880             enableRunOnAnimationRequests();
4881         }
4882 
disableRunOnAnimationRequests()4883         private void disableRunOnAnimationRequests() {
4884             mReSchedulePostAnimationCallback = false;
4885             mEatRunOnAnimationRequest = true;
4886         }
4887 
enableRunOnAnimationRequests()4888         private void enableRunOnAnimationRequests() {
4889             mEatRunOnAnimationRequest = false;
4890             if (mReSchedulePostAnimationCallback) {
4891                 postOnAnimation();
4892             }
4893         }
4894 
postOnAnimation()4895         void postOnAnimation() {
4896             if (mEatRunOnAnimationRequest) {
4897                 mReSchedulePostAnimationCallback = true;
4898             } else {
4899                 removeCallbacks(this);
4900                 RecyclerView.this.postOnAnimation(this);
4901             }
4902         }
4903 
fling(int velocityX, int velocityY)4904         public void fling(int velocityX, int velocityY) {
4905             setScrollState(SCROLL_STATE_SETTLING);
4906             mLastFlingX = mLastFlingY = 0;
4907             mScroller.fling(0, 0, velocityX, velocityY,
4908                     Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
4909             postOnAnimation();
4910         }
4911 
smoothScrollBy(int dx, int dy)4912         public void smoothScrollBy(int dx, int dy) {
4913             smoothScrollBy(dx, dy, 0, 0);
4914         }
4915 
smoothScrollBy(int dx, int dy, int vx, int vy)4916         public void smoothScrollBy(int dx, int dy, int vx, int vy) {
4917             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
4918         }
4919 
distanceInfluenceForSnapDuration(float f)4920         private float distanceInfluenceForSnapDuration(float f) {
4921             f -= 0.5f; // center the values about 0.
4922             f *= 0.3f * Math.PI / 2.0f;
4923             return (float) Math.sin(f);
4924         }
4925 
computeScrollDuration(int dx, int dy, int vx, int vy)4926         private int computeScrollDuration(int dx, int dy, int vx, int vy) {
4927             final int absDx = Math.abs(dx);
4928             final int absDy = Math.abs(dy);
4929             final boolean horizontal = absDx > absDy;
4930             final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
4931             final int delta = (int) Math.sqrt(dx * dx + dy * dy);
4932             final int containerSize = horizontal ? getWidth() : getHeight();
4933             final int halfContainerSize = containerSize / 2;
4934             final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
4935             final float distance = halfContainerSize + halfContainerSize
4936                     * distanceInfluenceForSnapDuration(distanceRatio);
4937 
4938             final int duration;
4939             if (velocity > 0) {
4940                 duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
4941             } else {
4942                 float absDelta = (float) (horizontal ? absDx : absDy);
4943                 duration = (int) (((absDelta / containerSize) + 1) * 300);
4944             }
4945             return Math.min(duration, MAX_SCROLL_DURATION);
4946         }
4947 
smoothScrollBy(int dx, int dy, int duration)4948         public void smoothScrollBy(int dx, int dy, int duration) {
4949             smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
4950         }
4951 
smoothScrollBy(int dx, int dy, Interpolator interpolator)4952         public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
4953             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, 0, 0),
4954                     interpolator == null ? sQuinticInterpolator : interpolator);
4955         }
4956 
smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator)4957         public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
4958             if (mInterpolator != interpolator) {
4959                 mInterpolator = interpolator;
4960                 mScroller = new OverScroller(getContext(), interpolator);
4961             }
4962             setScrollState(SCROLL_STATE_SETTLING);
4963             mLastFlingX = mLastFlingY = 0;
4964             mScroller.startScroll(0, 0, dx, dy, duration);
4965             postOnAnimation();
4966         }
4967 
stop()4968         public void stop() {
4969             removeCallbacks(this);
4970             mScroller.abortAnimation();
4971         }
4972 
4973     }
4974 
repositionShadowingViews()4975     void repositionShadowingViews() {
4976         // Fix up shadow views used by change animations
4977         int count = mChildHelper.getChildCount();
4978         for (int i = 0; i < count; i++) {
4979             View view = mChildHelper.getChildAt(i);
4980             ViewHolder holder = getChildViewHolder(view);
4981             if (holder != null && holder.mShadowingHolder != null) {
4982                 View shadowingView = holder.mShadowingHolder.itemView;
4983                 int left = view.getLeft();
4984                 int top = view.getTop();
4985                 if (left != shadowingView.getLeft() ||  top != shadowingView.getTop()) {
4986                     shadowingView.layout(left, top,
4987                             left + shadowingView.getWidth(),
4988                             top + shadowingView.getHeight());
4989                 }
4990             }
4991         }
4992     }
4993 
4994     private class RecyclerViewDataObserver extends AdapterDataObserver {
RecyclerViewDataObserver()4995         RecyclerViewDataObserver() {
4996         }
4997 
4998         @Override
onChanged()4999         public void onChanged() {
5000             assertNotInLayoutOrScroll(null);
5001             mState.mStructureChanged = true;
5002 
5003             setDataSetChangedAfterLayout();
5004             if (!mAdapterHelper.hasPendingUpdates()) {
5005                 requestLayout();
5006             }
5007         }
5008 
5009         @Override
onItemRangeChanged(int positionStart, int itemCount, Object payload)5010         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
5011             assertNotInLayoutOrScroll(null);
5012             if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
5013                 triggerUpdateProcessor();
5014             }
5015         }
5016 
5017         @Override
onItemRangeInserted(int positionStart, int itemCount)5018         public void onItemRangeInserted(int positionStart, int itemCount) {
5019             assertNotInLayoutOrScroll(null);
5020             if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
5021                 triggerUpdateProcessor();
5022             }
5023         }
5024 
5025         @Override
onItemRangeRemoved(int positionStart, int itemCount)5026         public void onItemRangeRemoved(int positionStart, int itemCount) {
5027             assertNotInLayoutOrScroll(null);
5028             if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
5029                 triggerUpdateProcessor();
5030             }
5031         }
5032 
5033         @Override
onItemRangeMoved(int fromPosition, int toPosition, int itemCount)5034         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
5035             assertNotInLayoutOrScroll(null);
5036             if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
5037                 triggerUpdateProcessor();
5038             }
5039         }
5040 
triggerUpdateProcessor()5041         void triggerUpdateProcessor() {
5042             if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
5043                 RecyclerView.this.postOnAnimation(mUpdateChildViewsRunnable);
5044             } else {
5045                 mAdapterUpdateDuringMeasure = true;
5046                 requestLayout();
5047             }
5048         }
5049     }
5050 
5051     /**
5052      * RecycledViewPool lets you share Views between multiple RecyclerViews.
5053      * <p>
5054      * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
5055      * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
5056      * <p>
5057      * RecyclerView automatically creates a pool for itself if you don't provide one.
5058      *
5059      */
5060     public static class RecycledViewPool {
5061         private static final int DEFAULT_MAX_SCRAP = 5;
5062 
5063         /**
5064          * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
5065          *
5066          * Note that this tracks running averages of create/bind time across all RecyclerViews
5067          * (and, indirectly, Adapters) that use this pool.
5068          *
5069          * 1) This enables us to track average create and bind times across multiple adapters. Even
5070          * though create (and especially bind) may behave differently for different Adapter
5071          * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
5072          *
5073          * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
5074          * false for all other views of its type for the same deadline. This prevents items
5075          * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
5076          */
5077         static class ScrapData {
5078             @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
5079             ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
5080             int mMaxScrap = DEFAULT_MAX_SCRAP;
5081             long mCreateRunningAverageNs = 0;
5082             long mBindRunningAverageNs = 0;
5083         }
5084         SparseArray<ScrapData> mScrap = new SparseArray<>();
5085 
5086         private int mAttachCount = 0;
5087 
clear()5088         public void clear() {
5089             for (int i = 0; i < mScrap.size(); i++) {
5090                 ScrapData data = mScrap.valueAt(i);
5091                 data.mScrapHeap.clear();
5092             }
5093         }
5094 
setMaxRecycledViews(int viewType, int max)5095         public void setMaxRecycledViews(int viewType, int max) {
5096             ScrapData scrapData = getScrapDataForType(viewType);
5097             scrapData.mMaxScrap = max;
5098             final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
5099             if (scrapHeap != null) {
5100                 while (scrapHeap.size() > max) {
5101                     scrapHeap.remove(scrapHeap.size() - 1);
5102                 }
5103             }
5104         }
5105 
5106         /**
5107          * Returns the current number of Views held by the RecycledViewPool of the given view type.
5108          */
getRecycledViewCount(int viewType)5109         public int getRecycledViewCount(int viewType) {
5110             return getScrapDataForType(viewType).mScrapHeap.size();
5111         }
5112 
getRecycledView(int viewType)5113         public ViewHolder getRecycledView(int viewType) {
5114             final ScrapData scrapData = mScrap.get(viewType);
5115             if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
5116                 final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
5117                 return scrapHeap.remove(scrapHeap.size() - 1);
5118             }
5119             return null;
5120         }
5121 
size()5122         int size() {
5123             int count = 0;
5124             for (int i = 0; i < mScrap.size(); i++) {
5125                 ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
5126                 if (viewHolders != null) {
5127                     count += viewHolders.size();
5128                 }
5129             }
5130             return count;
5131         }
5132 
putRecycledView(ViewHolder scrap)5133         public void putRecycledView(ViewHolder scrap) {
5134             final int viewType = scrap.getItemViewType();
5135             final ArrayList scrapHeap = getScrapDataForType(viewType).mScrapHeap;
5136             if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
5137                 return;
5138             }
5139             if (DEBUG && scrapHeap.contains(scrap)) {
5140                 throw new IllegalArgumentException("this scrap item already exists");
5141             }
5142             scrap.resetInternal();
5143             scrapHeap.add(scrap);
5144         }
5145 
runningAverage(long oldAverage, long newValue)5146         long runningAverage(long oldAverage, long newValue) {
5147             if (oldAverage == 0) {
5148                 return newValue;
5149             }
5150             return (oldAverage / 4 * 3) + (newValue / 4);
5151         }
5152 
factorInCreateTime(int viewType, long createTimeNs)5153         void factorInCreateTime(int viewType, long createTimeNs) {
5154             ScrapData scrapData = getScrapDataForType(viewType);
5155             scrapData.mCreateRunningAverageNs = runningAverage(
5156                     scrapData.mCreateRunningAverageNs, createTimeNs);
5157         }
5158 
factorInBindTime(int viewType, long bindTimeNs)5159         void factorInBindTime(int viewType, long bindTimeNs) {
5160             ScrapData scrapData = getScrapDataForType(viewType);
5161             scrapData.mBindRunningAverageNs = runningAverage(
5162                     scrapData.mBindRunningAverageNs, bindTimeNs);
5163         }
5164 
willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs)5165         boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5166             long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
5167             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5168         }
5169 
willBindInTime(int viewType, long approxCurrentNs, long deadlineNs)5170         boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5171             long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
5172             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5173         }
5174 
attach(Adapter adapter)5175         void attach(Adapter adapter) {
5176             mAttachCount++;
5177         }
5178 
detach()5179         void detach() {
5180             mAttachCount--;
5181         }
5182 
5183 
5184         /**
5185          * Detaches the old adapter and attaches the new one.
5186          * <p>
5187          * RecycledViewPool will clear its cache if it has only one adapter attached and the new
5188          * adapter uses a different ViewHolder than the oldAdapter.
5189          *
5190          * @param oldAdapter The previous adapter instance. Will be detached.
5191          * @param newAdapter The new adapter instance. Will be attached.
5192          * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
5193          *                               ViewHolder and view types.
5194          */
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, boolean compatibleWithPrevious)5195         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
5196                 boolean compatibleWithPrevious) {
5197             if (oldAdapter != null) {
5198                 detach();
5199             }
5200             if (!compatibleWithPrevious && mAttachCount == 0) {
5201                 clear();
5202             }
5203             if (newAdapter != null) {
5204                 attach(newAdapter);
5205             }
5206         }
5207 
getScrapDataForType(int viewType)5208         private ScrapData getScrapDataForType(int viewType) {
5209             ScrapData scrapData = mScrap.get(viewType);
5210             if (scrapData == null) {
5211                 scrapData = new ScrapData();
5212                 mScrap.put(viewType, scrapData);
5213             }
5214             return scrapData;
5215         }
5216     }
5217 
5218     /**
5219      * Utility method for finding an internal RecyclerView, if present
5220      */
5221     @Nullable
findNestedRecyclerView(@onNull View view)5222     static RecyclerView findNestedRecyclerView(@NonNull View view) {
5223         if (!(view instanceof ViewGroup)) {
5224             return null;
5225         }
5226         if (view instanceof RecyclerView) {
5227             return (RecyclerView) view;
5228         }
5229         final ViewGroup parent = (ViewGroup) view;
5230         final int count = parent.getChildCount();
5231         for (int i = 0; i < count; i++) {
5232             final View child = parent.getChildAt(i);
5233             final RecyclerView descendant = findNestedRecyclerView(child);
5234             if (descendant != null) {
5235                 return descendant;
5236             }
5237         }
5238         return null;
5239     }
5240 
5241     /**
5242      * Utility method for clearing holder's internal RecyclerView, if present
5243      */
clearNestedRecyclerViewIfNotNested(@onNull ViewHolder holder)5244     static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
5245         if (holder.mNestedRecyclerView != null) {
5246             View item = holder.mNestedRecyclerView.get();
5247             while (item != null) {
5248                 if (item == holder.itemView) {
5249                     return; // match found, don't need to clear
5250                 }
5251 
5252                 ViewParent parent = item.getParent();
5253                 if (parent instanceof View) {
5254                     item = (View) parent;
5255                 } else {
5256                     item = null;
5257                 }
5258             }
5259             holder.mNestedRecyclerView = null; // not nested
5260         }
5261     }
5262 
5263     /**
5264      * Time base for deadline-aware work scheduling. Overridable for testing.
5265      *
5266      * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
5267      * isn't relevant.
5268      */
getNanoTime()5269     long getNanoTime() {
5270         if (ALLOW_THREAD_GAP_WORK) {
5271             return System.nanoTime();
5272         } else {
5273             return 0;
5274         }
5275     }
5276 
5277     /**
5278      * A Recycler is responsible for managing scrapped or detached item views for reuse.
5279      *
5280      * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
5281      * that has been marked for removal or reuse.</p>
5282      *
5283      * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
5284      * an adapter's data set representing the data at a given position or item ID.
5285      * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
5286      * If not, the view can be quickly reused by the LayoutManager with no further work.
5287      * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
5288      * may be repositioned by a LayoutManager without remeasurement.</p>
5289      */
5290     public final class Recycler {
5291         final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
5292         ArrayList<ViewHolder> mChangedScrap = null;
5293 
5294         final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
5295 
5296         private final List<ViewHolder>
5297                 mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
5298 
5299         private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
5300         int mViewCacheMax = DEFAULT_CACHE_SIZE;
5301 
5302         RecycledViewPool mRecyclerPool;
5303 
5304         private ViewCacheExtension mViewCacheExtension;
5305 
5306         static final int DEFAULT_CACHE_SIZE = 2;
5307 
5308         /**
5309          * Clear scrap views out of this recycler. Detached views contained within a
5310          * recycled view pool will remain.
5311          */
clear()5312         public void clear() {
5313             mAttachedScrap.clear();
5314             recycleAndClearCachedViews();
5315         }
5316 
5317         /**
5318          * Set the maximum number of detached, valid views we should retain for later use.
5319          *
5320          * @param viewCount Number of views to keep before sending views to the shared pool
5321          */
setViewCacheSize(int viewCount)5322         public void setViewCacheSize(int viewCount) {
5323             mRequestedCacheMax = viewCount;
5324             updateViewCacheSize();
5325         }
5326 
updateViewCacheSize()5327         void updateViewCacheSize() {
5328             int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
5329             mViewCacheMax = mRequestedCacheMax + extraCache;
5330 
5331             // first, try the views that can be recycled
5332             for (int i = mCachedViews.size() - 1;
5333                     i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
5334                 recycleCachedViewAt(i);
5335             }
5336         }
5337 
5338         /**
5339          * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
5340          *
5341          * @return List of ViewHolders in the scrap list.
5342          */
getScrapList()5343         public List<ViewHolder> getScrapList() {
5344             return mUnmodifiableAttachedScrap;
5345         }
5346 
5347         /**
5348          * Helper method for getViewForPosition.
5349          * <p>
5350          * Checks whether a given view holder can be used for the provided position.
5351          *
5352          * @param holder ViewHolder
5353          * @return true if ViewHolder matches the provided position, false otherwise
5354          */
validateViewHolderForOffsetPosition(ViewHolder holder)5355         boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
5356             // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
5357             // if it is not removed, verify the type and id.
5358             if (holder.isRemoved()) {
5359                 if (DEBUG && !mState.isPreLayout()) {
5360                     throw new IllegalStateException("should not receive a removed view unless it"
5361                             + " is pre layout");
5362                 }
5363                 return mState.isPreLayout();
5364             }
5365             if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
5366                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
5367                         + "adapter position" + holder);
5368             }
5369             if (!mState.isPreLayout()) {
5370                 // don't check type if it is pre-layout.
5371                 final int type = mAdapter.getItemViewType(holder.mPosition);
5372                 if (type != holder.getItemViewType()) {
5373                     return false;
5374                 }
5375             }
5376             if (mAdapter.hasStableIds()) {
5377                 return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
5378             }
5379             return true;
5380         }
5381 
5382         /**
5383          * Attempts to bind view, and account for relevant timing information. If
5384          * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
5385          *
5386          * @param holder Holder to be bound.
5387          * @param offsetPosition Position of item to be bound.
5388          * @param position Pre-layout position of item to be bound.
5389          * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5390          *                   complete. If FOREVER_NS is passed, this method will not fail to
5391          *                   bind the holder.
5392          * @return
5393          */
tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition, int position, long deadlineNs)5394         private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,
5395                 int position, long deadlineNs) {
5396             holder.mOwnerRecyclerView = RecyclerView.this;
5397             final int viewType = holder.getItemViewType();
5398             long startBindNs = getNanoTime();
5399             if (deadlineNs != FOREVER_NS
5400                     && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
5401                 // abort - we have a deadline we can't meet
5402                 return false;
5403             }
5404             mAdapter.bindViewHolder(holder, offsetPosition);
5405             long endBindNs = getNanoTime();
5406             mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
5407             attachAccessibilityDelegate(holder.itemView);
5408             if (mState.isPreLayout()) {
5409                 holder.mPreLayoutPosition = position;
5410             }
5411             return true;
5412         }
5413 
5414         /**
5415          * Binds the given View to the position. The View can be a View previously retrieved via
5416          * {@link #getViewForPosition(int)} or created by
5417          * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
5418          * <p>
5419          * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
5420          * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
5421          * wants to handle its own recycling logic.
5422          * <p>
5423          * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
5424          * you don't need to call this method unless you want to bind this View to another position.
5425          *
5426          * @param view The view to update.
5427          * @param position The position of the item to bind to this View.
5428          */
bindViewToPosition(View view, int position)5429         public void bindViewToPosition(View view, int position) {
5430             ViewHolder holder = getChildViewHolderInt(view);
5431             if (holder == null) {
5432                 throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
5433                         + " pass arbitrary views to this method, they should be created by the "
5434                         + "Adapter");
5435             }
5436             final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5437             if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5438                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5439                         + "position " + position + "(offset:" + offsetPosition + ")."
5440                         + "state:" + mState.getItemCount());
5441             }
5442             tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
5443 
5444             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5445             final LayoutParams rvLayoutParams;
5446             if (lp == null) {
5447                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5448                 holder.itemView.setLayoutParams(rvLayoutParams);
5449             } else if (!checkLayoutParams(lp)) {
5450                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5451                 holder.itemView.setLayoutParams(rvLayoutParams);
5452             } else {
5453                 rvLayoutParams = (LayoutParams) lp;
5454             }
5455 
5456             rvLayoutParams.mInsetsDirty = true;
5457             rvLayoutParams.mViewHolder = holder;
5458             rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
5459         }
5460 
5461         /**
5462          * RecyclerView provides artificial position range (item count) in pre-layout state and
5463          * automatically maps these positions to {@link Adapter} positions when
5464          * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
5465          * <p>
5466          * Usually, LayoutManager does not need to worry about this. However, in some cases, your
5467          * LayoutManager may need to call some custom component with item positions in which
5468          * case you need the actual adapter position instead of the pre layout position. You
5469          * can use this method to convert a pre-layout position to adapter (post layout) position.
5470          * <p>
5471          * Note that if the provided position belongs to a deleted ViewHolder, this method will
5472          * return -1.
5473          * <p>
5474          * Calling this method in post-layout state returns the same value back.
5475          *
5476          * @param position The pre-layout position to convert. Must be greater or equal to 0 and
5477          *                 less than {@link State#getItemCount()}.
5478          */
convertPreLayoutPositionToPostLayout(int position)5479         public int convertPreLayoutPositionToPostLayout(int position) {
5480             if (position < 0 || position >= mState.getItemCount()) {
5481                 throw new IndexOutOfBoundsException("invalid position " + position + ". State "
5482                         + "item count is " + mState.getItemCount());
5483             }
5484             if (!mState.isPreLayout()) {
5485                 return position;
5486             }
5487             return mAdapterHelper.findPositionOffset(position);
5488         }
5489 
5490         /**
5491          * Obtain a view initialized for the given position.
5492          *
5493          * This method should be used by {@link LayoutManager} implementations to obtain
5494          * views to represent data from an {@link Adapter}.
5495          * <p>
5496          * The Recycler may reuse a scrap or detached view from a shared pool if one is
5497          * available for the correct view type. If the adapter has not indicated that the
5498          * data at the given position has changed, the Recycler will attempt to hand back
5499          * a scrap view that was previously initialized for that data without rebinding.
5500          *
5501          * @param position Position to obtain a view for
5502          * @return A view representing the data at <code>position</code> from <code>adapter</code>
5503          */
getViewForPosition(int position)5504         public View getViewForPosition(int position) {
5505             return getViewForPosition(position, false);
5506         }
5507 
getViewForPosition(int position, boolean dryRun)5508         View getViewForPosition(int position, boolean dryRun) {
5509             return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
5510         }
5511 
5512         /**
5513          * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
5514          * cache, the RecycledViewPool, or creating it directly.
5515          * <p>
5516          * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
5517          * rather than constructing or binding a ViewHolder if it doesn't think it has time.
5518          * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
5519          * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
5520          * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
5521          *
5522          * @param position Position of ViewHolder to be returned.
5523          * @param dryRun True if the ViewHolder should not be removed from scrap/cache/
5524          * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5525          *                   complete. If FOREVER_NS is passed, this method will not fail to
5526          *                   create/bind the holder if needed.
5527          *
5528          * @return ViewHolder for requested position
5529          */
5530         @Nullable
tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs)5531         ViewHolder tryGetViewHolderForPositionByDeadline(int position,
5532                 boolean dryRun, long deadlineNs) {
5533             if (position < 0 || position >= mState.getItemCount()) {
5534                 throw new IndexOutOfBoundsException("Invalid item position " + position
5535                         + "(" + position + "). Item count:" + mState.getItemCount());
5536             }
5537             boolean fromScrapOrHiddenOrCache = false;
5538             ViewHolder holder = null;
5539             // 0) If there is a changed scrap, try to find from there
5540             if (mState.isPreLayout()) {
5541                 holder = getChangedScrapViewForPosition(position);
5542                 fromScrapOrHiddenOrCache = holder != null;
5543             }
5544             // 1) Find by position from scrap/hidden list/cache
5545             if (holder == null) {
5546                 holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
5547                 if (holder != null) {
5548                     if (!validateViewHolderForOffsetPosition(holder)) {
5549                         // recycle holder (and unscrap if relevant) since it can't be used
5550                         if (!dryRun) {
5551                             // we would like to recycle this but need to make sure it is not used by
5552                             // animation logic etc.
5553                             holder.addFlags(ViewHolder.FLAG_INVALID);
5554                             if (holder.isScrap()) {
5555                                 removeDetachedView(holder.itemView, false);
5556                                 holder.unScrap();
5557                             } else if (holder.wasReturnedFromScrap()) {
5558                                 holder.clearReturnedFromScrapFlag();
5559                             }
5560                             recycleViewHolderInternal(holder);
5561                         }
5562                         holder = null;
5563                     } else {
5564                         fromScrapOrHiddenOrCache = true;
5565                     }
5566                 }
5567             }
5568             if (holder == null) {
5569                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5570                 if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5571                     throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5572                             + "position " + position + "(offset:" + offsetPosition + ")."
5573                             + "state:" + mState.getItemCount());
5574                 }
5575 
5576                 final int type = mAdapter.getItemViewType(offsetPosition);
5577                 // 2) Find from scrap/cache via stable ids, if exists
5578                 if (mAdapter.hasStableIds()) {
5579                     holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
5580                             type, dryRun);
5581                     if (holder != null) {
5582                         // update position
5583                         holder.mPosition = offsetPosition;
5584                         fromScrapOrHiddenOrCache = true;
5585                     }
5586                 }
5587                 if (holder == null && mViewCacheExtension != null) {
5588                     // We are NOT sending the offsetPosition because LayoutManager does not
5589                     // know it.
5590                     final View view = mViewCacheExtension
5591                             .getViewForPositionAndType(this, position, type);
5592                     if (view != null) {
5593                         holder = getChildViewHolder(view);
5594                         if (holder == null) {
5595                             throw new IllegalArgumentException("getViewForPositionAndType returned"
5596                                     + " a view which does not have a ViewHolder");
5597                         } else if (holder.shouldIgnore()) {
5598                             throw new IllegalArgumentException("getViewForPositionAndType returned"
5599                                     + " a view that is ignored. You must call stopIgnoring before"
5600                                     + " returning this view.");
5601                         }
5602                     }
5603                 }
5604                 if (holder == null) { // fallback to pool
5605                     if (DEBUG) {
5606                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
5607                                 + position + ") fetching from shared pool");
5608                     }
5609                     holder = getRecycledViewPool().getRecycledView(type);
5610                     if (holder != null) {
5611                         holder.resetInternal();
5612                         if (FORCE_INVALIDATE_DISPLAY_LIST) {
5613                             invalidateDisplayListInt(holder);
5614                         }
5615                     }
5616                 }
5617                 if (holder == null) {
5618                     long start = getNanoTime();
5619                     if (deadlineNs != FOREVER_NS
5620                             && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
5621                         // abort - we have a deadline we can't meet
5622                         return null;
5623                     }
5624                     holder = mAdapter.createViewHolder(RecyclerView.this, type);
5625                     if (ALLOW_THREAD_GAP_WORK) {
5626                         // only bother finding nested RV if prefetching
5627                         RecyclerView innerView = findNestedRecyclerView(holder.itemView);
5628                         if (innerView != null) {
5629                             holder.mNestedRecyclerView = new WeakReference<>(innerView);
5630                         }
5631                     }
5632 
5633                     long end = getNanoTime();
5634                     mRecyclerPool.factorInCreateTime(type, end - start);
5635                     if (DEBUG) {
5636                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
5637                     }
5638                 }
5639             }
5640 
5641             // This is very ugly but the only place we can grab this information
5642             // before the View is rebound and returned to the LayoutManager for post layout ops.
5643             // We don't need this in pre-layout since the VH is not updated by the LM.
5644             if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
5645                     .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
5646                 holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5647                 if (mState.mRunSimpleAnimations) {
5648                     int changeFlags = ItemAnimator
5649                             .buildAdapterChangeFlagsForAnimations(holder);
5650                     changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
5651                     final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
5652                             holder, changeFlags, holder.getUnmodifiedPayloads());
5653                     recordAnimationInfoIfBouncedHiddenView(holder, info);
5654                 }
5655             }
5656 
5657             boolean bound = false;
5658             if (mState.isPreLayout() && holder.isBound()) {
5659                 // do not update unless we absolutely have to.
5660                 holder.mPreLayoutPosition = position;
5661             } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
5662                 if (DEBUG && holder.isRemoved()) {
5663                     throw new IllegalStateException("Removed holder should be bound and it should"
5664                             + " come here only in pre-layout. Holder: " + holder);
5665                 }
5666                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5667                 bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
5668             }
5669 
5670             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5671             final LayoutParams rvLayoutParams;
5672             if (lp == null) {
5673                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5674                 holder.itemView.setLayoutParams(rvLayoutParams);
5675             } else if (!checkLayoutParams(lp)) {
5676                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5677                 holder.itemView.setLayoutParams(rvLayoutParams);
5678             } else {
5679                 rvLayoutParams = (LayoutParams) lp;
5680             }
5681             rvLayoutParams.mViewHolder = holder;
5682             rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
5683             return holder;
5684         }
5685 
attachAccessibilityDelegate(View itemView)5686         private void attachAccessibilityDelegate(View itemView) {
5687             if (isAccessibilityEnabled()) {
5688                 if (itemView.getImportantForAccessibility()
5689                         == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
5690                     itemView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
5691                 }
5692 
5693                 if (itemView.getAccessibilityDelegate() == null) {
5694                     itemView.setAccessibilityDelegate(mAccessibilityDelegate.getItemDelegate());
5695                 }
5696             }
5697         }
5698 
invalidateDisplayListInt(ViewHolder holder)5699         private void invalidateDisplayListInt(ViewHolder holder) {
5700             if (holder.itemView instanceof ViewGroup) {
5701                 invalidateDisplayListInt((ViewGroup) holder.itemView, false);
5702             }
5703         }
5704 
invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis)5705         private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
5706             for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
5707                 final View view = viewGroup.getChildAt(i);
5708                 if (view instanceof ViewGroup) {
5709                     invalidateDisplayListInt((ViewGroup) view, true);
5710                 }
5711             }
5712             if (!invalidateThis) {
5713                 return;
5714             }
5715             // we need to force it to become invisible
5716             if (viewGroup.getVisibility() == View.INVISIBLE) {
5717                 viewGroup.setVisibility(View.VISIBLE);
5718                 viewGroup.setVisibility(View.INVISIBLE);
5719             } else {
5720                 final int visibility = viewGroup.getVisibility();
5721                 viewGroup.setVisibility(View.INVISIBLE);
5722                 viewGroup.setVisibility(visibility);
5723             }
5724         }
5725 
5726         /**
5727          * Recycle a detached view. The specified view will be added to a pool of views
5728          * for later rebinding and reuse.
5729          *
5730          * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
5731          * View is scrapped, it will be removed from scrap list.</p>
5732          *
5733          * @param view Removed view for recycling
5734          * @see LayoutManager#removeAndRecycleView(View, Recycler)
5735          */
recycleView(View view)5736         public void recycleView(View view) {
5737             // This public recycle method tries to make view recycle-able since layout manager
5738             // intended to recycle this view (e.g. even if it is in scrap or change cache)
5739             ViewHolder holder = getChildViewHolderInt(view);
5740             if (holder.isTmpDetached()) {
5741                 removeDetachedView(view, false);
5742             }
5743             if (holder.isScrap()) {
5744                 holder.unScrap();
5745             } else if (holder.wasReturnedFromScrap()) {
5746                 holder.clearReturnedFromScrapFlag();
5747             }
5748             recycleViewHolderInternal(holder);
5749         }
5750 
5751         /**
5752          * Internally, use this method instead of {@link #recycleView(android.view.View)} to
5753          * catch potential bugs.
5754          * @param view
5755          */
recycleViewInternal(View view)5756         void recycleViewInternal(View view) {
5757             recycleViewHolderInternal(getChildViewHolderInt(view));
5758         }
5759 
recycleAndClearCachedViews()5760         void recycleAndClearCachedViews() {
5761             final int count = mCachedViews.size();
5762             for (int i = count - 1; i >= 0; i--) {
5763                 recycleCachedViewAt(i);
5764             }
5765             mCachedViews.clear();
5766             if (ALLOW_THREAD_GAP_WORK) {
5767                 mPrefetchRegistry.clearPrefetchPositions();
5768             }
5769         }
5770 
5771         /**
5772          * Recycles a cached view and removes the view from the list. Views are added to cache
5773          * if and only if they are recyclable, so this method does not check it again.
5774          * <p>
5775          * A small exception to this rule is when the view does not have an animator reference
5776          * but transient state is true (due to animations created outside ItemAnimator). In that
5777          * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
5778          * still recyclable since Adapter wants to do so.
5779          *
5780          * @param cachedViewIndex The index of the view in cached views list
5781          */
recycleCachedViewAt(int cachedViewIndex)5782         void recycleCachedViewAt(int cachedViewIndex) {
5783             if (DEBUG) {
5784                 Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
5785             }
5786             ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
5787             if (DEBUG) {
5788                 Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
5789             }
5790             addViewHolderToRecycledViewPool(viewHolder, true);
5791             mCachedViews.remove(cachedViewIndex);
5792         }
5793 
5794         /**
5795          * internal implementation checks if view is scrapped or attached and throws an exception
5796          * if so.
5797          * Public version un-scraps before calling recycle.
5798          */
recycleViewHolderInternal(ViewHolder holder)5799         void recycleViewHolderInternal(ViewHolder holder) {
5800             if (holder.isScrap() || holder.itemView.getParent() != null) {
5801                 throw new IllegalArgumentException(
5802                         "Scrapped or attached views may not be recycled. isScrap:"
5803                                 + holder.isScrap() + " isAttached:"
5804                                 + (holder.itemView.getParent() != null));
5805             }
5806 
5807             if (holder.isTmpDetached()) {
5808                 throw new IllegalArgumentException("Tmp detached view should be removed "
5809                         + "from RecyclerView before it can be recycled: " + holder);
5810             }
5811 
5812             if (holder.shouldIgnore()) {
5813                 throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
5814                         + " should first call stopIgnoringView(view) before calling recycle.");
5815             }
5816             //noinspection unchecked
5817             final boolean transientStatePreventsRecycling = holder
5818                     .doesTransientStatePreventRecycling();
5819             final boolean forceRecycle = mAdapter != null
5820                     && transientStatePreventsRecycling
5821                     && mAdapter.onFailedToRecycleView(holder);
5822             boolean cached = false;
5823             boolean recycled = false;
5824             if (DEBUG && mCachedViews.contains(holder)) {
5825                 throw new IllegalArgumentException("cached view received recycle internal? "
5826                         + holder);
5827             }
5828             if (forceRecycle || holder.isRecyclable()) {
5829                 if (mViewCacheMax > 0
5830                         && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
5831                                 | ViewHolder.FLAG_REMOVED
5832                                 | ViewHolder.FLAG_UPDATE
5833                                 | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
5834                     // Retire oldest cached view
5835                     int cachedViewSize = mCachedViews.size();
5836                     if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
5837                         recycleCachedViewAt(0);
5838                         cachedViewSize--;
5839                     }
5840 
5841                     int targetCacheIndex = cachedViewSize;
5842                     if (ALLOW_THREAD_GAP_WORK
5843                             && cachedViewSize > 0
5844                             && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
5845                         // when adding the view, skip past most recently prefetched views
5846                         int cacheIndex = cachedViewSize - 1;
5847                         while (cacheIndex >= 0) {
5848                             int cachedPos = mCachedViews.get(cacheIndex).mPosition;
5849                             if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
5850                                 break;
5851                             }
5852                             cacheIndex--;
5853                         }
5854                         targetCacheIndex = cacheIndex + 1;
5855                     }
5856                     mCachedViews.add(targetCacheIndex, holder);
5857                     cached = true;
5858                 }
5859                 if (!cached) {
5860                     addViewHolderToRecycledViewPool(holder, true);
5861                     recycled = true;
5862                 }
5863             } else {
5864                 // NOTE: A view can fail to be recycled when it is scrolled off while an animation
5865                 // runs. In this case, the item is eventually recycled by
5866                 // ItemAnimatorRestoreListener#onAnimationFinished.
5867 
5868                 // TODO: consider cancelling an animation when an item is removed scrollBy,
5869                 // to return it to the pool faster
5870                 if (DEBUG) {
5871                     Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
5872                             + "re-visit here. We are still removing it from animation lists");
5873                 }
5874             }
5875             // even if the holder is not removed, we still call this method so that it is removed
5876             // from view holder lists.
5877             mViewInfoStore.removeViewHolder(holder);
5878             if (!cached && !recycled && transientStatePreventsRecycling) {
5879                 holder.mOwnerRecyclerView = null;
5880             }
5881         }
5882 
5883         /**
5884          * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
5885          *
5886          * Pass false to dispatchRecycled for views that have not been bound.
5887          *
5888          * @param holder Holder to be added to the pool.
5889          * @param dispatchRecycled True to dispatch View recycled callbacks.
5890          */
addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled)5891         void addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) {
5892             clearNestedRecyclerViewIfNotNested(holder);
5893             holder.itemView.setAccessibilityDelegate(null);
5894             if (dispatchRecycled) {
5895                 dispatchViewRecycled(holder);
5896             }
5897             holder.mOwnerRecyclerView = null;
5898             getRecycledViewPool().putRecycledView(holder);
5899         }
5900 
5901         /**
5902          * Used as a fast path for unscrapping and recycling a view during a bulk operation.
5903          * The caller must call {@link #clearScrap()} when it's done to update the recycler's
5904          * internal bookkeeping.
5905          */
quickRecycleScrapView(View view)5906         void quickRecycleScrapView(View view) {
5907             final ViewHolder holder = getChildViewHolderInt(view);
5908             holder.mScrapContainer = null;
5909             holder.mInChangeScrap = false;
5910             holder.clearReturnedFromScrapFlag();
5911             recycleViewHolderInternal(holder);
5912         }
5913 
5914         /**
5915          * Mark an attached view as scrap.
5916          *
5917          * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
5918          * for rebinding and reuse. Requests for a view for a given position may return a
5919          * reused or rebound scrap view instance.</p>
5920          *
5921          * @param view View to scrap
5922          */
scrapView(View view)5923         void scrapView(View view) {
5924             final ViewHolder holder = getChildViewHolderInt(view);
5925             if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
5926                     || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
5927                 if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
5928                     throw new IllegalArgumentException("Called scrap view with an invalid view."
5929                             + " Invalid views cannot be reused from scrap, they should rebound from"
5930                             + " recycler pool.");
5931                 }
5932                 holder.setScrapContainer(this, false);
5933                 mAttachedScrap.add(holder);
5934             } else {
5935                 if (mChangedScrap == null) {
5936                     mChangedScrap = new ArrayList<ViewHolder>();
5937                 }
5938                 holder.setScrapContainer(this, true);
5939                 mChangedScrap.add(holder);
5940             }
5941         }
5942 
5943         /**
5944          * Remove a previously scrapped view from the pool of eligible scrap.
5945          *
5946          * <p>This view will no longer be eligible for reuse until re-scrapped or
5947          * until it is explicitly removed and recycled.</p>
5948          */
unscrapView(ViewHolder holder)5949         void unscrapView(ViewHolder holder) {
5950             if (holder.mInChangeScrap) {
5951                 mChangedScrap.remove(holder);
5952             } else {
5953                 mAttachedScrap.remove(holder);
5954             }
5955             holder.mScrapContainer = null;
5956             holder.mInChangeScrap = false;
5957             holder.clearReturnedFromScrapFlag();
5958         }
5959 
getScrapCount()5960         int getScrapCount() {
5961             return mAttachedScrap.size();
5962         }
5963 
getScrapViewAt(int index)5964         View getScrapViewAt(int index) {
5965             return mAttachedScrap.get(index).itemView;
5966         }
5967 
clearScrap()5968         void clearScrap() {
5969             mAttachedScrap.clear();
5970             if (mChangedScrap != null) {
5971                 mChangedScrap.clear();
5972             }
5973         }
5974 
getChangedScrapViewForPosition(int position)5975         ViewHolder getChangedScrapViewForPosition(int position) {
5976             // If pre-layout, check the changed scrap for an exact match.
5977             final int changedScrapSize;
5978             if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
5979                 return null;
5980             }
5981             // find by position
5982             for (int i = 0; i < changedScrapSize; i++) {
5983                 final ViewHolder holder = mChangedScrap.get(i);
5984                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
5985                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5986                     return holder;
5987                 }
5988             }
5989             // find by id
5990             if (mAdapter.hasStableIds()) {
5991                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5992                 if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
5993                     final long id = mAdapter.getItemId(offsetPosition);
5994                     for (int i = 0; i < changedScrapSize; i++) {
5995                         final ViewHolder holder = mChangedScrap.get(i);
5996                         if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
5997                             holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5998                             return holder;
5999                         }
6000                     }
6001                 }
6002             }
6003             return null;
6004         }
6005 
6006         /**
6007          * Returns a view for the position either from attach scrap, hidden children, or cache.
6008          *
6009          * @param position Item position
6010          * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
6011          * @return a ViewHolder that can be re-used for this position.
6012          */
getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun)6013         ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
6014             final int scrapCount = mAttachedScrap.size();
6015 
6016             // Try first for an exact, non-invalid match from scrap.
6017             for (int i = 0; i < scrapCount; i++) {
6018                 final ViewHolder holder = mAttachedScrap.get(i);
6019                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
6020                         && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
6021                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6022                     return holder;
6023                 }
6024             }
6025 
6026             if (!dryRun) {
6027                 View view = mChildHelper.findHiddenNonRemovedView(position);
6028                 if (view != null) {
6029                     // This View is good to be used. We just need to unhide, detach and move to the
6030                     // scrap list.
6031                     final ViewHolder vh = getChildViewHolderInt(view);
6032                     mChildHelper.unhide(view);
6033                     int layoutIndex = mChildHelper.indexOfChild(view);
6034                     if (layoutIndex == RecyclerView.NO_POSITION) {
6035                         throw new IllegalStateException("layout index should not be -1 after "
6036                                 + "unhiding a view:" + vh);
6037                     }
6038                     mChildHelper.detachViewFromParent(layoutIndex);
6039                     scrapView(view);
6040                     vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
6041                             | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
6042                     return vh;
6043                 }
6044             }
6045 
6046             // Search in our first-level recycled view cache.
6047             final int cacheSize = mCachedViews.size();
6048             for (int i = 0; i < cacheSize; i++) {
6049                 final ViewHolder holder = mCachedViews.get(i);
6050                 // invalid view holders may be in cache if adapter has stable ids as they can be
6051                 // retrieved via getScrapOrCachedViewForId
6052                 if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
6053                     if (!dryRun) {
6054                         mCachedViews.remove(i);
6055                     }
6056                     if (DEBUG) {
6057                         Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
6058                                 + ") found match in cache: " + holder);
6059                     }
6060                     return holder;
6061                 }
6062             }
6063             return null;
6064         }
6065 
getScrapOrCachedViewForId(long id, int type, boolean dryRun)6066         ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
6067             // Look in our attached views first
6068             final int count = mAttachedScrap.size();
6069             for (int i = count - 1; i >= 0; i--) {
6070                 final ViewHolder holder = mAttachedScrap.get(i);
6071                 if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
6072                     if (type == holder.getItemViewType()) {
6073                         holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6074                         if (holder.isRemoved()) {
6075                             // this might be valid in two cases:
6076                             // > item is removed but we are in pre-layout pass
6077                             // >> do nothing. return as is. make sure we don't rebind
6078                             // > item is removed then added to another position and we are in
6079                             // post layout.
6080                             // >> remove removed and invalid flags, add update flag to rebind
6081                             // because item was invisible to us and we don't know what happened in
6082                             // between.
6083                             if (!mState.isPreLayout()) {
6084                                 holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
6085                                         | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
6086                             }
6087                         }
6088                         return holder;
6089                     } else if (!dryRun) {
6090                         // if we are running animations, it is actually better to keep it in scrap
6091                         // but this would force layout manager to lay it out which would be bad.
6092                         // Recycle this scrap. Type mismatch.
6093                         mAttachedScrap.remove(i);
6094                         removeDetachedView(holder.itemView, false);
6095                         quickRecycleScrapView(holder.itemView);
6096                     }
6097                 }
6098             }
6099 
6100             // Search the first-level cache
6101             final int cacheSize = mCachedViews.size();
6102             for (int i = cacheSize - 1; i >= 0; i--) {
6103                 final ViewHolder holder = mCachedViews.get(i);
6104                 if (holder.getItemId() == id) {
6105                     if (type == holder.getItemViewType()) {
6106                         if (!dryRun) {
6107                             mCachedViews.remove(i);
6108                         }
6109                         return holder;
6110                     } else if (!dryRun) {
6111                         recycleCachedViewAt(i);
6112                         return null;
6113                     }
6114                 }
6115             }
6116             return null;
6117         }
6118 
dispatchViewRecycled(ViewHolder holder)6119         void dispatchViewRecycled(ViewHolder holder) {
6120             if (mRecyclerListener != null) {
6121                 mRecyclerListener.onViewRecycled(holder);
6122             }
6123             if (mAdapter != null) {
6124                 mAdapter.onViewRecycled(holder);
6125             }
6126             if (mState != null) {
6127                 mViewInfoStore.removeViewHolder(holder);
6128             }
6129             if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
6130         }
6131 
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, boolean compatibleWithPrevious)6132         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
6133                 boolean compatibleWithPrevious) {
6134             clear();
6135             getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
6136         }
6137 
offsetPositionRecordsForMove(int from, int to)6138         void offsetPositionRecordsForMove(int from, int to) {
6139             final int start, end, inBetweenOffset;
6140             if (from < to) {
6141                 start = from;
6142                 end = to;
6143                 inBetweenOffset = -1;
6144             } else {
6145                 start = to;
6146                 end = from;
6147                 inBetweenOffset = 1;
6148             }
6149             final int cachedCount = mCachedViews.size();
6150             for (int i = 0; i < cachedCount; i++) {
6151                 final ViewHolder holder = mCachedViews.get(i);
6152                 if (holder == null || holder.mPosition < start || holder.mPosition > end) {
6153                     continue;
6154                 }
6155                 if (holder.mPosition == from) {
6156                     holder.offsetPosition(to - from, false);
6157                 } else {
6158                     holder.offsetPosition(inBetweenOffset, false);
6159                 }
6160                 if (DEBUG) {
6161                     Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
6162                             + holder);
6163                 }
6164             }
6165         }
6166 
offsetPositionRecordsForInsert(int insertedAt, int count)6167         void offsetPositionRecordsForInsert(int insertedAt, int count) {
6168             final int cachedCount = mCachedViews.size();
6169             for (int i = 0; i < cachedCount; i++) {
6170                 final ViewHolder holder = mCachedViews.get(i);
6171                 if (holder != null && holder.mPosition >= insertedAt) {
6172                     if (DEBUG) {
6173                         Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder "
6174                                 + holder + " now at position " + (holder.mPosition + count));
6175                     }
6176                     holder.offsetPosition(count, true);
6177                 }
6178             }
6179         }
6180 
6181         /**
6182          * @param removedFrom Remove start index
6183          * @param count Remove count
6184          * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
6185          *                         false, they'll be applied before the second layout pass
6186          */
offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout)6187         void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
6188             final int removedEnd = removedFrom + count;
6189             final int cachedCount = mCachedViews.size();
6190             for (int i = cachedCount - 1; i >= 0; i--) {
6191                 final ViewHolder holder = mCachedViews.get(i);
6192                 if (holder != null) {
6193                     if (holder.mPosition >= removedEnd) {
6194                         if (DEBUG) {
6195                             Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
6196                                     + " holder " + holder + " now at position "
6197                                     + (holder.mPosition - count));
6198                         }
6199                         holder.offsetPosition(-count, applyToPreLayout);
6200                     } else if (holder.mPosition >= removedFrom) {
6201                         // Item for this view was removed. Dump it from the cache.
6202                         holder.addFlags(ViewHolder.FLAG_REMOVED);
6203                         recycleCachedViewAt(i);
6204                     }
6205                 }
6206             }
6207         }
6208 
setViewCacheExtension(ViewCacheExtension extension)6209         void setViewCacheExtension(ViewCacheExtension extension) {
6210             mViewCacheExtension = extension;
6211         }
6212 
setRecycledViewPool(RecycledViewPool pool)6213         void setRecycledViewPool(RecycledViewPool pool) {
6214             if (mRecyclerPool != null) {
6215                 mRecyclerPool.detach();
6216             }
6217             mRecyclerPool = pool;
6218             if (pool != null) {
6219                 mRecyclerPool.attach(getAdapter());
6220             }
6221         }
6222 
getRecycledViewPool()6223         RecycledViewPool getRecycledViewPool() {
6224             if (mRecyclerPool == null) {
6225                 mRecyclerPool = new RecycledViewPool();
6226             }
6227             return mRecyclerPool;
6228         }
6229 
viewRangeUpdate(int positionStart, int itemCount)6230         void viewRangeUpdate(int positionStart, int itemCount) {
6231             final int positionEnd = positionStart + itemCount;
6232             final int cachedCount = mCachedViews.size();
6233             for (int i = cachedCount - 1; i >= 0; i--) {
6234                 final ViewHolder holder = mCachedViews.get(i);
6235                 if (holder == null) {
6236                     continue;
6237                 }
6238 
6239                 final int pos = holder.getLayoutPosition();
6240                 if (pos >= positionStart && pos < positionEnd) {
6241                     holder.addFlags(ViewHolder.FLAG_UPDATE);
6242                     recycleCachedViewAt(i);
6243                     // cached views should not be flagged as changed because this will cause them
6244                     // to animate when they are returned from cache.
6245                 }
6246             }
6247         }
6248 
setAdapterPositionsAsUnknown()6249         void setAdapterPositionsAsUnknown() {
6250             final int cachedCount = mCachedViews.size();
6251             for (int i = 0; i < cachedCount; i++) {
6252                 final ViewHolder holder = mCachedViews.get(i);
6253                 if (holder != null) {
6254                     holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
6255                 }
6256             }
6257         }
6258 
markKnownViewsInvalid()6259         void markKnownViewsInvalid() {
6260             if (mAdapter != null && mAdapter.hasStableIds()) {
6261                 final int cachedCount = mCachedViews.size();
6262                 for (int i = 0; i < cachedCount; i++) {
6263                     final ViewHolder holder = mCachedViews.get(i);
6264                     if (holder != null) {
6265                         holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
6266                         holder.addChangePayload(null);
6267                     }
6268                 }
6269             } else {
6270                 // we cannot re-use cached views in this case. Recycle them all
6271                 recycleAndClearCachedViews();
6272             }
6273         }
6274 
clearOldPositions()6275         void clearOldPositions() {
6276             final int cachedCount = mCachedViews.size();
6277             for (int i = 0; i < cachedCount; i++) {
6278                 final ViewHolder holder = mCachedViews.get(i);
6279                 holder.clearOldPosition();
6280             }
6281             final int scrapCount = mAttachedScrap.size();
6282             for (int i = 0; i < scrapCount; i++) {
6283                 mAttachedScrap.get(i).clearOldPosition();
6284             }
6285             if (mChangedScrap != null) {
6286                 final int changedScrapCount = mChangedScrap.size();
6287                 for (int i = 0; i < changedScrapCount; i++) {
6288                     mChangedScrap.get(i).clearOldPosition();
6289                 }
6290             }
6291         }
6292 
markItemDecorInsetsDirty()6293         void markItemDecorInsetsDirty() {
6294             final int cachedCount = mCachedViews.size();
6295             for (int i = 0; i < cachedCount; i++) {
6296                 final ViewHolder holder = mCachedViews.get(i);
6297                 LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
6298                 if (layoutParams != null) {
6299                     layoutParams.mInsetsDirty = true;
6300                 }
6301             }
6302         }
6303     }
6304 
6305     /**
6306      * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
6307      * be controlled by the developer.
6308      * <p>
6309      * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
6310      * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
6311      * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
6312      * {@link RecycledViewPool}.
6313      * <p>
6314      * Note that, Recycler never sends Views to this method to be cached. It is developers
6315      * responsibility to decide whether they want to keep their Views in this custom cache or let
6316      * the default recycling policy handle it.
6317      */
6318     public abstract static class ViewCacheExtension {
6319 
6320         /**
6321          * Returns a View that can be binded to the given Adapter position.
6322          * <p>
6323          * This method should <b>not</b> create a new View. Instead, it is expected to return
6324          * an already created View that can be re-used for the given type and position.
6325          * If the View is marked as ignored, it should first call
6326          * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
6327          * <p>
6328          * RecyclerView will re-bind the returned View to the position if necessary.
6329          *
6330          * @param recycler The Recycler that can be used to bind the View
6331          * @param position The adapter position
6332          * @param type     The type of the View, defined by adapter
6333          * @return A View that is bound to the given position or NULL if there is no View to re-use
6334          * @see LayoutManager#ignoreView(View)
6335          */
6336         public abstract View getViewForPositionAndType(Recycler recycler, int position, int type);
6337     }
6338 
6339     /**
6340      * Base class for an Adapter
6341      *
6342      * <p>Adapters provide a binding from an app-specific data set to views that are displayed
6343      * within a {@link RecyclerView}.</p>
6344      *
6345      * @param <VH> A class that extends ViewHolder that will be used by the adapter.
6346      */
6347     public abstract static class Adapter<VH extends ViewHolder> {
6348         private final AdapterDataObservable mObservable = new AdapterDataObservable();
6349         private boolean mHasStableIds = false;
6350 
6351         /**
6352          * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
6353          * an item.
6354          * <p>
6355          * This new ViewHolder should be constructed with a new View that can represent the items
6356          * of the given type. You can either create a new View manually or inflate it from an XML
6357          * layout file.
6358          * <p>
6359          * The new ViewHolder will be used to display items of the adapter using
6360          * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
6361          * different items in the data set, it is a good idea to cache references to sub views of
6362          * the View to avoid unnecessary {@link View#findViewById(int)} calls.
6363          *
6364          * @param parent The ViewGroup into which the new View will be added after it is bound to
6365          *               an adapter position.
6366          * @param viewType The view type of the new View.
6367          *
6368          * @return A new ViewHolder that holds a View of the given view type.
6369          * @see #getItemViewType(int)
6370          * @see #onBindViewHolder(ViewHolder, int)
6371          */
6372         public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
6373 
6374         /**
6375          * Called by RecyclerView to display the data at the specified position. This method should
6376          * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
6377          * position.
6378          * <p>
6379          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6380          * again if the position of the item changes in the data set unless the item itself is
6381          * invalidated or the new position cannot be determined. For this reason, you should only
6382          * use the <code>position</code> parameter while acquiring the related data item inside
6383          * this method and should not keep a copy of it. If you need the position of an item later
6384          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6385          * have the updated adapter position.
6386          *
6387          * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
6388          * handle efficient partial bind.
6389          *
6390          * @param holder The ViewHolder which should be updated to represent the contents of the
6391          *        item at the given position in the data set.
6392          * @param position The position of the item within the adapter's data set.
6393          */
6394         public abstract void onBindViewHolder(VH holder, int position);
6395 
6396         /**
6397          * Called by RecyclerView to display the data at the specified position. This method
6398          * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
6399          * the given position.
6400          * <p>
6401          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6402          * again if the position of the item changes in the data set unless the item itself is
6403          * invalidated or the new position cannot be determined. For this reason, you should only
6404          * use the <code>position</code> parameter while acquiring the related data item inside
6405          * this method and should not keep a copy of it. If you need the position of an item later
6406          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6407          * have the updated adapter position.
6408          * <p>
6409          * Partial bind vs full bind:
6410          * <p>
6411          * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
6412          * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
6413          * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
6414          * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
6415          * Adapter should not assume that the payload passed in notify methods will be received by
6416          * onBindViewHolder().  For example when the view is not attached to the screen, the
6417          * payload in notifyItemChange() will be simply dropped.
6418          *
6419          * @param holder The ViewHolder which should be updated to represent the contents of the
6420          *               item at the given position in the data set.
6421          * @param position The position of the item within the adapter's data set.
6422          * @param payloads A non-null list of merged payloads. Can be empty list if requires full
6423          *                 update.
6424          */
onBindViewHolder(VH holder, int position, List<Object> payloads)6425         public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
6426             onBindViewHolder(holder, position);
6427         }
6428 
6429         /**
6430          * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
6431          * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
6432          *
6433          * @see #onCreateViewHolder(ViewGroup, int)
6434          */
createViewHolder(ViewGroup parent, int viewType)6435         public final VH createViewHolder(ViewGroup parent, int viewType) {
6436             Trace.beginSection(TRACE_CREATE_VIEW_TAG);
6437             final VH holder = onCreateViewHolder(parent, viewType);
6438             holder.mItemViewType = viewType;
6439             Trace.endSection();
6440             return holder;
6441         }
6442 
6443         /**
6444          * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
6445          * {@link ViewHolder} contents with the item at the given position and also sets up some
6446          * private fields to be used by RecyclerView.
6447          *
6448          * @see #onBindViewHolder(ViewHolder, int)
6449          */
bindViewHolder(VH holder, int position)6450         public final void bindViewHolder(VH holder, int position) {
6451             holder.mPosition = position;
6452             if (hasStableIds()) {
6453                 holder.mItemId = getItemId(position);
6454             }
6455             holder.setFlags(ViewHolder.FLAG_BOUND,
6456                     ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
6457                             | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
6458             Trace.beginSection(TRACE_BIND_VIEW_TAG);
6459             onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
6460             holder.clearPayload();
6461             final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
6462             if (layoutParams instanceof RecyclerView.LayoutParams) {
6463                 ((LayoutParams) layoutParams).mInsetsDirty = true;
6464             }
6465             Trace.endSection();
6466         }
6467 
6468         /**
6469          * Return the view type of the item at <code>position</code> for the purposes
6470          * of view recycling.
6471          *
6472          * <p>The default implementation of this method returns 0, making the assumption of
6473          * a single view type for the adapter. Unlike ListView adapters, types need not
6474          * be contiguous. Consider using id resources to uniquely identify item view types.
6475          *
6476          * @param position position to query
6477          * @return integer value identifying the type of the view needed to represent the item at
6478          *                 <code>position</code>. Type codes need not be contiguous.
6479          */
getItemViewType(int position)6480         public int getItemViewType(int position) {
6481             return 0;
6482         }
6483 
6484         /**
6485          * Indicates whether each item in the data set can be represented with a unique identifier
6486          * of type {@link java.lang.Long}.
6487          *
6488          * @param hasStableIds Whether items in data set have unique identifiers or not.
6489          * @see #hasStableIds()
6490          * @see #getItemId(int)
6491          */
setHasStableIds(boolean hasStableIds)6492         public void setHasStableIds(boolean hasStableIds) {
6493             if (hasObservers()) {
6494                 throw new IllegalStateException("Cannot change whether this adapter has "
6495                         + "stable IDs while the adapter has registered observers.");
6496             }
6497             mHasStableIds = hasStableIds;
6498         }
6499 
6500         /**
6501          * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
6502          * would return false this method should return {@link #NO_ID}. The default implementation
6503          * of this method returns {@link #NO_ID}.
6504          *
6505          * @param position Adapter position to query
6506          * @return the stable ID of the item at position
6507          */
getItemId(int position)6508         public long getItemId(int position) {
6509             return NO_ID;
6510         }
6511 
6512         /**
6513          * Returns the total number of items in the data set held by the adapter.
6514          *
6515          * @return The total number of items in this adapter.
6516          */
6517         public abstract int getItemCount();
6518 
6519         /**
6520          * Returns true if this adapter publishes a unique <code>long</code> value that can
6521          * act as a key for the item at a given position in the data set. If that item is relocated
6522          * in the data set, the ID returned for that item should be the same.
6523          *
6524          * @return true if this adapter's items have stable IDs
6525          */
hasStableIds()6526         public final boolean hasStableIds() {
6527             return mHasStableIds;
6528         }
6529 
6530         /**
6531          * Called when a view created by this adapter has been recycled.
6532          *
6533          * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
6534          * needs to be attached to its parent {@link RecyclerView}. This can be because it has
6535          * fallen out of visibility or a set of cached views represented by views still
6536          * attached to the parent RecyclerView. If an item view has large or expensive data
6537          * bound to it such as large bitmaps, this may be a good place to release those
6538          * resources.</p>
6539          * <p>
6540          * RecyclerView calls this method right before clearing ViewHolder's internal data and
6541          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
6542          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
6543          * its adapter position.
6544          *
6545          * @param holder The ViewHolder for the view being recycled
6546          */
onViewRecycled(VH holder)6547         public void onViewRecycled(VH holder) {
6548         }
6549 
6550         /**
6551          * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
6552          * due to its transient state. Upon receiving this callback, Adapter can clear the
6553          * animation(s) that effect the View's transient state and return <code>true</code> so that
6554          * the View can be recycled. Keep in mind that the View in question is already removed from
6555          * the RecyclerView.
6556          * <p>
6557          * In some cases, it is acceptable to recycle a View although it has transient state. Most
6558          * of the time, this is a case where the transient state will be cleared in
6559          * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
6560          * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
6561          * value of this method to decide whether the View should be recycled or not.
6562          * <p>
6563          * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
6564          * should never receive this callback because RecyclerView keeps those Views as children
6565          * until their animations are complete. This callback is useful when children of the item
6566          * views create animations which may not be easy to implement using an {@link ItemAnimator}.
6567          * <p>
6568          * You should <em>never</em> fix this issue by calling
6569          * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
6570          * <code>holder.itemView.setHasTransientState(true);</code>. Each
6571          * <code>View.setHasTransientState(true)</code> call must be matched by a
6572          * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
6573          * may become inconsistent. You should always prefer to end or cancel animations that are
6574          * triggering the transient state instead of handling it manually.
6575          *
6576          * @param holder The ViewHolder containing the View that could not be recycled due to its
6577          *               transient state.
6578          * @return True if the View should be recycled, false otherwise. Note that if this method
6579          * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
6580          * the View and recycle it regardless. If this method returns <code>false</code>,
6581          * RecyclerView will check the View's transient state again before giving a final decision.
6582          * Default implementation returns false.
6583          */
onFailedToRecycleView(VH holder)6584         public boolean onFailedToRecycleView(VH holder) {
6585             return false;
6586         }
6587 
6588         /**
6589          * Called when a view created by this adapter has been attached to a window.
6590          *
6591          * <p>This can be used as a reasonable signal that the view is about to be seen
6592          * by the user. If the adapter previously freed any resources in
6593          * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
6594          * those resources should be restored here.</p>
6595          *
6596          * @param holder Holder of the view being attached
6597          */
onViewAttachedToWindow(VH holder)6598         public void onViewAttachedToWindow(VH holder) {
6599         }
6600 
6601         /**
6602          * Called when a view created by this adapter has been detached from its window.
6603          *
6604          * <p>Becoming detached from the window is not necessarily a permanent condition;
6605          * the consumer of an Adapter's views may choose to cache views offscreen while they
6606          * are not visible, attaching and detaching them as appropriate.</p>
6607          *
6608          * @param holder Holder of the view being detached
6609          */
onViewDetachedFromWindow(VH holder)6610         public void onViewDetachedFromWindow(VH holder) {
6611         }
6612 
6613         /**
6614          * Returns true if one or more observers are attached to this adapter.
6615          *
6616          * @return true if this adapter has observers
6617          */
hasObservers()6618         public final boolean hasObservers() {
6619             return mObservable.hasObservers();
6620         }
6621 
6622         /**
6623          * Register a new observer to listen for data changes.
6624          *
6625          * <p>The adapter may publish a variety of events describing specific changes.
6626          * Not all adapters may support all change types and some may fall back to a generic
6627          * {@link com.android.internal.widget.RecyclerView.AdapterDataObserver#onChanged()
6628          * "something changed"} event if more specific data is not available.</p>
6629          *
6630          * <p>Components registering observers with an adapter are responsible for
6631          * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6632          * unregistering} those observers when finished.</p>
6633          *
6634          * @param observer Observer to register
6635          *
6636          * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6637          */
registerAdapterDataObserver(AdapterDataObserver observer)6638         public void registerAdapterDataObserver(AdapterDataObserver observer) {
6639             mObservable.registerObserver(observer);
6640         }
6641 
6642         /**
6643          * Unregister an observer currently listening for data changes.
6644          *
6645          * <p>The unregistered observer will no longer receive events about changes
6646          * to the adapter.</p>
6647          *
6648          * @param observer Observer to unregister
6649          *
6650          * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
6651          */
unregisterAdapterDataObserver(AdapterDataObserver observer)6652         public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
6653             mObservable.unregisterObserver(observer);
6654         }
6655 
6656         /**
6657          * Called by RecyclerView when it starts observing this Adapter.
6658          * <p>
6659          * Keep in mind that same adapter may be observed by multiple RecyclerViews.
6660          *
6661          * @param recyclerView The RecyclerView instance which started observing this adapter.
6662          * @see #onDetachedFromRecyclerView(RecyclerView)
6663          */
onAttachedToRecyclerView(RecyclerView recyclerView)6664         public void onAttachedToRecyclerView(RecyclerView recyclerView) {
6665         }
6666 
6667         /**
6668          * Called by RecyclerView when it stops observing this Adapter.
6669          *
6670          * @param recyclerView The RecyclerView instance which stopped observing this adapter.
6671          * @see #onAttachedToRecyclerView(RecyclerView)
6672          */
onDetachedFromRecyclerView(RecyclerView recyclerView)6673         public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
6674         }
6675 
6676         /**
6677          * Notify any registered observers that the data set has changed.
6678          *
6679          * <p>There are two different classes of data change events, item changes and structural
6680          * changes. Item changes are when a single item has its data updated but no positional
6681          * changes have occurred. Structural changes are when items are inserted, removed or moved
6682          * within the data set.</p>
6683          *
6684          * <p>This event does not specify what about the data set has changed, forcing
6685          * any observers to assume that all existing items and structure may no longer be valid.
6686          * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
6687          *
6688          * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
6689          * for adapters that report that they have {@link #hasStableIds() stable IDs} when
6690          * this method is used. This can help for the purposes of animation and visual
6691          * object persistence but individual item views will still need to be rebound
6692          * and relaid out.</p>
6693          *
6694          * <p>If you are writing an adapter it will always be more efficient to use the more
6695          * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
6696          * as a last resort.</p>
6697          *
6698          * @see #notifyItemChanged(int)
6699          * @see #notifyItemInserted(int)
6700          * @see #notifyItemRemoved(int)
6701          * @see #notifyItemRangeChanged(int, int)
6702          * @see #notifyItemRangeInserted(int, int)
6703          * @see #notifyItemRangeRemoved(int, int)
6704          */
notifyDataSetChanged()6705         public final void notifyDataSetChanged() {
6706             mObservable.notifyChanged();
6707         }
6708 
6709         /**
6710          * Notify any registered observers that the item at <code>position</code> has changed.
6711          * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
6712          *
6713          * <p>This is an item change event, not a structural change event. It indicates that any
6714          * reflection of the data at <code>position</code> is out of date and should be updated.
6715          * The item at <code>position</code> retains the same identity.</p>
6716          *
6717          * @param position Position of the item that has changed
6718          *
6719          * @see #notifyItemRangeChanged(int, int)
6720          */
notifyItemChanged(int position)6721         public final void notifyItemChanged(int position) {
6722             mObservable.notifyItemRangeChanged(position, 1);
6723         }
6724 
6725         /**
6726          * Notify any registered observers that the item at <code>position</code> has changed with
6727          * an optional payload object.
6728          *
6729          * <p>This is an item change event, not a structural change event. It indicates that any
6730          * reflection of the data at <code>position</code> is out of date and should be updated.
6731          * The item at <code>position</code> retains the same identity.
6732          * </p>
6733          *
6734          * <p>
6735          * Client can optionally pass a payload for partial change. These payloads will be merged
6736          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6737          * item is already represented by a ViewHolder and it will be rebound to the same
6738          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6739          * payloads on that item and prevent future payload until
6740          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6741          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6742          * attached, the payload will be simply dropped.
6743          *
6744          * @param position Position of the item that has changed
6745          * @param payload Optional parameter, use null to identify a "full" update
6746          *
6747          * @see #notifyItemRangeChanged(int, int)
6748          */
notifyItemChanged(int position, Object payload)6749         public final void notifyItemChanged(int position, Object payload) {
6750             mObservable.notifyItemRangeChanged(position, 1, payload);
6751         }
6752 
6753         /**
6754          * Notify any registered observers that the <code>itemCount</code> items starting at
6755          * position <code>positionStart</code> have changed.
6756          * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
6757          *
6758          * <p>This is an item change event, not a structural change event. It indicates that
6759          * any reflection of the data in the given position range is out of date and should
6760          * be updated. The items in the given range retain the same identity.</p>
6761          *
6762          * @param positionStart Position of the first item that has changed
6763          * @param itemCount Number of items that have changed
6764          *
6765          * @see #notifyItemChanged(int)
6766          */
notifyItemRangeChanged(int positionStart, int itemCount)6767         public final void notifyItemRangeChanged(int positionStart, int itemCount) {
6768             mObservable.notifyItemRangeChanged(positionStart, itemCount);
6769         }
6770 
6771         /**
6772          * Notify any registered observers that the <code>itemCount</code> items starting at
6773          * position <code>positionStart</code> have changed. An optional payload can be
6774          * passed to each changed item.
6775          *
6776          * <p>This is an item change event, not a structural change event. It indicates that any
6777          * reflection of the data in the given position range is out of date and should be updated.
6778          * The items in the given range retain the same identity.
6779          * </p>
6780          *
6781          * <p>
6782          * Client can optionally pass a payload for partial change. These payloads will be merged
6783          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6784          * item is already represented by a ViewHolder and it will be rebound to the same
6785          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6786          * payloads on that item and prevent future payload until
6787          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6788          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6789          * attached, the payload will be simply dropped.
6790          *
6791          * @param positionStart Position of the first item that has changed
6792          * @param itemCount Number of items that have changed
6793          * @param payload  Optional parameter, use null to identify a "full" update
6794          *
6795          * @see #notifyItemChanged(int)
6796          */
notifyItemRangeChanged(int positionStart, int itemCount, Object payload)6797         public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
6798             mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
6799         }
6800 
6801         /**
6802          * Notify any registered observers that the item reflected at <code>position</code>
6803          * has been newly inserted. The item previously at <code>position</code> is now at
6804          * position <code>position + 1</code>.
6805          *
6806          * <p>This is a structural change event. Representations of other existing items in the
6807          * data set are still considered up to date and will not be rebound, though their
6808          * positions may be altered.</p>
6809          *
6810          * @param position Position of the newly inserted item in the data set
6811          *
6812          * @see #notifyItemRangeInserted(int, int)
6813          */
notifyItemInserted(int position)6814         public final void notifyItemInserted(int position) {
6815             mObservable.notifyItemRangeInserted(position, 1);
6816         }
6817 
6818         /**
6819          * Notify any registered observers that the item reflected at <code>fromPosition</code>
6820          * has been moved to <code>toPosition</code>.
6821          *
6822          * <p>This is a structural change event. Representations of other existing items in the
6823          * data set are still considered up to date and will not be rebound, though their
6824          * positions may be altered.</p>
6825          *
6826          * @param fromPosition Previous position of the item.
6827          * @param toPosition New position of the item.
6828          */
notifyItemMoved(int fromPosition, int toPosition)6829         public final void notifyItemMoved(int fromPosition, int toPosition) {
6830             mObservable.notifyItemMoved(fromPosition, toPosition);
6831         }
6832 
6833         /**
6834          * Notify any registered observers that the currently reflected <code>itemCount</code>
6835          * items starting at <code>positionStart</code> have been newly inserted. The items
6836          * previously located at <code>positionStart</code> and beyond can now be found starting
6837          * at position <code>positionStart + itemCount</code>.
6838          *
6839          * <p>This is a structural change event. Representations of other existing items in the
6840          * data set are still considered up to date and will not be rebound, though their positions
6841          * may be altered.</p>
6842          *
6843          * @param positionStart Position of the first item that was inserted
6844          * @param itemCount Number of items inserted
6845          *
6846          * @see #notifyItemInserted(int)
6847          */
notifyItemRangeInserted(int positionStart, int itemCount)6848         public final void notifyItemRangeInserted(int positionStart, int itemCount) {
6849             mObservable.notifyItemRangeInserted(positionStart, itemCount);
6850         }
6851 
6852         /**
6853          * Notify any registered observers that the item previously located at <code>position</code>
6854          * has been removed from the data set. The items previously located at and after
6855          * <code>position</code> may now be found at <code>oldPosition - 1</code>.
6856          *
6857          * <p>This is a structural change event. Representations of other existing items in the
6858          * data set are still considered up to date and will not be rebound, though their positions
6859          * may be altered.</p>
6860          *
6861          * @param position Position of the item that has now been removed
6862          *
6863          * @see #notifyItemRangeRemoved(int, int)
6864          */
notifyItemRemoved(int position)6865         public final void notifyItemRemoved(int position) {
6866             mObservable.notifyItemRangeRemoved(position, 1);
6867         }
6868 
6869         /**
6870          * Notify any registered observers that the <code>itemCount</code> items previously
6871          * located at <code>positionStart</code> have been removed from the data set. The items
6872          * previously located at and after <code>positionStart + itemCount</code> may now be found
6873          * at <code>oldPosition - itemCount</code>.
6874          *
6875          * <p>This is a structural change event. Representations of other existing items in the data
6876          * set are still considered up to date and will not be rebound, though their positions
6877          * may be altered.</p>
6878          *
6879          * @param positionStart Previous position of the first item that was removed
6880          * @param itemCount Number of items removed from the data set
6881          */
notifyItemRangeRemoved(int positionStart, int itemCount)6882         public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
6883             mObservable.notifyItemRangeRemoved(positionStart, itemCount);
6884         }
6885     }
6886 
dispatchChildDetached(View child)6887     void dispatchChildDetached(View child) {
6888         final ViewHolder viewHolder = getChildViewHolderInt(child);
6889         onChildDetachedFromWindow(child);
6890         if (mAdapter != null && viewHolder != null) {
6891             mAdapter.onViewDetachedFromWindow(viewHolder);
6892         }
6893         if (mOnChildAttachStateListeners != null) {
6894             final int cnt = mOnChildAttachStateListeners.size();
6895             for (int i = cnt - 1; i >= 0; i--) {
6896                 mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
6897             }
6898         }
6899     }
6900 
dispatchChildAttached(View child)6901     void dispatchChildAttached(View child) {
6902         final ViewHolder viewHolder = getChildViewHolderInt(child);
6903         onChildAttachedToWindow(child);
6904         if (mAdapter != null && viewHolder != null) {
6905             mAdapter.onViewAttachedToWindow(viewHolder);
6906         }
6907         if (mOnChildAttachStateListeners != null) {
6908             final int cnt = mOnChildAttachStateListeners.size();
6909             for (int i = cnt - 1; i >= 0; i--) {
6910                 mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
6911             }
6912         }
6913     }
6914 
6915     /**
6916      * A <code>LayoutManager</code> is responsible for measuring and positioning item views
6917      * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
6918      * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
6919      * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
6920      * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
6921      * layout managers are provided for general use.
6922      * <p/>
6923      * If the LayoutManager specifies a default constructor or one with the signature
6924      * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
6925      * instantiate and set the LayoutManager when being inflated. Most used properties can
6926      * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
6927      * a LayoutManager specifies both constructors, the non-default constructor will take
6928      * precedence.
6929      *
6930      */
6931     public abstract static class LayoutManager {
6932         ChildHelper mChildHelper;
6933         RecyclerView mRecyclerView;
6934 
6935         @Nullable
6936         SmoothScroller mSmoothScroller;
6937 
6938         boolean mRequestedSimpleAnimations = false;
6939 
6940         boolean mIsAttachedToWindow = false;
6941 
6942         boolean mAutoMeasure = false;
6943 
6944         /**
6945          * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
6946          * if the space that will be given to it is already larger than what it has measured before.
6947          */
6948         private boolean mMeasurementCacheEnabled = true;
6949 
6950         private boolean mItemPrefetchEnabled = true;
6951 
6952         /**
6953          * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
6954          * requested by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)} or
6955          * {@link #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)} call.
6956          *
6957          * If expanded by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)},
6958          * will be reset upon layout to prevent initial prefetches (often large, since they're
6959          * proportional to expected child count) from expanding cache permanently.
6960          */
6961         int mPrefetchMaxCountObserved;
6962 
6963         /**
6964          * If true, mPrefetchMaxCountObserved is only valid until next layout, and should be reset.
6965          */
6966         boolean mPrefetchMaxObservedInInitialPrefetch;
6967 
6968         /**
6969          * These measure specs might be the measure specs that were passed into RecyclerView's
6970          * onMeasure method OR fake measure specs created by the RecyclerView.
6971          * For example, when a layout is run, RecyclerView always sets these specs to be
6972          * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
6973          * <p>
6974          * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
6975          * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
6976          * corrupt values. Older platforms have no responsibility to provide a size if they set
6977          * mode to unspecified.
6978          */
6979         private int mWidthMode, mHeightMode;
6980         private int mWidth, mHeight;
6981 
6982 
6983         /**
6984          * Interface for LayoutManagers to request items to be prefetched, based on position, with
6985          * specified distance from viewport, which indicates priority.
6986          *
6987          * @see LayoutManager#collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
6988          * @see LayoutManager#collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
6989          */
6990         public interface LayoutPrefetchRegistry {
6991             /**
6992              * Requests an an item to be prefetched, based on position, with a specified distance,
6993              * indicating priority.
6994              *
6995              * @param layoutPosition Position of the item to prefetch.
6996              * @param pixelDistance Distance from the current viewport to the bounds of the item,
6997              *                      must be non-negative.
6998              */
6999             void addPosition(int layoutPosition, int pixelDistance);
7000         }
7001 
setRecyclerView(RecyclerView recyclerView)7002         void setRecyclerView(RecyclerView recyclerView) {
7003             if (recyclerView == null) {
7004                 mRecyclerView = null;
7005                 mChildHelper = null;
7006                 mWidth = 0;
7007                 mHeight = 0;
7008             } else {
7009                 mRecyclerView = recyclerView;
7010                 mChildHelper = recyclerView.mChildHelper;
7011                 mWidth = recyclerView.getWidth();
7012                 mHeight = recyclerView.getHeight();
7013             }
7014             mWidthMode = MeasureSpec.EXACTLY;
7015             mHeightMode = MeasureSpec.EXACTLY;
7016         }
7017 
setMeasureSpecs(int wSpec, int hSpec)7018         void setMeasureSpecs(int wSpec, int hSpec) {
7019             mWidth = MeasureSpec.getSize(wSpec);
7020             mWidthMode = MeasureSpec.getMode(wSpec);
7021             if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
7022                 mWidth = 0;
7023             }
7024 
7025             mHeight = MeasureSpec.getSize(hSpec);
7026             mHeightMode = MeasureSpec.getMode(hSpec);
7027             if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
7028                 mHeight = 0;
7029             }
7030         }
7031 
7032         /**
7033          * Called after a layout is calculated during a measure pass when using auto-measure.
7034          * <p>
7035          * It simply traverses all children to calculate a bounding box then calls
7036          * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
7037          * if they need to handle the bounding box differently.
7038          * <p>
7039          * For example, GridLayoutManager override that method to ensure that even if a column is
7040          * empty, the GridLayoutManager still measures wide enough to include it.
7041          *
7042          * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
7043          * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
7044          */
setMeasuredDimensionFromChildren(int widthSpec, int heightSpec)7045         void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
7046             final int count = getChildCount();
7047             if (count == 0) {
7048                 mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
7049                 return;
7050             }
7051             int minX = Integer.MAX_VALUE;
7052             int minY = Integer.MAX_VALUE;
7053             int maxX = Integer.MIN_VALUE;
7054             int maxY = Integer.MIN_VALUE;
7055 
7056             for (int i = 0; i < count; i++) {
7057                 View child = getChildAt(i);
7058                 final Rect bounds = mRecyclerView.mTempRect;
7059                 getDecoratedBoundsWithMargins(child, bounds);
7060                 if (bounds.left < minX) {
7061                     minX = bounds.left;
7062                 }
7063                 if (bounds.right > maxX) {
7064                     maxX = bounds.right;
7065                 }
7066                 if (bounds.top < minY) {
7067                     minY = bounds.top;
7068                 }
7069                 if (bounds.bottom > maxY) {
7070                     maxY = bounds.bottom;
7071                 }
7072             }
7073             mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
7074             setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
7075         }
7076 
7077         /**
7078          * Sets the measured dimensions from the given bounding box of the children and the
7079          * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
7080          * called after the RecyclerView calls
7081          * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a measurement pass.
7082          * <p>
7083          * This method should call {@link #setMeasuredDimension(int, int)}.
7084          * <p>
7085          * The default implementation adds the RecyclerView's padding to the given bounding box
7086          * then caps the value to be within the given measurement specs.
7087          * <p>
7088          * This method is only called if the LayoutManager opted into the auto measurement API.
7089          *
7090          * @param childrenBounds The bounding box of all children
7091          * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
7092          * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
7093          *
7094          * @see #setAutoMeasureEnabled(boolean)
7095          */
setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec)7096         public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
7097             int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
7098             int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
7099             int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
7100             int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
7101             setMeasuredDimension(width, height);
7102         }
7103 
7104         /**
7105          * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
7106          */
requestLayout()7107         public void requestLayout() {
7108             if (mRecyclerView != null) {
7109                 mRecyclerView.requestLayout();
7110             }
7111         }
7112 
7113         /**
7114          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7115          * {@link IllegalStateException} if it <b>is not</b>.
7116          *
7117          * @param message The message for the exception. Can be null.
7118          * @see #assertNotInLayoutOrScroll(String)
7119          */
assertInLayoutOrScroll(String message)7120         public void assertInLayoutOrScroll(String message) {
7121             if (mRecyclerView != null) {
7122                 mRecyclerView.assertInLayoutOrScroll(message);
7123             }
7124         }
7125 
7126         /**
7127          * Chooses a size from the given specs and parameters that is closest to the desired size
7128          * and also complies with the spec.
7129          *
7130          * @param spec The measureSpec
7131          * @param desired The preferred measurement
7132          * @param min The minimum value
7133          *
7134          * @return A size that fits to the given specs
7135          */
chooseSize(int spec, int desired, int min)7136         public static int chooseSize(int spec, int desired, int min) {
7137             final int mode = View.MeasureSpec.getMode(spec);
7138             final int size = View.MeasureSpec.getSize(spec);
7139             switch (mode) {
7140                 case View.MeasureSpec.EXACTLY:
7141                     return size;
7142                 case View.MeasureSpec.AT_MOST:
7143                     return Math.min(size, Math.max(desired, min));
7144                 case View.MeasureSpec.UNSPECIFIED:
7145                 default:
7146                     return Math.max(desired, min);
7147             }
7148         }
7149 
7150         /**
7151          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7152          * {@link IllegalStateException} if it <b>is</b>.
7153          *
7154          * @param message The message for the exception. Can be null.
7155          * @see #assertInLayoutOrScroll(String)
7156          */
assertNotInLayoutOrScroll(String message)7157         public void assertNotInLayoutOrScroll(String message) {
7158             if (mRecyclerView != null) {
7159                 mRecyclerView.assertNotInLayoutOrScroll(message);
7160             }
7161         }
7162 
7163         /**
7164          * Defines whether the layout should be measured by the RecyclerView or the LayoutManager
7165          * wants to handle the layout measurements itself.
7166          * <p>
7167          * This method is usually called by the LayoutManager with value {@code true} if it wants
7168          * to support WRAP_CONTENT. If you are using a public LayoutManager but want to customize
7169          * the measurement logic, you can call this method with {@code false} and override
7170          * {@link LayoutManager#onMeasure(int, int)} to implement your custom measurement logic.
7171          * <p>
7172          * AutoMeasure is a convenience mechanism for LayoutManagers to easily wrap their content or
7173          * handle various specs provided by the RecyclerView's parent.
7174          * It works by calling {@link LayoutManager#onLayoutChildren(Recycler, State)} during an
7175          * {@link RecyclerView#onMeasure(int, int)} call, then calculating desired dimensions based
7176          * on children's positions. It does this while supporting all existing animation
7177          * capabilities of the RecyclerView.
7178          * <p>
7179          * AutoMeasure works as follows:
7180          * <ol>
7181          * <li>LayoutManager should call {@code setAutoMeasureEnabled(true)} to enable it. All of
7182          * the framework LayoutManagers use {@code auto-measure}.</li>
7183          * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided specs are
7184          * exact, RecyclerView will only call LayoutManager's {@code onMeasure} and return without
7185          * doing any layout calculation.</li>
7186          * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
7187          * layout process in {@code onMeasure} call. It will process all pending Adapter updates and
7188          * decide whether to run a predictive layout or not. If it decides to do so, it will first
7189          * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
7190          * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
7191          * return the width and height of the RecyclerView as of the last layout calculation.
7192          * <p>
7193          * After handling the predictive case, RecyclerView will call
7194          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7195          * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
7196          * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
7197          * {@link #getWidth()} and {@link #getWidthMode()}.</li>
7198          * <li>After the layout calculation, RecyclerView sets the measured width & height by
7199          * calculating the bounding box for the children (+ RecyclerView's padding). The
7200          * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
7201          * different values. For instance, GridLayoutManager overrides this value to handle the case
7202          * where if it is vertical and has 3 columns but only 2 items, it should still measure its
7203          * width to fit 3 items, not 2.</li>
7204          * <li>Any following on measure call to the RecyclerView will run
7205          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7206          * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
7207          * take care of which views are actually added / removed / moved / changed for animations so
7208          * that the LayoutManager should not worry about them and handle each
7209          * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.
7210          * </li>
7211          * <li>When measure is complete and RecyclerView's
7212          * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
7213          * whether it already did layout calculations during the measure pass and if so, it re-uses
7214          * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
7215          * if the last measure spec was different from the final dimensions or adapter contents
7216          * have changed between the measure call and the layout call.</li>
7217          * <li>Finally, animations are calculated and run as usual.</li>
7218          * </ol>
7219          *
7220          * @param enabled <code>True</code> if the Layout should be measured by the
7221          *                             RecyclerView, <code>false</code> if the LayoutManager wants
7222          *                             to measure itself.
7223          *
7224          * @see #setMeasuredDimension(Rect, int, int)
7225          * @see #isAutoMeasureEnabled()
7226          */
setAutoMeasureEnabled(boolean enabled)7227         public void setAutoMeasureEnabled(boolean enabled) {
7228             mAutoMeasure = enabled;
7229         }
7230 
7231         /**
7232          * Returns whether the LayoutManager uses the automatic measurement API or not.
7233          *
7234          * @return <code>True</code> if the LayoutManager is measured by the RecyclerView or
7235          * <code>false</code> if it measures itself.
7236          *
7237          * @see #setAutoMeasureEnabled(boolean)
7238          */
isAutoMeasureEnabled()7239         public boolean isAutoMeasureEnabled() {
7240             return mAutoMeasure;
7241         }
7242 
7243         /**
7244          * Returns whether this LayoutManager supports automatic item animations.
7245          * A LayoutManager wishing to support item animations should obey certain
7246          * rules as outlined in {@link #onLayoutChildren(Recycler, State)}.
7247          * The default return value is <code>false</code>, so subclasses of LayoutManager
7248          * will not get predictive item animations by default.
7249          *
7250          * <p>Whether item animations are enabled in a RecyclerView is determined both
7251          * by the return value from this method and the
7252          * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
7253          * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
7254          * method returns false, then simple item animations will be enabled, in which
7255          * views that are moving onto or off of the screen are simply faded in/out. If
7256          * the RecyclerView has a non-null ItemAnimator and this method returns true,
7257          * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to
7258          * setup up the information needed to more intelligently predict where appearing
7259          * and disappearing views should be animated from/to.</p>
7260          *
7261          * @return true if predictive item animations should be enabled, false otherwise
7262          */
supportsPredictiveItemAnimations()7263         public boolean supportsPredictiveItemAnimations() {
7264             return false;
7265         }
7266 
7267         /**
7268          * Sets whether the LayoutManager should be queried for views outside of
7269          * its viewport while the UI thread is idle between frames.
7270          *
7271          * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
7272          * view system traversals on devices running API 21 or greater. Default value is true.</p>
7273          *
7274          * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
7275          * to RenderThread and the starting up its next frame at the next VSync pulse. By
7276          * prefetching out of window views in this time period, delays from inflation and view
7277          * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
7278          *
7279          * <p>While prefetch is enabled, it will have the side effect of expanding the effective
7280          * size of the View cache to hold prefetched views.</p>
7281          *
7282          * @param enabled <code>True</code> if items should be prefetched in between traversals.
7283          *
7284          * @see #isItemPrefetchEnabled()
7285          */
setItemPrefetchEnabled(boolean enabled)7286         public final void setItemPrefetchEnabled(boolean enabled) {
7287             if (enabled != mItemPrefetchEnabled) {
7288                 mItemPrefetchEnabled = enabled;
7289                 mPrefetchMaxCountObserved = 0;
7290                 if (mRecyclerView != null) {
7291                     mRecyclerView.mRecycler.updateViewCacheSize();
7292                 }
7293             }
7294         }
7295 
7296         /**
7297          * Sets whether the LayoutManager should be queried for views outside of
7298          * its viewport while the UI thread is idle between frames.
7299          *
7300          * @see #setItemPrefetchEnabled(boolean)
7301          *
7302          * @return true if item prefetch is enabled, false otherwise
7303          */
isItemPrefetchEnabled()7304         public final boolean isItemPrefetchEnabled() {
7305             return mItemPrefetchEnabled;
7306         }
7307 
7308         /**
7309          * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
7310          *
7311          * <p>If item prefetch is enabled, this method is called in between traversals to gather
7312          * which positions the LayoutManager will soon need, given upcoming movement in subsequent
7313          * traversals.</p>
7314          *
7315          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7316          * each item to be prepared, and these positions will have their ViewHolders created and
7317          * bound, if there is sufficient time available, in advance of being needed by a
7318          * scroll or layout.</p>
7319          *
7320          * @param dx X movement component.
7321          * @param dy Y movement component.
7322          * @param state State of RecyclerView
7323          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7324          *
7325          * @see #isItemPrefetchEnabled()
7326          * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
7327          */
collectAdjacentPrefetchPositions(int dx, int dy, State state, LayoutPrefetchRegistry layoutPrefetchRegistry)7328         public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
7329                 LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7330 
7331         /**
7332          * Gather all positions from the LayoutManager to be prefetched in preperation for its
7333          * RecyclerView to come on screen, due to the movement of another, containing RecyclerView.
7334          *
7335          * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
7336          *
7337          * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
7338          * LayoutManager, this method is called in between draw traversals to gather
7339          * which positions this LayoutManager will first need, once it appears on the screen.</p>
7340          *
7341          * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
7342          * vertically scrolling LayoutManager, this method would be called when the horizontal list
7343          * is about to come onscreen.</p>
7344          *
7345          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7346          * each item to be prepared, and these positions will have their ViewHolders created and
7347          * bound, if there is sufficient time available, in advance of being needed by a
7348          * scroll or layout.</p>
7349          *
7350          * @param adapterItemCount number of items in the associated adapter.
7351          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7352          *
7353          * @see #isItemPrefetchEnabled()
7354          * @see #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
7355          */
collectInitialPrefetchPositions(int adapterItemCount, LayoutPrefetchRegistry layoutPrefetchRegistry)7356         public void collectInitialPrefetchPositions(int adapterItemCount,
7357                 LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7358 
dispatchAttachedToWindow(RecyclerView view)7359         void dispatchAttachedToWindow(RecyclerView view) {
7360             mIsAttachedToWindow = true;
7361             onAttachedToWindow(view);
7362         }
7363 
dispatchDetachedFromWindow(RecyclerView view, Recycler recycler)7364         void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
7365             mIsAttachedToWindow = false;
7366             onDetachedFromWindow(view, recycler);
7367         }
7368 
7369         /**
7370          * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
7371          * to a window.
7372          *
7373          * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
7374          * is attached to window.
7375          */
isAttachedToWindow()7376         public boolean isAttachedToWindow() {
7377             return mIsAttachedToWindow;
7378         }
7379 
7380         /**
7381          * Causes the Runnable to execute on the next animation time step.
7382          * The runnable will be run on the user interface thread.
7383          * <p>
7384          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7385          *
7386          * @param action The Runnable that will be executed.
7387          *
7388          * @see #removeCallbacks
7389          */
postOnAnimation(Runnable action)7390         public void postOnAnimation(Runnable action) {
7391             if (mRecyclerView != null) {
7392                 mRecyclerView.postOnAnimation(action);
7393             }
7394         }
7395 
7396         /**
7397          * Removes the specified Runnable from the message queue.
7398          * <p>
7399          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7400          *
7401          * @param action The Runnable to remove from the message handling queue
7402          *
7403          * @return true if RecyclerView could ask the Handler to remove the Runnable,
7404          *         false otherwise. When the returned value is true, the Runnable
7405          *         may or may not have been actually removed from the message queue
7406          *         (for instance, if the Runnable was not in the queue already.)
7407          *
7408          * @see #postOnAnimation
7409          */
removeCallbacks(Runnable action)7410         public boolean removeCallbacks(Runnable action) {
7411             if (mRecyclerView != null) {
7412                 return mRecyclerView.removeCallbacks(action);
7413             }
7414             return false;
7415         }
7416         /**
7417          * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
7418          * is attached to a window.
7419          * <p>
7420          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7421          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7422          * not requested on the RecyclerView while it was detached.
7423          * <p>
7424          * Subclass implementations should always call through to the superclass implementation.
7425          *
7426          * @param view The RecyclerView this LayoutManager is bound to
7427          *
7428          * @see #onDetachedFromWindow(RecyclerView, Recycler)
7429          */
7430         @CallSuper
onAttachedToWindow(RecyclerView view)7431         public void onAttachedToWindow(RecyclerView view) {
7432         }
7433 
7434         /**
7435          * @deprecated
7436          * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
7437          */
7438         @Deprecated
onDetachedFromWindow(RecyclerView view)7439         public void onDetachedFromWindow(RecyclerView view) {
7440 
7441         }
7442 
7443         /**
7444          * Called when this LayoutManager is detached from its parent RecyclerView or when
7445          * its parent RecyclerView is detached from its window.
7446          * <p>
7447          * LayoutManager should clear all of its View references as another LayoutManager might be
7448          * assigned to the RecyclerView.
7449          * <p>
7450          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7451          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7452          * not requested on the RecyclerView while it was detached.
7453          * <p>
7454          * If your LayoutManager has View references that it cleans in on-detach, it should also
7455          * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
7456          * RecyclerView is re-attached.
7457          * <p>
7458          * Subclass implementations should always call through to the superclass implementation.
7459          *
7460          * @param view The RecyclerView this LayoutManager is bound to
7461          * @param recycler The recycler to use if you prefer to recycle your children instead of
7462          *                 keeping them around.
7463          *
7464          * @see #onAttachedToWindow(RecyclerView)
7465          */
7466         @CallSuper
onDetachedFromWindow(RecyclerView view, Recycler recycler)7467         public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
7468             onDetachedFromWindow(view);
7469         }
7470 
7471         /**
7472          * Check if the RecyclerView is configured to clip child views to its padding.
7473          *
7474          * @return true if this RecyclerView clips children to its padding, false otherwise
7475          */
getClipToPadding()7476         public boolean getClipToPadding() {
7477             return mRecyclerView != null && mRecyclerView.mClipToPadding;
7478         }
7479 
7480         /**
7481          * Lay out all relevant child views from the given adapter.
7482          *
7483          * The LayoutManager is in charge of the behavior of item animations. By default,
7484          * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
7485          * item animations are enabled. This means that add/remove operations on the
7486          * adapter will result in animations to add new or appearing items, removed or
7487          * disappearing items, and moved items. If a LayoutManager returns false from
7488          * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
7489          * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
7490          * RecyclerView will have enough information to run those animations in a simple
7491          * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
7492          * simply fade views in and out, whether they are actually added/removed or whether
7493          * they are moved on or off the screen due to other add/remove operations.
7494          *
7495          * <p>A LayoutManager wanting a better item animation experience, where items can be
7496          * animated onto and off of the screen according to where the items exist when they
7497          * are not on screen, then the LayoutManager should return true from
7498          * {@link #supportsPredictiveItemAnimations()} and add additional logic to
7499          * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
7500          * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
7501          * once as a "pre" layout step to determine where items would have been prior to
7502          * a real layout, and again to do the "real" layout. In the pre-layout phase,
7503          * items will remember their pre-layout positions to allow them to be laid out
7504          * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
7505          * be returned from the scrap to help determine correct placement of other items.
7506          * These removed items should not be added to the child list, but should be used
7507          * to help calculate correct positioning of other views, including views that
7508          * were not previously onscreen (referred to as APPEARING views), but whose
7509          * pre-layout offscreen position can be determined given the extra
7510          * information about the pre-layout removed views.</p>
7511          *
7512          * <p>The second layout pass is the real layout in which only non-removed views
7513          * will be used. The only additional requirement during this pass is, if
7514          * {@link #supportsPredictiveItemAnimations()} returns true, to note which
7515          * views exist in the child list prior to layout and which are not there after
7516          * layout (referred to as DISAPPEARING views), and to position/layout those views
7517          * appropriately, without regard to the actual bounds of the RecyclerView. This allows
7518          * the animation system to know the location to which to animate these disappearing
7519          * views.</p>
7520          *
7521          * <p>The default LayoutManager implementations for RecyclerView handle all of these
7522          * requirements for animations already. Clients of RecyclerView can either use one
7523          * of these layout managers directly or look at their implementations of
7524          * onLayoutChildren() to see how they account for the APPEARING and
7525          * DISAPPEARING views.</p>
7526          *
7527          * @param recycler         Recycler to use for fetching potentially cached views for a
7528          *                         position
7529          * @param state            Transient state of RecyclerView
7530          */
onLayoutChildren(Recycler recycler, State state)7531         public void onLayoutChildren(Recycler recycler, State state) {
7532             Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
7533         }
7534 
7535         /**
7536          * Called after a full layout calculation is finished. The layout calculation may include
7537          * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
7538          * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
7539          * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
7540          * <p>
7541          * This is a good place for the LayoutManager to do some cleanup like pending scroll
7542          * position, saved state etc.
7543          *
7544          * @param state Transient state of RecyclerView
7545          */
onLayoutCompleted(State state)7546         public void onLayoutCompleted(State state) {
7547         }
7548 
7549         /**
7550          * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
7551          *
7552          * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
7553          * to store extra information specific to the layout. Client code should subclass
7554          * {@link RecyclerView.LayoutParams} for this purpose.</p>
7555          *
7556          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7557          * you must also override
7558          * {@link #checkLayoutParams(LayoutParams)},
7559          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7560          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7561          *
7562          * @return A new LayoutParams for a child view
7563          */
7564         public abstract LayoutParams generateDefaultLayoutParams();
7565 
7566         /**
7567          * Determines the validity of the supplied LayoutParams object.
7568          *
7569          * <p>This should check to make sure that the object is of the correct type
7570          * and all values are within acceptable ranges. The default implementation
7571          * returns <code>true</code> for non-null params.</p>
7572          *
7573          * @param lp LayoutParams object to check
7574          * @return true if this LayoutParams object is valid, false otherwise
7575          */
checkLayoutParams(LayoutParams lp)7576         public boolean checkLayoutParams(LayoutParams lp) {
7577             return lp != null;
7578         }
7579 
7580         /**
7581          * Create a LayoutParams object suitable for this LayoutManager, copying relevant
7582          * values from the supplied LayoutParams object if possible.
7583          *
7584          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7585          * you must also override
7586          * {@link #checkLayoutParams(LayoutParams)},
7587          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7588          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7589          *
7590          * @param lp Source LayoutParams object to copy values from
7591          * @return a new LayoutParams object
7592          */
generateLayoutParams(ViewGroup.LayoutParams lp)7593         public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
7594             if (lp instanceof LayoutParams) {
7595                 return new LayoutParams((LayoutParams) lp);
7596             } else if (lp instanceof MarginLayoutParams) {
7597                 return new LayoutParams((MarginLayoutParams) lp);
7598             } else {
7599                 return new LayoutParams(lp);
7600             }
7601         }
7602 
7603         /**
7604          * Create a LayoutParams object suitable for this LayoutManager from
7605          * an inflated layout resource.
7606          *
7607          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7608          * you must also override
7609          * {@link #checkLayoutParams(LayoutParams)},
7610          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7611          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7612          *
7613          * @param c Context for obtaining styled attributes
7614          * @param attrs AttributeSet describing the supplied arguments
7615          * @return a new LayoutParams object
7616          */
generateLayoutParams(Context c, AttributeSet attrs)7617         public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
7618             return new LayoutParams(c, attrs);
7619         }
7620 
7621         /**
7622          * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
7623          * The default implementation does nothing and returns 0.
7624          *
7625          * @param dx            distance to scroll by in pixels. X increases as scroll position
7626          *                      approaches the right.
7627          * @param recycler      Recycler to use for fetching potentially cached views for a
7628          *                      position
7629          * @param state         Transient state of RecyclerView
7630          * @return The actual distance scrolled. The return value will be negative if dx was
7631          * negative and scrolling proceeeded in that direction.
7632          * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
7633          */
scrollHorizontallyBy(int dx, Recycler recycler, State state)7634         public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
7635             return 0;
7636         }
7637 
7638         /**
7639          * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
7640          * The default implementation does nothing and returns 0.
7641          *
7642          * @param dy            distance to scroll in pixels. Y increases as scroll position
7643          *                      approaches the bottom.
7644          * @param recycler      Recycler to use for fetching potentially cached views for a
7645          *                      position
7646          * @param state         Transient state of RecyclerView
7647          * @return The actual distance scrolled. The return value will be negative if dy was
7648          * negative and scrolling proceeeded in that direction.
7649          * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
7650          */
scrollVerticallyBy(int dy, Recycler recycler, State state)7651         public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
7652             return 0;
7653         }
7654 
7655         /**
7656          * Query if horizontal scrolling is currently supported. The default implementation
7657          * returns false.
7658          *
7659          * @return True if this LayoutManager can scroll the current contents horizontally
7660          */
canScrollHorizontally()7661         public boolean canScrollHorizontally() {
7662             return false;
7663         }
7664 
7665         /**
7666          * Query if vertical scrolling is currently supported. The default implementation
7667          * returns false.
7668          *
7669          * @return True if this LayoutManager can scroll the current contents vertically
7670          */
canScrollVertically()7671         public boolean canScrollVertically() {
7672             return false;
7673         }
7674 
7675         /**
7676          * Scroll to the specified adapter position.
7677          *
7678          * Actual position of the item on the screen depends on the LayoutManager implementation.
7679          * @param position Scroll to this adapter position.
7680          */
scrollToPosition(int position)7681         public void scrollToPosition(int position) {
7682             if (DEBUG) {
7683                 Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
7684             }
7685         }
7686 
7687         /**
7688          * <p>Smooth scroll to the specified adapter position.</p>
7689          * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
7690          * instance and call {@link #startSmoothScroll(SmoothScroller)}.
7691          * </p>
7692          * @param recyclerView The RecyclerView to which this layout manager is attached
7693          * @param state    Current State of RecyclerView
7694          * @param position Scroll to this adapter position.
7695          */
smoothScrollToPosition(RecyclerView recyclerView, State state, int position)7696         public void smoothScrollToPosition(RecyclerView recyclerView, State state,
7697                 int position) {
7698             Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
7699         }
7700 
7701         /**
7702          * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
7703          * <p>Calling this method will cancel any previous smooth scroll request.</p>
7704          * @param smoothScroller Instance which defines how smooth scroll should be animated
7705          */
startSmoothScroll(SmoothScroller smoothScroller)7706         public void startSmoothScroll(SmoothScroller smoothScroller) {
7707             if (mSmoothScroller != null && smoothScroller != mSmoothScroller
7708                     && mSmoothScroller.isRunning()) {
7709                 mSmoothScroller.stop();
7710             }
7711             mSmoothScroller = smoothScroller;
7712             mSmoothScroller.start(mRecyclerView, this);
7713         }
7714 
7715         /**
7716          * @return true if RecycylerView is currently in the state of smooth scrolling.
7717          */
isSmoothScrolling()7718         public boolean isSmoothScrolling() {
7719             return mSmoothScroller != null && mSmoothScroller.isRunning();
7720         }
7721 
7722 
7723         /**
7724          * Returns the resolved layout direction for this RecyclerView.
7725          *
7726          * @return {@link android.view.View#LAYOUT_DIRECTION_RTL} if the layout
7727          * direction is RTL or returns
7728          * {@link android.view.View#LAYOUT_DIRECTION_LTR} if the layout direction
7729          * is not RTL.
7730          */
getLayoutDirection()7731         public int getLayoutDirection() {
7732             return mRecyclerView.getLayoutDirection();
7733         }
7734 
7735         /**
7736          * Ends all animations on the view created by the {@link ItemAnimator}.
7737          *
7738          * @param view The View for which the animations should be ended.
7739          * @see RecyclerView.ItemAnimator#endAnimations()
7740          */
endAnimation(View view)7741         public void endAnimation(View view) {
7742             if (mRecyclerView.mItemAnimator != null) {
7743                 mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
7744             }
7745         }
7746 
7747         /**
7748          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
7749          * to the layout that is known to be going away, either because it has been
7750          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
7751          * visible portion of the container but is being laid out in order to inform RecyclerView
7752          * in how to animate the item out of view.
7753          * <p>
7754          * Views added via this method are going to be invisible to LayoutManager after the
7755          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
7756          * or won't be included in {@link #getChildCount()} method.
7757          *
7758          * @param child View to add and then remove with animation.
7759          */
addDisappearingView(View child)7760         public void addDisappearingView(View child) {
7761             addDisappearingView(child, -1);
7762         }
7763 
7764         /**
7765          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
7766          * to the layout that is known to be going away, either because it has been
7767          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
7768          * visible portion of the container but is being laid out in order to inform RecyclerView
7769          * in how to animate the item out of view.
7770          * <p>
7771          * Views added via this method are going to be invisible to LayoutManager after the
7772          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
7773          * or won't be included in {@link #getChildCount()} method.
7774          *
7775          * @param child View to add and then remove with animation.
7776          * @param index Index of the view.
7777          */
addDisappearingView(View child, int index)7778         public void addDisappearingView(View child, int index) {
7779             addViewInt(child, index, true);
7780         }
7781 
7782         /**
7783          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
7784          * use this method to add views obtained from a {@link Recycler} using
7785          * {@link Recycler#getViewForPosition(int)}.
7786          *
7787          * @param child View to add
7788          */
addView(View child)7789         public void addView(View child) {
7790             addView(child, -1);
7791         }
7792 
7793         /**
7794          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
7795          * use this method to add views obtained from a {@link Recycler} using
7796          * {@link Recycler#getViewForPosition(int)}.
7797          *
7798          * @param child View to add
7799          * @param index Index to add child at
7800          */
addView(View child, int index)7801         public void addView(View child, int index) {
7802             addViewInt(child, index, false);
7803         }
7804 
addViewInt(View child, int index, boolean disappearing)7805         private void addViewInt(View child, int index, boolean disappearing) {
7806             final ViewHolder holder = getChildViewHolderInt(child);
7807             if (disappearing || holder.isRemoved()) {
7808                 // these views will be hidden at the end of the layout pass.
7809                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
7810             } else {
7811                 // This may look like unnecessary but may happen if layout manager supports
7812                 // predictive layouts and adapter removed then re-added the same item.
7813                 // In this case, added version will be visible in the post layout (because add is
7814                 // deferred) but RV will still bind it to the same View.
7815                 // So if a View re-appears in post layout pass, remove it from disappearing list.
7816                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
7817             }
7818             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
7819             if (holder.wasReturnedFromScrap() || holder.isScrap()) {
7820                 if (holder.isScrap()) {
7821                     holder.unScrap();
7822                 } else {
7823                     holder.clearReturnedFromScrapFlag();
7824                 }
7825                 mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
7826                 if (DISPATCH_TEMP_DETACH) {
7827                     child.dispatchFinishTemporaryDetach();
7828                 }
7829             } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
7830                 // ensure in correct position
7831                 int currentIndex = mChildHelper.indexOfChild(child);
7832                 if (index == -1) {
7833                     index = mChildHelper.getChildCount();
7834                 }
7835                 if (currentIndex == -1) {
7836                     throw new IllegalStateException("Added View has RecyclerView as parent but"
7837                             + " view is not a real child. Unfiltered index:"
7838                             + mRecyclerView.indexOfChild(child));
7839                 }
7840                 if (currentIndex != index) {
7841                     mRecyclerView.mLayout.moveView(currentIndex, index);
7842                 }
7843             } else {
7844                 mChildHelper.addView(child, index, false);
7845                 lp.mInsetsDirty = true;
7846                 if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
7847                     mSmoothScroller.onChildAttachedToWindow(child);
7848                 }
7849             }
7850             if (lp.mPendingInvalidate) {
7851                 if (DEBUG) {
7852                     Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
7853                 }
7854                 holder.itemView.invalidate();
7855                 lp.mPendingInvalidate = false;
7856             }
7857         }
7858 
7859         /**
7860          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
7861          * use this method to completely remove a child view that is no longer needed.
7862          * LayoutManagers should strongly consider recycling removed views using
7863          * {@link Recycler#recycleView(android.view.View)}.
7864          *
7865          * @param child View to remove
7866          */
removeView(View child)7867         public void removeView(View child) {
7868             mChildHelper.removeView(child);
7869         }
7870 
7871         /**
7872          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
7873          * use this method to completely remove a child view that is no longer needed.
7874          * LayoutManagers should strongly consider recycling removed views using
7875          * {@link Recycler#recycleView(android.view.View)}.
7876          *
7877          * @param index Index of the child view to remove
7878          */
removeViewAt(int index)7879         public void removeViewAt(int index) {
7880             final View child = getChildAt(index);
7881             if (child != null) {
7882                 mChildHelper.removeViewAt(index);
7883             }
7884         }
7885 
7886         /**
7887          * Remove all views from the currently attached RecyclerView. This will not recycle
7888          * any of the affected views; the LayoutManager is responsible for doing so if desired.
7889          */
removeAllViews()7890         public void removeAllViews() {
7891             // Only remove non-animating views
7892             final int childCount = getChildCount();
7893             for (int i = childCount - 1; i >= 0; i--) {
7894                 mChildHelper.removeViewAt(i);
7895             }
7896         }
7897 
7898         /**
7899          * Returns offset of the RecyclerView's text baseline from the its top boundary.
7900          *
7901          * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
7902          * there is no baseline.
7903          */
getBaseline()7904         public int getBaseline() {
7905             return -1;
7906         }
7907 
7908         /**
7909          * Returns the adapter position of the item represented by the given View. This does not
7910          * contain any adapter changes that might have happened after the last layout.
7911          *
7912          * @param view The view to query
7913          * @return The adapter position of the item which is rendered by this View.
7914          */
getPosition(View view)7915         public int getPosition(View view) {
7916             return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
7917         }
7918 
7919         /**
7920          * Returns the View type defined by the adapter.
7921          *
7922          * @param view The view to query
7923          * @return The type of the view assigned by the adapter.
7924          */
getItemViewType(View view)7925         public int getItemViewType(View view) {
7926             return getChildViewHolderInt(view).getItemViewType();
7927         }
7928 
7929         /**
7930          * Traverses the ancestors of the given view and returns the item view that contains it
7931          * and also a direct child of the LayoutManager.
7932          * <p>
7933          * Note that this method may return null if the view is a child of the RecyclerView but
7934          * not a child of the LayoutManager (e.g. running a disappear animation).
7935          *
7936          * @param view The view that is a descendant of the LayoutManager.
7937          *
7938          * @return The direct child of the LayoutManager which contains the given view or null if
7939          * the provided view is not a descendant of this LayoutManager.
7940          *
7941          * @see RecyclerView#getChildViewHolder(View)
7942          * @see RecyclerView#findContainingViewHolder(View)
7943          */
7944         @Nullable
findContainingItemView(View view)7945         public View findContainingItemView(View view) {
7946             if (mRecyclerView == null) {
7947                 return null;
7948             }
7949             View found = mRecyclerView.findContainingItemView(view);
7950             if (found == null) {
7951                 return null;
7952             }
7953             if (mChildHelper.isHidden(found)) {
7954                 return null;
7955             }
7956             return found;
7957         }
7958 
7959         /**
7960          * Finds the view which represents the given adapter position.
7961          * <p>
7962          * This method traverses each child since it has no information about child order.
7963          * Override this method to improve performance if your LayoutManager keeps data about
7964          * child views.
7965          * <p>
7966          * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
7967          *
7968          * @param position Position of the item in adapter
7969          * @return The child view that represents the given position or null if the position is not
7970          * laid out
7971          */
findViewByPosition(int position)7972         public View findViewByPosition(int position) {
7973             final int childCount = getChildCount();
7974             for (int i = 0; i < childCount; i++) {
7975                 View child = getChildAt(i);
7976                 ViewHolder vh = getChildViewHolderInt(child);
7977                 if (vh == null) {
7978                     continue;
7979                 }
7980                 if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
7981                         && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
7982                     return child;
7983                 }
7984             }
7985             return null;
7986         }
7987 
7988         /**
7989          * Temporarily detach a child view.
7990          *
7991          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
7992          * views currently attached to the RecyclerView. Generally LayoutManager implementations
7993          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
7994          * so that the detached view may be rebound and reused.</p>
7995          *
7996          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
7997          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
7998          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
7999          * before the LayoutManager entry point method called by RecyclerView returns.</p>
8000          *
8001          * @param child Child to detach
8002          */
detachView(View child)8003         public void detachView(View child) {
8004             final int ind = mChildHelper.indexOfChild(child);
8005             if (ind >= 0) {
8006                 detachViewInternal(ind, child);
8007             }
8008         }
8009 
8010         /**
8011          * Temporarily detach a child view.
8012          *
8013          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
8014          * views currently attached to the RecyclerView. Generally LayoutManager implementations
8015          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
8016          * so that the detached view may be rebound and reused.</p>
8017          *
8018          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
8019          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
8020          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
8021          * before the LayoutManager entry point method called by RecyclerView returns.</p>
8022          *
8023          * @param index Index of the child to detach
8024          */
detachViewAt(int index)8025         public void detachViewAt(int index) {
8026             detachViewInternal(index, getChildAt(index));
8027         }
8028 
detachViewInternal(int index, View view)8029         private void detachViewInternal(int index, View view) {
8030             if (DISPATCH_TEMP_DETACH) {
8031                 view.dispatchStartTemporaryDetach();
8032             }
8033             mChildHelper.detachViewFromParent(index);
8034         }
8035 
8036         /**
8037          * Reattach a previously {@link #detachView(android.view.View) detached} view.
8038          * This method should not be used to reattach views that were previously
8039          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8040          *
8041          * @param child Child to reattach
8042          * @param index Intended child index for child
8043          * @param lp LayoutParams for child
8044          */
attachView(View child, int index, LayoutParams lp)8045         public void attachView(View child, int index, LayoutParams lp) {
8046             ViewHolder vh = getChildViewHolderInt(child);
8047             if (vh.isRemoved()) {
8048                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
8049             } else {
8050                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
8051             }
8052             mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
8053             if (DISPATCH_TEMP_DETACH)  {
8054                 child.dispatchFinishTemporaryDetach();
8055             }
8056         }
8057 
8058         /**
8059          * Reattach a previously {@link #detachView(android.view.View) detached} view.
8060          * This method should not be used to reattach views that were previously
8061          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8062          *
8063          * @param child Child to reattach
8064          * @param index Intended child index for child
8065          */
attachView(View child, int index)8066         public void attachView(View child, int index) {
8067             attachView(child, index, (LayoutParams) child.getLayoutParams());
8068         }
8069 
8070         /**
8071          * Reattach a previously {@link #detachView(android.view.View) detached} view.
8072          * This method should not be used to reattach views that were previously
8073          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8074          *
8075          * @param child Child to reattach
8076          */
attachView(View child)8077         public void attachView(View child) {
8078             attachView(child, -1);
8079         }
8080 
8081         /**
8082          * Finish removing a view that was previously temporarily
8083          * {@link #detachView(android.view.View) detached}.
8084          *
8085          * @param child Detached child to remove
8086          */
removeDetachedView(View child)8087         public void removeDetachedView(View child) {
8088             mRecyclerView.removeDetachedView(child, false);
8089         }
8090 
8091         /**
8092          * Moves a View from one position to another.
8093          *
8094          * @param fromIndex The View's initial index
8095          * @param toIndex The View's target index
8096          */
moveView(int fromIndex, int toIndex)8097         public void moveView(int fromIndex, int toIndex) {
8098             View view = getChildAt(fromIndex);
8099             if (view == null) {
8100                 throw new IllegalArgumentException("Cannot move a child from non-existing index:"
8101                         + fromIndex);
8102             }
8103             detachViewAt(fromIndex);
8104             attachView(view, toIndex);
8105         }
8106 
8107         /**
8108          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8109          *
8110          * <p>Scrapping a view allows it to be rebound and reused to show updated or
8111          * different data.</p>
8112          *
8113          * @param child Child to detach and scrap
8114          * @param recycler Recycler to deposit the new scrap view into
8115          */
detachAndScrapView(View child, Recycler recycler)8116         public void detachAndScrapView(View child, Recycler recycler) {
8117             int index = mChildHelper.indexOfChild(child);
8118             scrapOrRecycleView(recycler, index, child);
8119         }
8120 
8121         /**
8122          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8123          *
8124          * <p>Scrapping a view allows it to be rebound and reused to show updated or
8125          * different data.</p>
8126          *
8127          * @param index Index of child to detach and scrap
8128          * @param recycler Recycler to deposit the new scrap view into
8129          */
detachAndScrapViewAt(int index, Recycler recycler)8130         public void detachAndScrapViewAt(int index, Recycler recycler) {
8131             final View child = getChildAt(index);
8132             scrapOrRecycleView(recycler, index, child);
8133         }
8134 
8135         /**
8136          * Remove a child view and recycle it using the given Recycler.
8137          *
8138          * @param child Child to remove and recycle
8139          * @param recycler Recycler to use to recycle child
8140          */
removeAndRecycleView(View child, Recycler recycler)8141         public void removeAndRecycleView(View child, Recycler recycler) {
8142             removeView(child);
8143             recycler.recycleView(child);
8144         }
8145 
8146         /**
8147          * Remove a child view and recycle it using the given Recycler.
8148          *
8149          * @param index Index of child to remove and recycle
8150          * @param recycler Recycler to use to recycle child
8151          */
removeAndRecycleViewAt(int index, Recycler recycler)8152         public void removeAndRecycleViewAt(int index, Recycler recycler) {
8153             final View view = getChildAt(index);
8154             removeViewAt(index);
8155             recycler.recycleView(view);
8156         }
8157 
8158         /**
8159          * Return the current number of child views attached to the parent RecyclerView.
8160          * This does not include child views that were temporarily detached and/or scrapped.
8161          *
8162          * @return Number of attached children
8163          */
getChildCount()8164         public int getChildCount() {
8165             return mChildHelper != null ? mChildHelper.getChildCount() : 0;
8166         }
8167 
8168         /**
8169          * Return the child view at the given index
8170          * @param index Index of child to return
8171          * @return Child view at index
8172          */
getChildAt(int index)8173         public View getChildAt(int index) {
8174             return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
8175         }
8176 
8177         /**
8178          * Return the width measurement spec mode of the RecyclerView.
8179          * <p>
8180          * This value is set only if the LayoutManager opts into the auto measure api via
8181          * {@link #setAutoMeasureEnabled(boolean)}.
8182          * <p>
8183          * When RecyclerView is running a layout, this value is always set to
8184          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8185          *
8186          * @return Width measure spec mode.
8187          *
8188          * @see View.MeasureSpec#getMode(int)
8189          * @see View#onMeasure(int, int)
8190          */
getWidthMode()8191         public int getWidthMode() {
8192             return mWidthMode;
8193         }
8194 
8195         /**
8196          * Return the height measurement spec mode of the RecyclerView.
8197          * <p>
8198          * This value is set only if the LayoutManager opts into the auto measure api via
8199          * {@link #setAutoMeasureEnabled(boolean)}.
8200          * <p>
8201          * When RecyclerView is running a layout, this value is always set to
8202          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8203          *
8204          * @return Height measure spec mode.
8205          *
8206          * @see View.MeasureSpec#getMode(int)
8207          * @see View#onMeasure(int, int)
8208          */
getHeightMode()8209         public int getHeightMode() {
8210             return mHeightMode;
8211         }
8212 
8213         /**
8214          * Return the width of the parent RecyclerView
8215          *
8216          * @return Width in pixels
8217          */
getWidth()8218         public int getWidth() {
8219             return mWidth;
8220         }
8221 
8222         /**
8223          * Return the height of the parent RecyclerView
8224          *
8225          * @return Height in pixels
8226          */
getHeight()8227         public int getHeight() {
8228             return mHeight;
8229         }
8230 
8231         /**
8232          * Return the left padding of the parent RecyclerView
8233          *
8234          * @return Padding in pixels
8235          */
getPaddingLeft()8236         public int getPaddingLeft() {
8237             return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
8238         }
8239 
8240         /**
8241          * Return the top padding of the parent RecyclerView
8242          *
8243          * @return Padding in pixels
8244          */
getPaddingTop()8245         public int getPaddingTop() {
8246             return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
8247         }
8248 
8249         /**
8250          * Return the right padding of the parent RecyclerView
8251          *
8252          * @return Padding in pixels
8253          */
getPaddingRight()8254         public int getPaddingRight() {
8255             return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
8256         }
8257 
8258         /**
8259          * Return the bottom padding of the parent RecyclerView
8260          *
8261          * @return Padding in pixels
8262          */
getPaddingBottom()8263         public int getPaddingBottom() {
8264             return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
8265         }
8266 
8267         /**
8268          * Return the start padding of the parent RecyclerView
8269          *
8270          * @return Padding in pixels
8271          */
getPaddingStart()8272         public int getPaddingStart() {
8273             return mRecyclerView != null ? mRecyclerView.getPaddingStart() : 0;
8274         }
8275 
8276         /**
8277          * Return the end padding of the parent RecyclerView
8278          *
8279          * @return Padding in pixels
8280          */
getPaddingEnd()8281         public int getPaddingEnd() {
8282             return mRecyclerView != null ? mRecyclerView.getPaddingEnd() : 0;
8283         }
8284 
8285         /**
8286          * Returns true if the RecyclerView this LayoutManager is bound to has focus.
8287          *
8288          * @return True if the RecyclerView has focus, false otherwise.
8289          * @see View#isFocused()
8290          */
isFocused()8291         public boolean isFocused() {
8292             return mRecyclerView != null && mRecyclerView.isFocused();
8293         }
8294 
8295         /**
8296          * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
8297          *
8298          * @return true if the RecyclerView has or contains focus
8299          * @see View#hasFocus()
8300          */
hasFocus()8301         public boolean hasFocus() {
8302             return mRecyclerView != null && mRecyclerView.hasFocus();
8303         }
8304 
8305         /**
8306          * Returns the item View which has or contains focus.
8307          *
8308          * @return A direct child of RecyclerView which has focus or contains the focused child.
8309          */
getFocusedChild()8310         public View getFocusedChild() {
8311             if (mRecyclerView == null) {
8312                 return null;
8313             }
8314             final View focused = mRecyclerView.getFocusedChild();
8315             if (focused == null || mChildHelper.isHidden(focused)) {
8316                 return null;
8317             }
8318             return focused;
8319         }
8320 
8321         /**
8322          * Returns the number of items in the adapter bound to the parent RecyclerView.
8323          * <p>
8324          * Note that this number is not necessarily equal to
8325          * {@link State#getItemCount() State#getItemCount()}. In methods where {@link State} is
8326          * available, you should use {@link State#getItemCount() State#getItemCount()} instead.
8327          * For more details, check the documentation for
8328          * {@link State#getItemCount() State#getItemCount()}.
8329          *
8330          * @return The number of items in the bound adapter
8331          * @see State#getItemCount()
8332          */
getItemCount()8333         public int getItemCount() {
8334             final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
8335             return a != null ? a.getItemCount() : 0;
8336         }
8337 
8338         /**
8339          * Offset all child views attached to the parent RecyclerView by dx pixels along
8340          * the horizontal axis.
8341          *
8342          * @param dx Pixels to offset by
8343          */
offsetChildrenHorizontal(int dx)8344         public void offsetChildrenHorizontal(int dx) {
8345             if (mRecyclerView != null) {
8346                 mRecyclerView.offsetChildrenHorizontal(dx);
8347             }
8348         }
8349 
8350         /**
8351          * Offset all child views attached to the parent RecyclerView by dy pixels along
8352          * the vertical axis.
8353          *
8354          * @param dy Pixels to offset by
8355          */
offsetChildrenVertical(int dy)8356         public void offsetChildrenVertical(int dy) {
8357             if (mRecyclerView != null) {
8358                 mRecyclerView.offsetChildrenVertical(dy);
8359             }
8360         }
8361 
8362         /**
8363          * Flags a view so that it will not be scrapped or recycled.
8364          * <p>
8365          * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
8366          * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
8367          * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
8368          * ignore the child.
8369          * <p>
8370          * Before this child can be recycled again, you have to call
8371          * {@link #stopIgnoringView(View)}.
8372          * <p>
8373          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8374          *
8375          * @param view View to ignore.
8376          * @see #stopIgnoringView(View)
8377          */
ignoreView(View view)8378         public void ignoreView(View view) {
8379             if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
8380                 // checking this because calling this method on a recycled or detached view may
8381                 // cause loss of state.
8382                 throw new IllegalArgumentException("View should be fully attached to be ignored");
8383             }
8384             final ViewHolder vh = getChildViewHolderInt(view);
8385             vh.addFlags(ViewHolder.FLAG_IGNORE);
8386             mRecyclerView.mViewInfoStore.removeViewHolder(vh);
8387         }
8388 
8389         /**
8390          * View can be scrapped and recycled again.
8391          * <p>
8392          * Note that calling this method removes all information in the view holder.
8393          * <p>
8394          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8395          *
8396          * @param view View to ignore.
8397          */
stopIgnoringView(View view)8398         public void stopIgnoringView(View view) {
8399             final ViewHolder vh = getChildViewHolderInt(view);
8400             vh.stopIgnoring();
8401             vh.resetInternal();
8402             vh.addFlags(ViewHolder.FLAG_INVALID);
8403         }
8404 
8405         /**
8406          * Temporarily detach and scrap all currently attached child views. Views will be scrapped
8407          * into the given Recycler. The Recycler may prefer to reuse scrap views before
8408          * other views that were previously recycled.
8409          *
8410          * @param recycler Recycler to scrap views into
8411          */
detachAndScrapAttachedViews(Recycler recycler)8412         public void detachAndScrapAttachedViews(Recycler recycler) {
8413             final int childCount = getChildCount();
8414             for (int i = childCount - 1; i >= 0; i--) {
8415                 final View v = getChildAt(i);
8416                 scrapOrRecycleView(recycler, i, v);
8417             }
8418         }
8419 
scrapOrRecycleView(Recycler recycler, int index, View view)8420         private void scrapOrRecycleView(Recycler recycler, int index, View view) {
8421             final ViewHolder viewHolder = getChildViewHolderInt(view);
8422             if (viewHolder.shouldIgnore()) {
8423                 if (DEBUG) {
8424                     Log.d(TAG, "ignoring view " + viewHolder);
8425                 }
8426                 return;
8427             }
8428             if (viewHolder.isInvalid() && !viewHolder.isRemoved()
8429                     && !mRecyclerView.mAdapter.hasStableIds()) {
8430                 removeViewAt(index);
8431                 recycler.recycleViewHolderInternal(viewHolder);
8432             } else {
8433                 detachViewAt(index);
8434                 recycler.scrapView(view);
8435                 mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
8436             }
8437         }
8438 
8439         /**
8440          * Recycles the scrapped views.
8441          * <p>
8442          * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
8443          * the expected behavior if scrapped views are used for animations. Otherwise, we need to
8444          * call remove and invalidate RecyclerView to ensure UI update.
8445          *
8446          * @param recycler Recycler
8447          */
removeAndRecycleScrapInt(Recycler recycler)8448         void removeAndRecycleScrapInt(Recycler recycler) {
8449             final int scrapCount = recycler.getScrapCount();
8450             // Loop backward, recycler might be changed by removeDetachedView()
8451             for (int i = scrapCount - 1; i >= 0; i--) {
8452                 final View scrap = recycler.getScrapViewAt(i);
8453                 final ViewHolder vh = getChildViewHolderInt(scrap);
8454                 if (vh.shouldIgnore()) {
8455                     continue;
8456                 }
8457                 // If the scrap view is animating, we need to cancel them first. If we cancel it
8458                 // here, ItemAnimator callback may recycle it which will cause double recycling.
8459                 // To avoid this, we mark it as not recycleable before calling the item animator.
8460                 // Since removeDetachedView calls a user API, a common mistake (ending animations on
8461                 // the view) may recycle it too, so we guard it before we call user APIs.
8462                 vh.setIsRecyclable(false);
8463                 if (vh.isTmpDetached()) {
8464                     mRecyclerView.removeDetachedView(scrap, false);
8465                 }
8466                 if (mRecyclerView.mItemAnimator != null) {
8467                     mRecyclerView.mItemAnimator.endAnimation(vh);
8468                 }
8469                 vh.setIsRecyclable(true);
8470                 recycler.quickRecycleScrapView(scrap);
8471             }
8472             recycler.clearScrap();
8473             if (scrapCount > 0) {
8474                 mRecyclerView.invalidate();
8475             }
8476         }
8477 
8478 
8479         /**
8480          * Measure a child view using standard measurement policy, taking the padding
8481          * of the parent RecyclerView and any added item decorations into account.
8482          *
8483          * <p>If the RecyclerView can be scrolled in either dimension the caller may
8484          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8485          *
8486          * @param child Child view to measure
8487          * @param widthUsed Width in pixels currently consumed by other views, if relevant
8488          * @param heightUsed Height in pixels currently consumed by other views, if relevant
8489          */
measureChild(View child, int widthUsed, int heightUsed)8490         public void measureChild(View child, int widthUsed, int heightUsed) {
8491             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8492 
8493             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8494             widthUsed += insets.left + insets.right;
8495             heightUsed += insets.top + insets.bottom;
8496             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8497                     getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
8498                     canScrollHorizontally());
8499             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8500                     getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
8501                     canScrollVertically());
8502             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8503                 child.measure(widthSpec, heightSpec);
8504             }
8505         }
8506 
8507         /**
8508          * RecyclerView internally does its own View measurement caching which should help with
8509          * WRAP_CONTENT.
8510          * <p>
8511          * Use this method if the View is already measured once in this layout pass.
8512          */
shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp)8513         boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8514             return !mMeasurementCacheEnabled
8515                     || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
8516                     || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
8517         }
8518 
8519         // we may consider making this public
8520         /**
8521          * RecyclerView internally does its own View measurement caching which should help with
8522          * WRAP_CONTENT.
8523          * <p>
8524          * Use this method if the View is not yet measured and you need to decide whether to
8525          * measure this View or not.
8526          */
shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp)8527         boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8528             return child.isLayoutRequested()
8529                     || !mMeasurementCacheEnabled
8530                     || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
8531                     || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
8532         }
8533 
8534         /**
8535          * In addition to the View Framework's measurement cache, RecyclerView uses its own
8536          * additional measurement cache for its children to avoid re-measuring them when not
8537          * necessary. It is on by default but it can be turned off via
8538          * {@link #setMeasurementCacheEnabled(boolean)}.
8539          *
8540          * @return True if measurement cache is enabled, false otherwise.
8541          *
8542          * @see #setMeasurementCacheEnabled(boolean)
8543          */
isMeasurementCacheEnabled()8544         public boolean isMeasurementCacheEnabled() {
8545             return mMeasurementCacheEnabled;
8546         }
8547 
8548         /**
8549          * Sets whether RecyclerView should use its own measurement cache for the children. This is
8550          * a more aggressive cache than the framework uses.
8551          *
8552          * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
8553          *
8554          * @see #isMeasurementCacheEnabled()
8555          */
setMeasurementCacheEnabled(boolean measurementCacheEnabled)8556         public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
8557             mMeasurementCacheEnabled = measurementCacheEnabled;
8558         }
8559 
isMeasurementUpToDate(int childSize, int spec, int dimension)8560         private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
8561             final int specMode = MeasureSpec.getMode(spec);
8562             final int specSize = MeasureSpec.getSize(spec);
8563             if (dimension > 0 && childSize != dimension) {
8564                 return false;
8565             }
8566             switch (specMode) {
8567                 case MeasureSpec.UNSPECIFIED:
8568                     return true;
8569                 case MeasureSpec.AT_MOST:
8570                     return specSize >= childSize;
8571                 case MeasureSpec.EXACTLY:
8572                     return  specSize == childSize;
8573             }
8574             return false;
8575         }
8576 
8577         /**
8578          * Measure a child view using standard measurement policy, taking the padding
8579          * of the parent RecyclerView, any added item decorations and the child margins
8580          * into account.
8581          *
8582          * <p>If the RecyclerView can be scrolled in either dimension the caller may
8583          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8584          *
8585          * @param child Child view to measure
8586          * @param widthUsed Width in pixels currently consumed by other views, if relevant
8587          * @param heightUsed Height in pixels currently consumed by other views, if relevant
8588          */
measureChildWithMargins(View child, int widthUsed, int heightUsed)8589         public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
8590             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8591 
8592             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8593             widthUsed += insets.left + insets.right;
8594             heightUsed += insets.top + insets.bottom;
8595 
8596             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8597                     getPaddingLeft() + getPaddingRight()
8598                             + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
8599                     canScrollHorizontally());
8600             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8601                     getPaddingTop() + getPaddingBottom()
8602                             + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
8603                     canScrollVertically());
8604             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8605                 child.measure(widthSpec, heightSpec);
8606             }
8607         }
8608 
8609         /**
8610          * Calculate a MeasureSpec value for measuring a child view in one dimension.
8611          *
8612          * @param parentSize Size of the parent view where the child will be placed
8613          * @param padding Total space currently consumed by other elements of the parent
8614          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
8615          *                       Generally obtained from the child view's LayoutParams
8616          * @param canScroll true if the parent RecyclerView can scroll in this dimension
8617          *
8618          * @return a MeasureSpec value for the child view
8619          * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
8620          */
8621         @Deprecated
getChildMeasureSpec(int parentSize, int padding, int childDimension, boolean canScroll)8622         public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
8623                 boolean canScroll) {
8624             int size = Math.max(0, parentSize - padding);
8625             int resultSize = 0;
8626             int resultMode = 0;
8627             if (canScroll) {
8628                 if (childDimension >= 0) {
8629                     resultSize = childDimension;
8630                     resultMode = MeasureSpec.EXACTLY;
8631                 } else {
8632                     // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
8633                     // instead using UNSPECIFIED.
8634                     resultSize = 0;
8635                     resultMode = MeasureSpec.UNSPECIFIED;
8636                 }
8637             } else {
8638                 if (childDimension >= 0) {
8639                     resultSize = childDimension;
8640                     resultMode = MeasureSpec.EXACTLY;
8641                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
8642                     resultSize = size;
8643                     // TODO this should be my spec.
8644                     resultMode = MeasureSpec.EXACTLY;
8645                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8646                     resultSize = size;
8647                     resultMode = MeasureSpec.AT_MOST;
8648                 }
8649             }
8650             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
8651         }
8652 
8653         /**
8654          * Calculate a MeasureSpec value for measuring a child view in one dimension.
8655          *
8656          * @param parentSize Size of the parent view where the child will be placed
8657          * @param parentMode The measurement spec mode of the parent
8658          * @param padding Total space currently consumed by other elements of parent
8659          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
8660          *                       Generally obtained from the child view's LayoutParams
8661          * @param canScroll true if the parent RecyclerView can scroll in this dimension
8662          *
8663          * @return a MeasureSpec value for the child view
8664          */
getChildMeasureSpec(int parentSize, int parentMode, int padding, int childDimension, boolean canScroll)8665         public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
8666                 int childDimension, boolean canScroll) {
8667             int size = Math.max(0, parentSize - padding);
8668             int resultSize = 0;
8669             int resultMode = 0;
8670             if (canScroll) {
8671                 if (childDimension >= 0) {
8672                     resultSize = childDimension;
8673                     resultMode = MeasureSpec.EXACTLY;
8674                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
8675                     switch (parentMode) {
8676                         case MeasureSpec.AT_MOST:
8677                         case MeasureSpec.EXACTLY:
8678                             resultSize = size;
8679                             resultMode = parentMode;
8680                             break;
8681                         case MeasureSpec.UNSPECIFIED:
8682                             resultSize = 0;
8683                             resultMode = MeasureSpec.UNSPECIFIED;
8684                             break;
8685                     }
8686                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8687                     resultSize = 0;
8688                     resultMode = MeasureSpec.UNSPECIFIED;
8689                 }
8690             } else {
8691                 if (childDimension >= 0) {
8692                     resultSize = childDimension;
8693                     resultMode = MeasureSpec.EXACTLY;
8694                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
8695                     resultSize = size;
8696                     resultMode = parentMode;
8697                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8698                     resultSize = size;
8699                     if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
8700                         resultMode = MeasureSpec.AT_MOST;
8701                     } else {
8702                         resultMode = MeasureSpec.UNSPECIFIED;
8703                     }
8704 
8705                 }
8706             }
8707             //noinspection WrongConstant
8708             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
8709         }
8710 
8711         /**
8712          * Returns the measured width of the given child, plus the additional size of
8713          * any insets applied by {@link ItemDecoration ItemDecorations}.
8714          *
8715          * @param child Child view to query
8716          * @return child's measured width plus <code>ItemDecoration</code> insets
8717          *
8718          * @see View#getMeasuredWidth()
8719          */
getDecoratedMeasuredWidth(View child)8720         public int getDecoratedMeasuredWidth(View child) {
8721             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8722             return child.getMeasuredWidth() + insets.left + insets.right;
8723         }
8724 
8725         /**
8726          * Returns the measured height of the given child, plus the additional size of
8727          * any insets applied by {@link ItemDecoration ItemDecorations}.
8728          *
8729          * @param child Child view to query
8730          * @return child's measured height plus <code>ItemDecoration</code> insets
8731          *
8732          * @see View#getMeasuredHeight()
8733          */
getDecoratedMeasuredHeight(View child)8734         public int getDecoratedMeasuredHeight(View child) {
8735             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8736             return child.getMeasuredHeight() + insets.top + insets.bottom;
8737         }
8738 
8739         /**
8740          * Lay out the given child view within the RecyclerView using coordinates that
8741          * include any current {@link ItemDecoration ItemDecorations}.
8742          *
8743          * <p>LayoutManagers should prefer working in sizes and coordinates that include
8744          * item decoration insets whenever possible. This allows the LayoutManager to effectively
8745          * ignore decoration insets within measurement and layout code. See the following
8746          * methods:</p>
8747          * <ul>
8748          *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
8749          *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
8750          *     <li>{@link #measureChild(View, int, int)}</li>
8751          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
8752          *     <li>{@link #getDecoratedLeft(View)}</li>
8753          *     <li>{@link #getDecoratedTop(View)}</li>
8754          *     <li>{@link #getDecoratedRight(View)}</li>
8755          *     <li>{@link #getDecoratedBottom(View)}</li>
8756          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
8757          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
8758          * </ul>
8759          *
8760          * @param child Child to lay out
8761          * @param left Left edge, with item decoration insets included
8762          * @param top Top edge, with item decoration insets included
8763          * @param right Right edge, with item decoration insets included
8764          * @param bottom Bottom edge, with item decoration insets included
8765          *
8766          * @see View#layout(int, int, int, int)
8767          * @see #layoutDecoratedWithMargins(View, int, int, int, int)
8768          */
layoutDecorated(View child, int left, int top, int right, int bottom)8769         public void layoutDecorated(View child, int left, int top, int right, int bottom) {
8770             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8771             child.layout(left + insets.left, top + insets.top, right - insets.right,
8772                     bottom - insets.bottom);
8773         }
8774 
8775         /**
8776          * Lay out the given child view within the RecyclerView using coordinates that
8777          * include any current {@link ItemDecoration ItemDecorations} and margins.
8778          *
8779          * <p>LayoutManagers should prefer working in sizes and coordinates that include
8780          * item decoration insets whenever possible. This allows the LayoutManager to effectively
8781          * ignore decoration insets within measurement and layout code. See the following
8782          * methods:</p>
8783          * <ul>
8784          *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
8785          *     <li>{@link #measureChild(View, int, int)}</li>
8786          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
8787          *     <li>{@link #getDecoratedLeft(View)}</li>
8788          *     <li>{@link #getDecoratedTop(View)}</li>
8789          *     <li>{@link #getDecoratedRight(View)}</li>
8790          *     <li>{@link #getDecoratedBottom(View)}</li>
8791          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
8792          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
8793          * </ul>
8794          *
8795          * @param child Child to lay out
8796          * @param left Left edge, with item decoration insets and left margin included
8797          * @param top Top edge, with item decoration insets and top margin included
8798          * @param right Right edge, with item decoration insets and right margin included
8799          * @param bottom Bottom edge, with item decoration insets and bottom margin included
8800          *
8801          * @see View#layout(int, int, int, int)
8802          * @see #layoutDecorated(View, int, int, int, int)
8803          */
layoutDecoratedWithMargins(View child, int left, int top, int right, int bottom)8804         public void layoutDecoratedWithMargins(View child, int left, int top, int right,
8805                 int bottom) {
8806             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8807             final Rect insets = lp.mDecorInsets;
8808             child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
8809                     right - insets.right - lp.rightMargin,
8810                     bottom - insets.bottom - lp.bottomMargin);
8811         }
8812 
8813         /**
8814          * Calculates the bounding box of the View while taking into account its matrix changes
8815          * (translation, scale etc) with respect to the RecyclerView.
8816          * <p>
8817          * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
8818          * the View's matrix so that the decor offsets also go through the same transformation.
8819          *
8820          * @param child The ItemView whose bounding box should be calculated.
8821          * @param includeDecorInsets True if the decor insets should be included in the bounding box
8822          * @param out The rectangle into which the output will be written.
8823          */
getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out)8824         public void getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out) {
8825             if (includeDecorInsets) {
8826                 Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8827                 out.set(-insets.left, -insets.top,
8828                         child.getWidth() + insets.right, child.getHeight() + insets.bottom);
8829             } else {
8830                 out.set(0, 0, child.getWidth(), child.getHeight());
8831             }
8832 
8833             if (mRecyclerView != null) {
8834                 final Matrix childMatrix = child.getMatrix();
8835                 if (childMatrix != null && !childMatrix.isIdentity()) {
8836                     final RectF tempRectF = mRecyclerView.mTempRectF;
8837                     tempRectF.set(out);
8838                     childMatrix.mapRect(tempRectF);
8839                     out.set(
8840                             (int) Math.floor(tempRectF.left),
8841                             (int) Math.floor(tempRectF.top),
8842                             (int) Math.ceil(tempRectF.right),
8843                             (int) Math.ceil(tempRectF.bottom)
8844                     );
8845                 }
8846             }
8847             out.offset(child.getLeft(), child.getTop());
8848         }
8849 
8850         /**
8851          * Returns the bounds of the view including its decoration and margins.
8852          *
8853          * @param view The view element to check
8854          * @param outBounds A rect that will receive the bounds of the element including its
8855          *                  decoration and margins.
8856          */
getDecoratedBoundsWithMargins(View view, Rect outBounds)8857         public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
8858             RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
8859         }
8860 
8861         /**
8862          * Returns the left edge of the given child view within its parent, offset by any applied
8863          * {@link ItemDecoration ItemDecorations}.
8864          *
8865          * @param child Child to query
8866          * @return Child left edge with offsets applied
8867          * @see #getLeftDecorationWidth(View)
8868          */
getDecoratedLeft(View child)8869         public int getDecoratedLeft(View child) {
8870             return child.getLeft() - getLeftDecorationWidth(child);
8871         }
8872 
8873         /**
8874          * Returns the top edge of the given child view within its parent, offset by any applied
8875          * {@link ItemDecoration ItemDecorations}.
8876          *
8877          * @param child Child to query
8878          * @return Child top edge with offsets applied
8879          * @see #getTopDecorationHeight(View)
8880          */
getDecoratedTop(View child)8881         public int getDecoratedTop(View child) {
8882             return child.getTop() - getTopDecorationHeight(child);
8883         }
8884 
8885         /**
8886          * Returns the right edge of the given child view within its parent, offset by any applied
8887          * {@link ItemDecoration ItemDecorations}.
8888          *
8889          * @param child Child to query
8890          * @return Child right edge with offsets applied
8891          * @see #getRightDecorationWidth(View)
8892          */
getDecoratedRight(View child)8893         public int getDecoratedRight(View child) {
8894             return child.getRight() + getRightDecorationWidth(child);
8895         }
8896 
8897         /**
8898          * Returns the bottom edge of the given child view within its parent, offset by any applied
8899          * {@link ItemDecoration ItemDecorations}.
8900          *
8901          * @param child Child to query
8902          * @return Child bottom edge with offsets applied
8903          * @see #getBottomDecorationHeight(View)
8904          */
getDecoratedBottom(View child)8905         public int getDecoratedBottom(View child) {
8906             return child.getBottom() + getBottomDecorationHeight(child);
8907         }
8908 
8909         /**
8910          * Calculates the item decor insets applied to the given child and updates the provided
8911          * Rect instance with the inset values.
8912          * <ul>
8913          *     <li>The Rect's left is set to the total width of left decorations.</li>
8914          *     <li>The Rect's top is set to the total height of top decorations.</li>
8915          *     <li>The Rect's right is set to the total width of right decorations.</li>
8916          *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
8917          * </ul>
8918          * <p>
8919          * Note that item decorations are automatically calculated when one of the LayoutManager's
8920          * measure child methods is called. If you need to measure the child with custom specs via
8921          * {@link View#measure(int, int)}, you can use this method to get decorations.
8922          *
8923          * @param child The child view whose decorations should be calculated
8924          * @param outRect The Rect to hold result values
8925          */
calculateItemDecorationsForChild(View child, Rect outRect)8926         public void calculateItemDecorationsForChild(View child, Rect outRect) {
8927             if (mRecyclerView == null) {
8928                 outRect.set(0, 0, 0, 0);
8929                 return;
8930             }
8931             Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8932             outRect.set(insets);
8933         }
8934 
8935         /**
8936          * Returns the total height of item decorations applied to child's top.
8937          * <p>
8938          * Note that this value is not updated until the View is measured or
8939          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8940          *
8941          * @param child Child to query
8942          * @return The total height of item decorations applied to the child's top.
8943          * @see #getDecoratedTop(View)
8944          * @see #calculateItemDecorationsForChild(View, Rect)
8945          */
getTopDecorationHeight(View child)8946         public int getTopDecorationHeight(View child) {
8947             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
8948         }
8949 
8950         /**
8951          * Returns the total height of item decorations applied to child's bottom.
8952          * <p>
8953          * Note that this value is not updated until the View is measured or
8954          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8955          *
8956          * @param child Child to query
8957          * @return The total height of item decorations applied to the child's bottom.
8958          * @see #getDecoratedBottom(View)
8959          * @see #calculateItemDecorationsForChild(View, Rect)
8960          */
getBottomDecorationHeight(View child)8961         public int getBottomDecorationHeight(View child) {
8962             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
8963         }
8964 
8965         /**
8966          * Returns the total width of item decorations applied to child's left.
8967          * <p>
8968          * Note that this value is not updated until the View is measured or
8969          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8970          *
8971          * @param child Child to query
8972          * @return The total width of item decorations applied to the child's left.
8973          * @see #getDecoratedLeft(View)
8974          * @see #calculateItemDecorationsForChild(View, Rect)
8975          */
getLeftDecorationWidth(View child)8976         public int getLeftDecorationWidth(View child) {
8977             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
8978         }
8979 
8980         /**
8981          * Returns the total width of item decorations applied to child's right.
8982          * <p>
8983          * Note that this value is not updated until the View is measured or
8984          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8985          *
8986          * @param child Child to query
8987          * @return The total width of item decorations applied to the child's right.
8988          * @see #getDecoratedRight(View)
8989          * @see #calculateItemDecorationsForChild(View, Rect)
8990          */
getRightDecorationWidth(View child)8991         public int getRightDecorationWidth(View child) {
8992             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
8993         }
8994 
8995         /**
8996          * Called when searching for a focusable view in the given direction has failed
8997          * for the current content of the RecyclerView.
8998          *
8999          * <p>This is the LayoutManager's opportunity to populate views in the given direction
9000          * to fulfill the request if it can. The LayoutManager should attach and return
9001          * the view to be focused. The default implementation returns null.</p>
9002          *
9003          * @param focused   The currently focused view
9004          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9005          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9006          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9007          *                  or 0 for not applicable
9008          * @param recycler  The recycler to use for obtaining views for currently offscreen items
9009          * @param state     Transient state of RecyclerView
9010          * @return The chosen view to be focused
9011          */
9012         @Nullable
onFocusSearchFailed(View focused, int direction, Recycler recycler, State state)9013         public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
9014                 State state) {
9015             return null;
9016         }
9017 
9018         /**
9019          * This method gives a LayoutManager an opportunity to intercept the initial focus search
9020          * before the default behavior of {@link FocusFinder} is used. If this method returns
9021          * null FocusFinder will attempt to find a focusable child view. If it fails
9022          * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
9023          * will be called to give the LayoutManager an opportunity to add new views for items
9024          * that did not have attached views representing them. The LayoutManager should not add
9025          * or remove views from this method.
9026          *
9027          * @param focused The currently focused view
9028          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9029          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9030          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9031          * @return A descendant view to focus or null to fall back to default behavior.
9032          *         The default implementation returns null.
9033          */
onInterceptFocusSearch(View focused, int direction)9034         public View onInterceptFocusSearch(View focused, int direction) {
9035             return null;
9036         }
9037 
9038         /**
9039          * Called when a child of the RecyclerView wants a particular rectangle to be positioned
9040          * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
9041          * android.graphics.Rect, boolean)} for more details.
9042          *
9043          * <p>The base implementation will attempt to perform a standard programmatic scroll
9044          * to bring the given rect into view, within the padded area of the RecyclerView.</p>
9045          *
9046          * @param child The direct child making the request.
9047          * @param rect  The rectangle in the child's coordinates the child
9048          *              wishes to be on the screen.
9049          * @param immediate True to forbid animated or delayed scrolling,
9050          *                  false otherwise
9051          * @return Whether the group scrolled to handle the operation
9052          */
requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect, boolean immediate)9053         public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
9054                 boolean immediate) {
9055             final int parentLeft = getPaddingLeft();
9056             final int parentTop = getPaddingTop();
9057             final int parentRight = getWidth() - getPaddingRight();
9058             final int parentBottom = getHeight() - getPaddingBottom();
9059             final int childLeft = child.getLeft() + rect.left - child.getScrollX();
9060             final int childTop = child.getTop() + rect.top - child.getScrollY();
9061             final int childRight = childLeft + rect.width();
9062             final int childBottom = childTop + rect.height();
9063 
9064             final int offScreenLeft = Math.min(0, childLeft - parentLeft);
9065             final int offScreenTop = Math.min(0, childTop - parentTop);
9066             final int offScreenRight = Math.max(0, childRight - parentRight);
9067             final int offScreenBottom = Math.max(0, childBottom - parentBottom);
9068 
9069             // Favor the "start" layout direction over the end when bringing one side or the other
9070             // of a large rect into view. If we decide to bring in end because start is already
9071             // visible, limit the scroll such that start won't go out of bounds.
9072             final int dx;
9073             if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
9074                 dx = offScreenRight != 0 ? offScreenRight
9075                         : Math.max(offScreenLeft, childRight - parentRight);
9076             } else {
9077                 dx = offScreenLeft != 0 ? offScreenLeft
9078                         : Math.min(childLeft - parentLeft, offScreenRight);
9079             }
9080 
9081             // Favor bringing the top into view over the bottom. If top is already visible and
9082             // we should scroll to make bottom visible, make sure top does not go out of bounds.
9083             final int dy = offScreenTop != 0 ? offScreenTop
9084                     : Math.min(childTop - parentTop, offScreenBottom);
9085 
9086             if (dx != 0 || dy != 0) {
9087                 if (immediate) {
9088                     parent.scrollBy(dx, dy);
9089                 } else {
9090                     parent.smoothScrollBy(dx, dy);
9091                 }
9092                 return true;
9093             }
9094             return false;
9095         }
9096 
9097         /**
9098          * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
9099          */
9100         @Deprecated
onRequestChildFocus(RecyclerView parent, View child, View focused)9101         public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
9102             // eat the request if we are in the middle of a scroll or layout
9103             return isSmoothScrolling() || parent.isComputingLayout();
9104         }
9105 
9106         /**
9107          * Called when a descendant view of the RecyclerView requests focus.
9108          *
9109          * <p>A LayoutManager wishing to keep focused views aligned in a specific
9110          * portion of the view may implement that behavior in an override of this method.</p>
9111          *
9112          * <p>If the LayoutManager executes different behavior that should override the default
9113          * behavior of scrolling the focused child on screen instead of running alongside it,
9114          * this method should return true.</p>
9115          *
9116          * @param parent  The RecyclerView hosting this LayoutManager
9117          * @param state   Current state of RecyclerView
9118          * @param child   Direct child of the RecyclerView containing the newly focused view
9119          * @param focused The newly focused view. This may be the same view as child or it may be
9120          *                null
9121          * @return true if the default scroll behavior should be suppressed
9122          */
onRequestChildFocus(RecyclerView parent, State state, View child, View focused)9123         public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
9124                 View focused) {
9125             return onRequestChildFocus(parent, child, focused);
9126         }
9127 
9128         /**
9129          * Called if the RecyclerView this LayoutManager is bound to has a different adapter set.
9130          * The LayoutManager may use this opportunity to clear caches and configure state such
9131          * that it can relayout appropriately with the new data and potentially new view types.
9132          *
9133          * <p>The default implementation removes all currently attached views.</p>
9134          *
9135          * @param oldAdapter The previous adapter instance. Will be null if there was previously no
9136          *                   adapter.
9137          * @param newAdapter The new adapter instance. Might be null if
9138          *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
9139          */
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter)9140         public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
9141         }
9142 
9143         /**
9144          * Called to populate focusable views within the RecyclerView.
9145          *
9146          * <p>The LayoutManager implementation should return <code>true</code> if the default
9147          * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
9148          * suppressed.</p>
9149          *
9150          * <p>The default implementation returns <code>false</code> to trigger RecyclerView
9151          * to fall back to the default ViewGroup behavior.</p>
9152          *
9153          * @param recyclerView The RecyclerView hosting this LayoutManager
9154          * @param views List of output views. This method should add valid focusable views
9155          *              to this list.
9156          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9157          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9158          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9159          * @param focusableMode The type of focusables to be added.
9160          *
9161          * @return true to suppress the default behavior, false to add default focusables after
9162          *         this method returns.
9163          *
9164          * @see #FOCUSABLES_ALL
9165          * @see #FOCUSABLES_TOUCH_MODE
9166          */
onAddFocusables(RecyclerView recyclerView, ArrayList<View> views, int direction, int focusableMode)9167         public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,
9168                 int direction, int focusableMode) {
9169             return false;
9170         }
9171 
9172         /**
9173          * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving
9174          * detailed information on what has actually changed.
9175          *
9176          * @param recyclerView
9177          */
onItemsChanged(RecyclerView recyclerView)9178         public void onItemsChanged(RecyclerView recyclerView) {
9179         }
9180 
9181         /**
9182          * Called when items have been added to the adapter. The LayoutManager may choose to
9183          * requestLayout if the inserted items would require refreshing the currently visible set
9184          * of child views. (e.g. currently empty space would be filled by appended items, etc.)
9185          *
9186          * @param recyclerView
9187          * @param positionStart
9188          * @param itemCount
9189          */
onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount)9190         public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
9191         }
9192 
9193         /**
9194          * Called when items have been removed from the adapter.
9195          *
9196          * @param recyclerView
9197          * @param positionStart
9198          * @param itemCount
9199          */
onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount)9200         public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
9201         }
9202 
9203         /**
9204          * Called when items have been changed in the adapter.
9205          * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
9206          * instead, then this callback will not be invoked.
9207          *
9208          * @param recyclerView
9209          * @param positionStart
9210          * @param itemCount
9211          */
onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount)9212         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
9213         }
9214 
9215         /**
9216          * Called when items have been changed in the adapter and with optional payload.
9217          * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
9218          *
9219          * @param recyclerView
9220          * @param positionStart
9221          * @param itemCount
9222          * @param payload
9223          */
onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount, Object payload)9224         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
9225                 Object payload) {
9226             onItemsUpdated(recyclerView, positionStart, itemCount);
9227         }
9228 
9229         /**
9230          * Called when an item is moved withing the adapter.
9231          * <p>
9232          * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
9233          * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
9234          * is called.
9235          *
9236          * @param recyclerView
9237          * @param from
9238          * @param to
9239          * @param itemCount
9240          */
onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount)9241         public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
9242 
9243         }
9244 
9245 
9246         /**
9247          * <p>Override this method if you want to support scroll bars.</p>
9248          *
9249          * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
9250          *
9251          * <p>Default implementation returns 0.</p>
9252          *
9253          * @param state Current state of RecyclerView
9254          * @return The horizontal extent of the scrollbar's thumb
9255          * @see RecyclerView#computeHorizontalScrollExtent()
9256          */
computeHorizontalScrollExtent(State state)9257         public int computeHorizontalScrollExtent(State state) {
9258             return 0;
9259         }
9260 
9261         /**
9262          * <p>Override this method if you want to support scroll bars.</p>
9263          *
9264          * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
9265          *
9266          * <p>Default implementation returns 0.</p>
9267          *
9268          * @param state Current State of RecyclerView where you can find total item count
9269          * @return The horizontal offset of the scrollbar's thumb
9270          * @see RecyclerView#computeHorizontalScrollOffset()
9271          */
computeHorizontalScrollOffset(State state)9272         public int computeHorizontalScrollOffset(State state) {
9273             return 0;
9274         }
9275 
9276         /**
9277          * <p>Override this method if you want to support scroll bars.</p>
9278          *
9279          * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
9280          *
9281          * <p>Default implementation returns 0.</p>
9282          *
9283          * @param state Current State of RecyclerView where you can find total item count
9284          * @return The total horizontal range represented by the vertical scrollbar
9285          * @see RecyclerView#computeHorizontalScrollRange()
9286          */
computeHorizontalScrollRange(State state)9287         public int computeHorizontalScrollRange(State state) {
9288             return 0;
9289         }
9290 
9291         /**
9292          * <p>Override this method if you want to support scroll bars.</p>
9293          *
9294          * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
9295          *
9296          * <p>Default implementation returns 0.</p>
9297          *
9298          * @param state Current state of RecyclerView
9299          * @return The vertical extent of the scrollbar's thumb
9300          * @see RecyclerView#computeVerticalScrollExtent()
9301          */
computeVerticalScrollExtent(State state)9302         public int computeVerticalScrollExtent(State state) {
9303             return 0;
9304         }
9305 
9306         /**
9307          * <p>Override this method if you want to support scroll bars.</p>
9308          *
9309          * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
9310          *
9311          * <p>Default implementation returns 0.</p>
9312          *
9313          * @param state Current State of RecyclerView where you can find total item count
9314          * @return The vertical offset of the scrollbar's thumb
9315          * @see RecyclerView#computeVerticalScrollOffset()
9316          */
computeVerticalScrollOffset(State state)9317         public int computeVerticalScrollOffset(State state) {
9318             return 0;
9319         }
9320 
9321         /**
9322          * <p>Override this method if you want to support scroll bars.</p>
9323          *
9324          * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
9325          *
9326          * <p>Default implementation returns 0.</p>
9327          *
9328          * @param state Current State of RecyclerView where you can find total item count
9329          * @return The total vertical range represented by the vertical scrollbar
9330          * @see RecyclerView#computeVerticalScrollRange()
9331          */
computeVerticalScrollRange(State state)9332         public int computeVerticalScrollRange(State state) {
9333             return 0;
9334         }
9335 
9336         /**
9337          * Measure the attached RecyclerView. Implementations must call
9338          * {@link #setMeasuredDimension(int, int)} before returning.
9339          *
9340          * <p>The default implementation will handle EXACTLY measurements and respect
9341          * the minimum width and height properties of the host RecyclerView if measured
9342          * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
9343          * will consume all available space.</p>
9344          *
9345          * @param recycler Recycler
9346          * @param state Transient state of RecyclerView
9347          * @param widthSpec Width {@link android.view.View.MeasureSpec}
9348          * @param heightSpec Height {@link android.view.View.MeasureSpec}
9349          */
onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec)9350         public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
9351             mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
9352         }
9353 
9354         /**
9355          * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
9356          * host RecyclerView.
9357          *
9358          * @param widthSize Measured width
9359          * @param heightSize Measured height
9360          */
setMeasuredDimension(int widthSize, int heightSize)9361         public void setMeasuredDimension(int widthSize, int heightSize) {
9362             mRecyclerView.setMeasuredDimension(widthSize, heightSize);
9363         }
9364 
9365         /**
9366          * @return The host RecyclerView's {@link View#getMinimumWidth()}
9367          */
getMinimumWidth()9368         public int getMinimumWidth() {
9369             return mRecyclerView.getMinimumWidth();
9370         }
9371 
9372         /**
9373          * @return The host RecyclerView's {@link View#getMinimumHeight()}
9374          */
getMinimumHeight()9375         public int getMinimumHeight() {
9376             return mRecyclerView.getMinimumHeight();
9377         }
9378         /**
9379          * <p>Called when the LayoutManager should save its state. This is a good time to save your
9380          * scroll position, configuration and anything else that may be required to restore the same
9381          * layout state if the LayoutManager is recreated.</p>
9382          * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
9383          * restore. This will let you share information between your LayoutManagers but it is also
9384          * your responsibility to make sure they use the same parcelable class.</p>
9385          *
9386          * @return Necessary information for LayoutManager to be able to restore its state
9387          */
onSaveInstanceState()9388         public Parcelable onSaveInstanceState() {
9389             return null;
9390         }
9391 
9392 
onRestoreInstanceState(Parcelable state)9393         public void onRestoreInstanceState(Parcelable state) {
9394 
9395         }
9396 
stopSmoothScroller()9397         void stopSmoothScroller() {
9398             if (mSmoothScroller != null) {
9399                 mSmoothScroller.stop();
9400             }
9401         }
9402 
onSmoothScrollerStopped(SmoothScroller smoothScroller)9403         private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
9404             if (mSmoothScroller == smoothScroller) {
9405                 mSmoothScroller = null;
9406             }
9407         }
9408 
9409         /**
9410          * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
9411          *
9412          * @param state The new scroll state for RecyclerView
9413          */
onScrollStateChanged(int state)9414         public void onScrollStateChanged(int state) {
9415         }
9416 
9417         /**
9418          * Removes all views and recycles them using the given recycler.
9419          * <p>
9420          * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
9421          * <p>
9422          * If a View is marked as "ignored", it is not removed nor recycled.
9423          *
9424          * @param recycler Recycler to use to recycle children
9425          * @see #removeAndRecycleView(View, Recycler)
9426          * @see #removeAndRecycleViewAt(int, Recycler)
9427          * @see #ignoreView(View)
9428          */
removeAndRecycleAllViews(Recycler recycler)9429         public void removeAndRecycleAllViews(Recycler recycler) {
9430             for (int i = getChildCount() - 1; i >= 0; i--) {
9431                 final View view = getChildAt(i);
9432                 if (!getChildViewHolderInt(view).shouldIgnore()) {
9433                     removeAndRecycleViewAt(i, recycler);
9434                 }
9435             }
9436         }
9437 
9438         // called by accessibility delegate
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)9439         void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
9440             onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
9441         }
9442 
9443         /**
9444          * Called by the AccessibilityDelegate when the information about the current layout should
9445          * be populated.
9446          * <p>
9447          * Default implementation adds a {@link
9448          * android.view.accessibility.AccessibilityNodeInfo.CollectionInfo}.
9449          * <p>
9450          * You should override
9451          * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
9452          * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
9453          * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
9454          * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
9455          * more accurate accessibility information.
9456          *
9457          * @param recycler The Recycler that can be used to convert view positions into adapter
9458          *                 positions
9459          * @param state    The current state of RecyclerView
9460          * @param info     The info that should be filled by the LayoutManager
9461          * @see View#onInitializeAccessibilityNodeInfo(
9462          *android.view.accessibility.AccessibilityNodeInfo)
9463          * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9464          * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9465          * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
9466          * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9467          */
onInitializeAccessibilityNodeInfo(Recycler recycler, State state, AccessibilityNodeInfo info)9468         public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
9469                 AccessibilityNodeInfo info) {
9470             if (mRecyclerView.canScrollVertically(-1)
9471                     || mRecyclerView.canScrollHorizontally(-1)) {
9472                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
9473                 info.setScrollable(true);
9474             }
9475             if (mRecyclerView.canScrollVertically(1)
9476                     || mRecyclerView.canScrollHorizontally(1)) {
9477                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
9478                 info.setScrollable(true);
9479             }
9480             final AccessibilityNodeInfo.CollectionInfo collectionInfo =
9481                     AccessibilityNodeInfo.CollectionInfo
9482                             .obtain(getRowCountForAccessibility(recycler, state),
9483                                     getColumnCountForAccessibility(recycler, state),
9484                                     isLayoutHierarchical(recycler, state),
9485                                     getSelectionModeForAccessibility(recycler, state));
9486             info.setCollectionInfo(collectionInfo);
9487         }
9488 
9489         // called by accessibility delegate
onInitializeAccessibilityEvent(AccessibilityEvent event)9490         public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
9491             onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
9492         }
9493 
9494         /**
9495          * Called by the accessibility delegate to initialize an accessibility event.
9496          * <p>
9497          * Default implementation adds item count and scroll information to the event.
9498          *
9499          * @param recycler The Recycler that can be used to convert view positions into adapter
9500          *                 positions
9501          * @param state    The current state of RecyclerView
9502          * @param event    The event instance to initialize
9503          * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
9504          */
onInitializeAccessibilityEvent(Recycler recycler, State state, AccessibilityEvent event)9505         public void onInitializeAccessibilityEvent(Recycler recycler, State state,
9506                 AccessibilityEvent event) {
9507             if (mRecyclerView == null || event == null) {
9508                 return;
9509             }
9510             event.setScrollable(mRecyclerView.canScrollVertically(1)
9511                     || mRecyclerView.canScrollVertically(-1)
9512                     || mRecyclerView.canScrollHorizontally(-1)
9513                     || mRecyclerView.canScrollHorizontally(1));
9514 
9515             if (mRecyclerView.mAdapter != null) {
9516                 event.setItemCount(mRecyclerView.mAdapter.getItemCount());
9517             }
9518         }
9519 
9520         // called by accessibility delegate
onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfo info)9521         void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfo info) {
9522             final ViewHolder vh = getChildViewHolderInt(host);
9523             // avoid trying to create accessibility node info for removed children
9524             if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
9525                 onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
9526                         mRecyclerView.mState, host, info);
9527             }
9528         }
9529 
9530         /**
9531          * Called by the AccessibilityDelegate when the accessibility information for a specific
9532          * item should be populated.
9533          * <p>
9534          * Default implementation adds basic positioning information about the item.
9535          *
9536          * @param recycler The Recycler that can be used to convert view positions into adapter
9537          *                 positions
9538          * @param state    The current state of RecyclerView
9539          * @param host     The child for which accessibility node info should be populated
9540          * @param info     The info to fill out about the item
9541          * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
9542          * android.view.accessibility.AccessibilityNodeInfo)
9543          */
onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state, View host, AccessibilityNodeInfo info)9544         public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
9545                 View host, AccessibilityNodeInfo info) {
9546             int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
9547             int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
9548             final AccessibilityNodeInfo.CollectionItemInfo itemInfo =
9549                     AccessibilityNodeInfo.CollectionItemInfo.obtain(rowIndexGuess, 1,
9550                             columnIndexGuess, 1, false, false);
9551             info.setCollectionItemInfo(itemInfo);
9552         }
9553 
9554         /**
9555          * A LayoutManager can call this method to force RecyclerView to run simple animations in
9556          * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
9557          * change).
9558          * <p>
9559          * Note that, calling this method will not guarantee that RecyclerView will run animations
9560          * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
9561          * not run any animations but will still clear this flag after the layout is complete.
9562          *
9563          */
requestSimpleAnimationsInNextLayout()9564         public void requestSimpleAnimationsInNextLayout() {
9565             mRequestedSimpleAnimations = true;
9566         }
9567 
9568         /**
9569          * Returns the selection mode for accessibility. Should be
9570          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE},
9571          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_SINGLE} or
9572          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_MULTIPLE}.
9573          * <p>
9574          * Default implementation returns
9575          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE}.
9576          *
9577          * @param recycler The Recycler that can be used to convert view positions into adapter
9578          *                 positions
9579          * @param state    The current state of RecyclerView
9580          * @return Selection mode for accessibility. Default implementation returns
9581          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE}.
9582          */
getSelectionModeForAccessibility(Recycler recycler, State state)9583         public int getSelectionModeForAccessibility(Recycler recycler, State state) {
9584             return AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_NONE;
9585         }
9586 
9587         /**
9588          * Returns the number of rows for accessibility.
9589          * <p>
9590          * Default implementation returns the number of items in the adapter if LayoutManager
9591          * supports vertical scrolling or 1 if LayoutManager does not support vertical
9592          * scrolling.
9593          *
9594          * @param recycler The Recycler that can be used to convert view positions into adapter
9595          *                 positions
9596          * @param state    The current state of RecyclerView
9597          * @return The number of rows in LayoutManager for accessibility.
9598          */
getRowCountForAccessibility(Recycler recycler, State state)9599         public int getRowCountForAccessibility(Recycler recycler, State state) {
9600             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
9601                 return 1;
9602             }
9603             return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
9604         }
9605 
9606         /**
9607          * Returns the number of columns for accessibility.
9608          * <p>
9609          * Default implementation returns the number of items in the adapter if LayoutManager
9610          * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
9611          * scrolling.
9612          *
9613          * @param recycler The Recycler that can be used to convert view positions into adapter
9614          *                 positions
9615          * @param state    The current state of RecyclerView
9616          * @return The number of rows in LayoutManager for accessibility.
9617          */
getColumnCountForAccessibility(Recycler recycler, State state)9618         public int getColumnCountForAccessibility(Recycler recycler, State state) {
9619             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
9620                 return 1;
9621             }
9622             return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
9623         }
9624 
9625         /**
9626          * Returns whether layout is hierarchical or not to be used for accessibility.
9627          * <p>
9628          * Default implementation returns false.
9629          *
9630          * @param recycler The Recycler that can be used to convert view positions into adapter
9631          *                 positions
9632          * @param state    The current state of RecyclerView
9633          * @return True if layout is hierarchical.
9634          */
isLayoutHierarchical(Recycler recycler, State state)9635         public boolean isLayoutHierarchical(Recycler recycler, State state) {
9636             return false;
9637         }
9638 
9639         // called by accessibility delegate
performAccessibilityAction(int action, Bundle args)9640         boolean performAccessibilityAction(int action, Bundle args) {
9641             return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
9642                     action, args);
9643         }
9644 
9645         /**
9646          * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
9647          *
9648          * @param recycler  The Recycler that can be used to convert view positions into adapter
9649          *                  positions
9650          * @param state     The current state of RecyclerView
9651          * @param action    The action to perform
9652          * @param args      Optional action arguments
9653          * @see View#performAccessibilityAction(int, android.os.Bundle)
9654          */
performAccessibilityAction(Recycler recycler, State state, int action, Bundle args)9655         public boolean performAccessibilityAction(Recycler recycler, State state, int action,
9656                 Bundle args) {
9657             if (mRecyclerView == null) {
9658                 return false;
9659             }
9660             int vScroll = 0, hScroll = 0;
9661             switch (action) {
9662                 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
9663                     if (mRecyclerView.canScrollVertically(-1)) {
9664                         vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
9665                     }
9666                     if (mRecyclerView.canScrollHorizontally(-1)) {
9667                         hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
9668                     }
9669                     break;
9670                 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
9671                     if (mRecyclerView.canScrollVertically(1)) {
9672                         vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
9673                     }
9674                     if (mRecyclerView.canScrollHorizontally(1)) {
9675                         hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
9676                     }
9677                     break;
9678             }
9679             if (vScroll == 0 && hScroll == 0) {
9680                 return false;
9681             }
9682             mRecyclerView.smoothScrollBy(hScroll, vScroll);
9683             return true;
9684         }
9685 
9686         // called by accessibility delegate
performAccessibilityActionForItem(View view, int action, Bundle args)9687         boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
9688             return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
9689                     view, action, args);
9690         }
9691 
9692         /**
9693          * Called by AccessibilityDelegate when an accessibility action is requested on one of the
9694          * children of LayoutManager.
9695          * <p>
9696          * Default implementation does not do anything.
9697          *
9698          * @param recycler The Recycler that can be used to convert view positions into adapter
9699          *                 positions
9700          * @param state    The current state of RecyclerView
9701          * @param view     The child view on which the action is performed
9702          * @param action   The action to perform
9703          * @param args     Optional action arguments
9704          * @return true if action is handled
9705          * @see View#performAccessibilityAction(int, android.os.Bundle)
9706          */
performAccessibilityActionForItem(Recycler recycler, State state, View view, int action, Bundle args)9707         public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
9708                 int action, Bundle args) {
9709             return false;
9710         }
9711 
9712         /**
9713          * Parse the xml attributes to get the most common properties used by layout managers.
9714          *
9715          * @return an object containing the properties as specified in the attrs.
9716          */
getProperties(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)9717         public static Properties getProperties(Context context, AttributeSet attrs,
9718                 int defStyleAttr, int defStyleRes) {
9719             Properties properties = new Properties();
9720             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
9721                     defStyleAttr, defStyleRes);
9722             properties.orientation = a.getInt(R.styleable.RecyclerView_orientation, VERTICAL);
9723             properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
9724             properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
9725             properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
9726             a.recycle();
9727             return properties;
9728         }
9729 
setExactMeasureSpecsFrom(RecyclerView recyclerView)9730         void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
9731             setMeasureSpecs(
9732                     MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
9733                     MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
9734             );
9735         }
9736 
9737         /**
9738          * Internal API to allow LayoutManagers to be measured twice.
9739          * <p>
9740          * This is not public because LayoutManagers should be able to handle their layouts in one
9741          * pass but it is very convenient to make existing LayoutManagers support wrapping content
9742          * when both orientations are undefined.
9743          * <p>
9744          * This API will be removed after default LayoutManagers properly implement wrap content in
9745          * non-scroll orientation.
9746          */
shouldMeasureTwice()9747         boolean shouldMeasureTwice() {
9748             return false;
9749         }
9750 
hasFlexibleChildInBothOrientations()9751         boolean hasFlexibleChildInBothOrientations() {
9752             final int childCount = getChildCount();
9753             for (int i = 0; i < childCount; i++) {
9754                 final View child = getChildAt(i);
9755                 final ViewGroup.LayoutParams lp = child.getLayoutParams();
9756                 if (lp.width < 0 && lp.height < 0) {
9757                     return true;
9758                 }
9759             }
9760             return false;
9761         }
9762 
9763         /**
9764          * Some general properties that a LayoutManager may want to use.
9765          */
9766         public static class Properties {
9767             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation */
9768             public int orientation;
9769             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount */
9770             public int spanCount;
9771             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout */
9772             public boolean reverseLayout;
9773             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd */
9774             public boolean stackFromEnd;
9775         }
9776     }
9777 
9778     /**
9779      * An ItemDecoration allows the application to add a special drawing and layout offset
9780      * to specific item views from the adapter's data set. This can be useful for drawing dividers
9781      * between items, highlights, visual grouping boundaries and more.
9782      *
9783      * <p>All ItemDecorations are drawn in the order they were added, before the item
9784      * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
9785      * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
9786      * RecyclerView.State)}.</p>
9787      */
9788     public abstract static class ItemDecoration {
9789         /**
9790          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
9791          * Any content drawn by this method will be drawn before the item views are drawn,
9792          * and will thus appear underneath the views.
9793          *
9794          * @param c Canvas to draw into
9795          * @param parent RecyclerView this ItemDecoration is drawing into
9796          * @param state The current state of RecyclerView
9797          */
onDraw(Canvas c, RecyclerView parent, State state)9798         public void onDraw(Canvas c, RecyclerView parent, State state) {
9799             onDraw(c, parent);
9800         }
9801 
9802         /**
9803          * @deprecated
9804          * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
9805          */
9806         @Deprecated
onDraw(Canvas c, RecyclerView parent)9807         public void onDraw(Canvas c, RecyclerView parent) {
9808         }
9809 
9810         /**
9811          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
9812          * Any content drawn by this method will be drawn after the item views are drawn
9813          * and will thus appear over the views.
9814          *
9815          * @param c Canvas to draw into
9816          * @param parent RecyclerView this ItemDecoration is drawing into
9817          * @param state The current state of RecyclerView.
9818          */
onDrawOver(Canvas c, RecyclerView parent, State state)9819         public void onDrawOver(Canvas c, RecyclerView parent, State state) {
9820             onDrawOver(c, parent);
9821         }
9822 
9823         /**
9824          * @deprecated
9825          * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
9826          */
9827         @Deprecated
onDrawOver(Canvas c, RecyclerView parent)9828         public void onDrawOver(Canvas c, RecyclerView parent) {
9829         }
9830 
9831 
9832         /**
9833          * @deprecated
9834          * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
9835          */
9836         @Deprecated
getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)9837         public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
9838             outRect.set(0, 0, 0, 0);
9839         }
9840 
9841         /**
9842          * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
9843          * the number of pixels that the item view should be inset by, similar to padding or margin.
9844          * The default implementation sets the bounds of outRect to 0 and returns.
9845          *
9846          * <p>
9847          * If this ItemDecoration does not affect the positioning of item views, it should set
9848          * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
9849          * before returning.
9850          *
9851          * <p>
9852          * If you need to access Adapter for additional data, you can call
9853          * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
9854          * View.
9855          *
9856          * @param outRect Rect to receive the output.
9857          * @param view    The child view to decorate
9858          * @param parent  RecyclerView this ItemDecoration is decorating
9859          * @param state   The current state of RecyclerView.
9860          */
getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)9861         public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
9862             getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
9863                     parent);
9864         }
9865     }
9866 
9867     /**
9868      * An OnItemTouchListener allows the application to intercept touch events in progress at the
9869      * view hierarchy level of the RecyclerView before those touch events are considered for
9870      * RecyclerView's own scrolling behavior.
9871      *
9872      * <p>This can be useful for applications that wish to implement various forms of gestural
9873      * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
9874      * a touch interaction already in progress even if the RecyclerView is already handling that
9875      * gesture stream itself for the purposes of scrolling.</p>
9876      *
9877      * @see SimpleOnItemTouchListener
9878      */
9879     public interface OnItemTouchListener {
9880         /**
9881          * Silently observe and/or take over touch events sent to the RecyclerView
9882          * before they are handled by either the RecyclerView itself or its child views.
9883          *
9884          * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
9885          * in the order in which each listener was added, before any other touch processing
9886          * by the RecyclerView itself or child views occurs.</p>
9887          *
9888          * @param e MotionEvent describing the touch event. All coordinates are in
9889          *          the RecyclerView's coordinate system.
9890          * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
9891          *         to continue with the current behavior and continue observing future events in
9892          *         the gesture.
9893          */
9894         boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
9895 
9896         /**
9897          * Process a touch event as part of a gesture that was claimed by returning true from
9898          * a previous call to {@link #onInterceptTouchEvent}.
9899          *
9900          * @param e MotionEvent describing the touch event. All coordinates are in
9901          *          the RecyclerView's coordinate system.
9902          */
9903         void onTouchEvent(RecyclerView rv, MotionEvent e);
9904 
9905         /**
9906          * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
9907          * intercept touch events with
9908          * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
9909          *
9910          * @param disallowIntercept True if the child does not want the parent to
9911          *            intercept touch events.
9912          * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
9913          */
9914         void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
9915     }
9916 
9917     /**
9918      * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies
9919      * and default return values.
9920      * <p>
9921      * You may prefer to extend this class if you don't need to override all methods. Another
9922      * benefit of using this class is future compatibility. As the interface may change, we'll
9923      * always provide a default implementation on this class so that your code won't break when
9924      * you update to a new version of the support library.
9925      */
9926     public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
9927         @Override
onInterceptTouchEvent(RecyclerView rv, MotionEvent e)9928         public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
9929             return false;
9930         }
9931 
9932         @Override
onTouchEvent(RecyclerView rv, MotionEvent e)9933         public void onTouchEvent(RecyclerView rv, MotionEvent e) {
9934         }
9935 
9936         @Override
onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)9937         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
9938         }
9939     }
9940 
9941 
9942     /**
9943      * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
9944      * has occurred on that RecyclerView.
9945      * <p>
9946      * @see RecyclerView#addOnScrollListener(OnScrollListener)
9947      * @see RecyclerView#clearOnChildAttachStateChangeListeners()
9948      *
9949      */
9950     public abstract static class OnScrollListener {
9951         /**
9952          * Callback method to be invoked when RecyclerView's scroll state changes.
9953          *
9954          * @param recyclerView The RecyclerView whose scroll state has changed.
9955          * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
9956          *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
9957          */
onScrollStateChanged(RecyclerView recyclerView, int newState)9958         public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
9959 
9960         /**
9961          * Callback method to be invoked when the RecyclerView has been scrolled. This will be
9962          * called after the scroll has completed.
9963          * <p>
9964          * This callback will also be called if visible item range changes after a layout
9965          * calculation. In that case, dx and dy will be 0.
9966          *
9967          * @param recyclerView The RecyclerView which scrolled.
9968          * @param dx The amount of horizontal scroll.
9969          * @param dy The amount of vertical scroll.
9970          */
onScrolled(RecyclerView recyclerView, int dx, int dy)9971         public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
9972     }
9973 
9974     /**
9975      * A RecyclerListener can be set on a RecyclerView to receive messages whenever
9976      * a view is recycled.
9977      *
9978      * @see RecyclerView#setRecyclerListener(RecyclerListener)
9979      */
9980     public interface RecyclerListener {
9981 
9982         /**
9983          * This method is called whenever the view in the ViewHolder is recycled.
9984          *
9985          * RecyclerView calls this method right before clearing ViewHolder's internal data and
9986          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
9987          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
9988          * its adapter position.
9989          *
9990          * @param holder The ViewHolder containing the view that was recycled
9991          */
9992         void onViewRecycled(ViewHolder holder);
9993     }
9994 
9995     /**
9996      * A Listener interface that can be attached to a RecylcerView to get notified
9997      * whenever a ViewHolder is attached to or detached from RecyclerView.
9998      */
9999     public interface OnChildAttachStateChangeListener {
10000 
10001         /**
10002          * Called when a view is attached to the RecyclerView.
10003          *
10004          * @param view The View which is attached to the RecyclerView
10005          */
10006         void onChildViewAttachedToWindow(View view);
10007 
10008         /**
10009          * Called when a view is detached from RecyclerView.
10010          *
10011          * @param view The View which is being detached from the RecyclerView
10012          */
10013         void onChildViewDetachedFromWindow(View view);
10014     }
10015 
10016     /**
10017      * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
10018      *
10019      * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
10020      * potentially expensive {@link View#findViewById(int)} results.</p>
10021      *
10022      * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
10023      * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
10024      * their own custom ViewHolder implementations to store data that makes binding view contents
10025      * easier. Implementations should assume that individual item views will hold strong references
10026      * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
10027      * strong references to extra off-screen item views for caching purposes</p>
10028      */
10029     public abstract static class ViewHolder {
10030         public final View itemView;
10031         WeakReference<RecyclerView> mNestedRecyclerView;
10032         int mPosition = NO_POSITION;
10033         int mOldPosition = NO_POSITION;
10034         long mItemId = NO_ID;
10035         int mItemViewType = INVALID_TYPE;
10036         int mPreLayoutPosition = NO_POSITION;
10037 
10038         // The item that this holder is shadowing during an item change event/animation
10039         ViewHolder mShadowedHolder = null;
10040         // The item that is shadowing this holder during an item change event/animation
10041         ViewHolder mShadowingHolder = null;
10042 
10043         /**
10044          * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
10045          * are all valid.
10046          */
10047         static final int FLAG_BOUND = 1 << 0;
10048 
10049         /**
10050          * The data this ViewHolder's view reflects is stale and needs to be rebound
10051          * by the adapter. mPosition and mItemId are consistent.
10052          */
10053         static final int FLAG_UPDATE = 1 << 1;
10054 
10055         /**
10056          * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
10057          * are not to be trusted and may no longer match the item view type.
10058          * This ViewHolder must be fully rebound to different data.
10059          */
10060         static final int FLAG_INVALID = 1 << 2;
10061 
10062         /**
10063          * This ViewHolder points at data that represents an item previously removed from the
10064          * data set. Its view may still be used for things like outgoing animations.
10065          */
10066         static final int FLAG_REMOVED = 1 << 3;
10067 
10068         /**
10069          * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
10070          * and is intended to keep views around during animations.
10071          */
10072         static final int FLAG_NOT_RECYCLABLE = 1 << 4;
10073 
10074         /**
10075          * This ViewHolder is returned from scrap which means we are expecting an addView call
10076          * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
10077          * the end of the layout pass and then recycled by RecyclerView if it is not added back to
10078          * the RecyclerView.
10079          */
10080         static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
10081 
10082         /**
10083          * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
10084          * it unless LayoutManager is replaced.
10085          * It is still fully visible to the LayoutManager.
10086          */
10087         static final int FLAG_IGNORE = 1 << 7;
10088 
10089         /**
10090          * When the View is detached form the parent, we set this flag so that we can take correct
10091          * action when we need to remove it or add it back.
10092          */
10093         static final int FLAG_TMP_DETACHED = 1 << 8;
10094 
10095         /**
10096          * Set when we can no longer determine the adapter position of this ViewHolder until it is
10097          * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
10098          * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
10099          * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
10100          * re-calculated.
10101          */
10102         static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
10103 
10104         /**
10105          * Set when a addChangePayload(null) is called
10106          */
10107         static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
10108 
10109         /**
10110          * Used by ItemAnimator when a ViewHolder's position changes
10111          */
10112         static final int FLAG_MOVED = 1 << 11;
10113 
10114         /**
10115          * Used by ItemAnimator when a ViewHolder appears in pre-layout
10116          */
10117         static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
10118 
10119         static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
10120 
10121         /**
10122          * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
10123          * hidden list (as if it was scrap) without being recycled in between.
10124          *
10125          * When a ViewHolder is hidden, there are 2 paths it can be re-used:
10126          *   a) Animation ends, view is recycled and used from the recycle pool.
10127          *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
10128          *
10129          * This flag is used to represent "case b" where the ViewHolder is reused without being
10130          * recycled (thus "bounced" from the hidden list). This state requires special handling
10131          * because the ViewHolder must be added to pre layout maps for animations as if it was
10132          * already there.
10133          */
10134         static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
10135 
10136         private int mFlags;
10137 
10138         private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
10139 
10140         List<Object> mPayloads = null;
10141         List<Object> mUnmodifiedPayloads = null;
10142 
10143         private int mIsRecyclableCount = 0;
10144 
10145         // If non-null, view is currently considered scrap and may be reused for other data by the
10146         // scrap container.
10147         private Recycler mScrapContainer = null;
10148         // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
10149         private boolean mInChangeScrap = false;
10150 
10151         // Saves isImportantForAccessibility value for the view item while it's in hidden state and
10152         // marked as unimportant for accessibility.
10153         private int mWasImportantForAccessibilityBeforeHidden =
10154                 View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10155         // set if we defer the accessibility state change of the view holder
10156         @VisibleForTesting
10157         int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
10158 
10159         /**
10160          * Is set when VH is bound from the adapter and cleaned right before it is sent to
10161          * {@link RecycledViewPool}.
10162          */
10163         RecyclerView mOwnerRecyclerView;
10164 
ViewHolder(View itemView)10165         public ViewHolder(View itemView) {
10166             if (itemView == null) {
10167                 throw new IllegalArgumentException("itemView may not be null");
10168             }
10169             this.itemView = itemView;
10170         }
10171 
flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout)10172         void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
10173             addFlags(ViewHolder.FLAG_REMOVED);
10174             offsetPosition(offset, applyToPreLayout);
10175             mPosition = mNewPosition;
10176         }
10177 
offsetPosition(int offset, boolean applyToPreLayout)10178         void offsetPosition(int offset, boolean applyToPreLayout) {
10179             if (mOldPosition == NO_POSITION) {
10180                 mOldPosition = mPosition;
10181             }
10182             if (mPreLayoutPosition == NO_POSITION) {
10183                 mPreLayoutPosition = mPosition;
10184             }
10185             if (applyToPreLayout) {
10186                 mPreLayoutPosition += offset;
10187             }
10188             mPosition += offset;
10189             if (itemView.getLayoutParams() != null) {
10190                 ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
10191             }
10192         }
10193 
clearOldPosition()10194         void clearOldPosition() {
10195             mOldPosition = NO_POSITION;
10196             mPreLayoutPosition = NO_POSITION;
10197         }
10198 
saveOldPosition()10199         void saveOldPosition() {
10200             if (mOldPosition == NO_POSITION) {
10201                 mOldPosition = mPosition;
10202             }
10203         }
10204 
shouldIgnore()10205         boolean shouldIgnore() {
10206             return (mFlags & FLAG_IGNORE) != 0;
10207         }
10208 
10209         /**
10210          * @deprecated This method is deprecated because its meaning is ambiguous due to the async
10211          * handling of adapter updates. Please use {@link #getLayoutPosition()} or
10212          * {@link #getAdapterPosition()} depending on your use case.
10213          *
10214          * @see #getLayoutPosition()
10215          * @see #getAdapterPosition()
10216          */
10217         @Deprecated
getPosition()10218         public final int getPosition() {
10219             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10220         }
10221 
10222         /**
10223          * Returns the position of the ViewHolder in terms of the latest layout pass.
10224          * <p>
10225          * This position is mostly used by RecyclerView components to be consistent while
10226          * RecyclerView lazily processes adapter updates.
10227          * <p>
10228          * For performance and animation reasons, RecyclerView batches all adapter updates until the
10229          * next layout pass. This may cause mismatches between the Adapter position of the item and
10230          * the position it had in the latest layout calculations.
10231          * <p>
10232          * LayoutManagers should always call this method while doing calculations based on item
10233          * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
10234          * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
10235          * of the item.
10236          * <p>
10237          * If LayoutManager needs to call an external method that requires the adapter position of
10238          * the item, it can use {@link #getAdapterPosition()} or
10239          * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
10240          *
10241          * @return Returns the adapter position of the ViewHolder in the latest layout pass.
10242          * @see #getAdapterPosition()
10243          */
getLayoutPosition()10244         public final int getLayoutPosition() {
10245             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10246         }
10247 
10248         /**
10249          * Returns the Adapter position of the item represented by this ViewHolder.
10250          * <p>
10251          * Note that this might be different than the {@link #getLayoutPosition()} if there are
10252          * pending adapter updates but a new layout pass has not happened yet.
10253          * <p>
10254          * RecyclerView does not handle any adapter updates until the next layout traversal. This
10255          * may create temporary inconsistencies between what user sees on the screen and what
10256          * adapter contents have. This inconsistency is not important since it will be less than
10257          * 16ms but it might be a problem if you want to use ViewHolder position to access the
10258          * adapter. Sometimes, you may need to get the exact adapter position to do
10259          * some actions in response to user events. In that case, you should use this method which
10260          * will calculate the Adapter position of the ViewHolder.
10261          * <p>
10262          * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
10263          * next layout pass, the return value of this method will be {@link #NO_POSITION}.
10264          *
10265          * @return The adapter position of the item if it still exists in the adapter.
10266          * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
10267          * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
10268          * layout pass or the ViewHolder has already been recycled.
10269          */
getAdapterPosition()10270         public final int getAdapterPosition() {
10271             if (mOwnerRecyclerView == null) {
10272                 return NO_POSITION;
10273             }
10274             return mOwnerRecyclerView.getAdapterPositionFor(this);
10275         }
10276 
10277         /**
10278          * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
10279          * to perform animations.
10280          * <p>
10281          * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
10282          * adapter index in the previous layout.
10283          *
10284          * @return The previous adapter index of the Item represented by this ViewHolder or
10285          * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
10286          * complete).
10287          */
getOldPosition()10288         public final int getOldPosition() {
10289             return mOldPosition;
10290         }
10291 
10292         /**
10293          * Returns The itemId represented by this ViewHolder.
10294          *
10295          * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
10296          * otherwise
10297          */
getItemId()10298         public final long getItemId() {
10299             return mItemId;
10300         }
10301 
10302         /**
10303          * @return The view type of this ViewHolder.
10304          */
getItemViewType()10305         public final int getItemViewType() {
10306             return mItemViewType;
10307         }
10308 
isScrap()10309         boolean isScrap() {
10310             return mScrapContainer != null;
10311         }
10312 
unScrap()10313         void unScrap() {
10314             mScrapContainer.unscrapView(this);
10315         }
10316 
wasReturnedFromScrap()10317         boolean wasReturnedFromScrap() {
10318             return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
10319         }
10320 
clearReturnedFromScrapFlag()10321         void clearReturnedFromScrapFlag() {
10322             mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
10323         }
10324 
clearTmpDetachFlag()10325         void clearTmpDetachFlag() {
10326             mFlags = mFlags & ~FLAG_TMP_DETACHED;
10327         }
10328 
stopIgnoring()10329         void stopIgnoring() {
10330             mFlags = mFlags & ~FLAG_IGNORE;
10331         }
10332 
setScrapContainer(Recycler recycler, boolean isChangeScrap)10333         void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
10334             mScrapContainer = recycler;
10335             mInChangeScrap = isChangeScrap;
10336         }
10337 
isInvalid()10338         boolean isInvalid() {
10339             return (mFlags & FLAG_INVALID) != 0;
10340         }
10341 
needsUpdate()10342         boolean needsUpdate() {
10343             return (mFlags & FLAG_UPDATE) != 0;
10344         }
10345 
isBound()10346         boolean isBound() {
10347             return (mFlags & FLAG_BOUND) != 0;
10348         }
10349 
isRemoved()10350         boolean isRemoved() {
10351             return (mFlags & FLAG_REMOVED) != 0;
10352         }
10353 
hasAnyOfTheFlags(int flags)10354         boolean hasAnyOfTheFlags(int flags) {
10355             return (mFlags & flags) != 0;
10356         }
10357 
isTmpDetached()10358         boolean isTmpDetached() {
10359             return (mFlags & FLAG_TMP_DETACHED) != 0;
10360         }
10361 
isAdapterPositionUnknown()10362         boolean isAdapterPositionUnknown() {
10363             return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
10364         }
10365 
setFlags(int flags, int mask)10366         void setFlags(int flags, int mask) {
10367             mFlags = (mFlags & ~mask) | (flags & mask);
10368         }
10369 
addFlags(int flags)10370         void addFlags(int flags) {
10371             mFlags |= flags;
10372         }
10373 
addChangePayload(Object payload)10374         void addChangePayload(Object payload) {
10375             if (payload == null) {
10376                 addFlags(FLAG_ADAPTER_FULLUPDATE);
10377             } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10378                 createPayloadsIfNeeded();
10379                 mPayloads.add(payload);
10380             }
10381         }
10382 
createPayloadsIfNeeded()10383         private void createPayloadsIfNeeded() {
10384             if (mPayloads == null) {
10385                 mPayloads = new ArrayList<Object>();
10386                 mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
10387             }
10388         }
10389 
clearPayload()10390         void clearPayload() {
10391             if (mPayloads != null) {
10392                 mPayloads.clear();
10393             }
10394             mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
10395         }
10396 
getUnmodifiedPayloads()10397         List<Object> getUnmodifiedPayloads() {
10398             if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10399                 if (mPayloads == null || mPayloads.size() == 0) {
10400                     // Initial state,  no update being called.
10401                     return FULLUPDATE_PAYLOADS;
10402                 }
10403                 // there are none-null payloads
10404                 return mUnmodifiedPayloads;
10405             } else {
10406                 // a full update has been called.
10407                 return FULLUPDATE_PAYLOADS;
10408             }
10409         }
10410 
resetInternal()10411         void resetInternal() {
10412             mFlags = 0;
10413             mPosition = NO_POSITION;
10414             mOldPosition = NO_POSITION;
10415             mItemId = NO_ID;
10416             mPreLayoutPosition = NO_POSITION;
10417             mIsRecyclableCount = 0;
10418             mShadowedHolder = null;
10419             mShadowingHolder = null;
10420             clearPayload();
10421             mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10422             mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
10423             clearNestedRecyclerViewIfNotNested(this);
10424         }
10425 
10426         /**
10427          * Called when the child view enters the hidden state
10428          */
onEnteredHiddenState(RecyclerView parent)10429         private void onEnteredHiddenState(RecyclerView parent) {
10430             // While the view item is in hidden state, make it invisible for the accessibility.
10431             mWasImportantForAccessibilityBeforeHidden =
10432                     itemView.getImportantForAccessibility();
10433             parent.setChildImportantForAccessibilityInternal(this,
10434                     View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
10435         }
10436 
10437         /**
10438          * Called when the child view leaves the hidden state
10439          */
onLeftHiddenState(RecyclerView parent)10440         private void onLeftHiddenState(RecyclerView parent) {
10441             parent.setChildImportantForAccessibilityInternal(this,
10442                     mWasImportantForAccessibilityBeforeHidden);
10443             mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10444         }
10445 
10446         @Override
toString()10447         public String toString() {
10448             final StringBuilder sb = new StringBuilder("ViewHolder{"
10449                     + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
10450                     + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
10451             if (isScrap()) {
10452                 sb.append(" scrap ")
10453                         .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
10454             }
10455             if (isInvalid()) sb.append(" invalid");
10456             if (!isBound()) sb.append(" unbound");
10457             if (needsUpdate()) sb.append(" update");
10458             if (isRemoved()) sb.append(" removed");
10459             if (shouldIgnore()) sb.append(" ignored");
10460             if (isTmpDetached()) sb.append(" tmpDetached");
10461             if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
10462             if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
10463 
10464             if (itemView.getParent() == null) sb.append(" no parent");
10465             sb.append("}");
10466             return sb.toString();
10467         }
10468 
10469         /**
10470          * Informs the recycler whether this item can be recycled. Views which are not
10471          * recyclable will not be reused for other items until setIsRecyclable() is
10472          * later set to true. Calls to setIsRecyclable() should always be paired (one
10473          * call to setIsRecyclabe(false) should always be matched with a later call to
10474          * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
10475          * reference-counted.
10476          *
10477          * @param recyclable Whether this item is available to be recycled. Default value
10478          * is true.
10479          *
10480          * @see #isRecyclable()
10481          */
setIsRecyclable(boolean recyclable)10482         public final void setIsRecyclable(boolean recyclable) {
10483             mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
10484             if (mIsRecyclableCount < 0) {
10485                 mIsRecyclableCount = 0;
10486                 if (DEBUG) {
10487                     throw new RuntimeException("isRecyclable decremented below 0: "
10488                             + "unmatched pair of setIsRecyable() calls for " + this);
10489                 }
10490                 Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: "
10491                         + "unmatched pair of setIsRecyable() calls for " + this);
10492             } else if (!recyclable && mIsRecyclableCount == 1) {
10493                 mFlags |= FLAG_NOT_RECYCLABLE;
10494             } else if (recyclable && mIsRecyclableCount == 0) {
10495                 mFlags &= ~FLAG_NOT_RECYCLABLE;
10496             }
10497             if (DEBUG) {
10498                 Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
10499             }
10500         }
10501 
10502         /**
10503          * @return true if this item is available to be recycled, false otherwise.
10504          *
10505          * @see #setIsRecyclable(boolean)
10506          */
isRecyclable()10507         public final boolean isRecyclable() {
10508             return (mFlags & FLAG_NOT_RECYCLABLE) == 0
10509                     && !itemView.hasTransientState();
10510         }
10511 
10512         /**
10513          * Returns whether we have animations referring to this view holder or not.
10514          * This is similar to isRecyclable flag but does not check transient state.
10515          */
shouldBeKeptAsChild()10516         private boolean shouldBeKeptAsChild() {
10517             return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
10518         }
10519 
10520         /**
10521          * @return True if ViewHolder is not referenced by RecyclerView animations but has
10522          * transient state which will prevent it from being recycled.
10523          */
doesTransientStatePreventRecycling()10524         private boolean doesTransientStatePreventRecycling() {
10525             return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && itemView.hasTransientState();
10526         }
10527 
isUpdated()10528         boolean isUpdated() {
10529             return (mFlags & FLAG_UPDATE) != 0;
10530         }
10531     }
10532 
10533     /**
10534      * This method is here so that we can control the important for a11y changes and test it.
10535      */
10536     @VisibleForTesting
setChildImportantForAccessibilityInternal(ViewHolder viewHolder, int importantForAccessibility)10537     boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
10538             int importantForAccessibility) {
10539         if (isComputingLayout()) {
10540             viewHolder.mPendingAccessibilityState = importantForAccessibility;
10541             mPendingAccessibilityImportanceChange.add(viewHolder);
10542             return false;
10543         }
10544         viewHolder.itemView.setImportantForAccessibility(importantForAccessibility);
10545         return true;
10546     }
10547 
dispatchPendingImportantForAccessibilityChanges()10548     void dispatchPendingImportantForAccessibilityChanges() {
10549         for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
10550             ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
10551             if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
10552                 continue;
10553             }
10554             int state = viewHolder.mPendingAccessibilityState;
10555             if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
10556                 //noinspection WrongConstant
10557                 viewHolder.itemView.setImportantForAccessibility(state);
10558                 viewHolder.mPendingAccessibilityState =
10559                         ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
10560             }
10561         }
10562         mPendingAccessibilityImportanceChange.clear();
10563     }
10564 
getAdapterPositionFor(ViewHolder viewHolder)10565     int getAdapterPositionFor(ViewHolder viewHolder) {
10566         if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
10567                 | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
10568                 || !viewHolder.isBound()) {
10569             return RecyclerView.NO_POSITION;
10570         }
10571         return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
10572     }
10573 
10574     /**
10575      * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
10576      * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
10577      * to create their own subclass of this <code>LayoutParams</code> class
10578      * to store any additional required per-child view metadata about the layout.
10579      */
10580     public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
10581         ViewHolder mViewHolder;
10582         final Rect mDecorInsets = new Rect();
10583         boolean mInsetsDirty = true;
10584         // Flag is set to true if the view is bound while it is detached from RV.
10585         // In this case, we need to manually call invalidate after view is added to guarantee that
10586         // invalidation is populated through the View hierarchy
10587         boolean mPendingInvalidate = false;
10588 
LayoutParams(Context c, AttributeSet attrs)10589         public LayoutParams(Context c, AttributeSet attrs) {
10590             super(c, attrs);
10591         }
10592 
LayoutParams(int width, int height)10593         public LayoutParams(int width, int height) {
10594             super(width, height);
10595         }
10596 
LayoutParams(MarginLayoutParams source)10597         public LayoutParams(MarginLayoutParams source) {
10598             super(source);
10599         }
10600 
LayoutParams(ViewGroup.LayoutParams source)10601         public LayoutParams(ViewGroup.LayoutParams source) {
10602             super(source);
10603         }
10604 
LayoutParams(LayoutParams source)10605         public LayoutParams(LayoutParams source) {
10606             super((ViewGroup.LayoutParams) source);
10607         }
10608 
10609         /**
10610          * Returns true if the view this LayoutParams is attached to needs to have its content
10611          * updated from the corresponding adapter.
10612          *
10613          * @return true if the view should have its content updated
10614          */
viewNeedsUpdate()10615         public boolean viewNeedsUpdate() {
10616             return mViewHolder.needsUpdate();
10617         }
10618 
10619         /**
10620          * Returns true if the view this LayoutParams is attached to is now representing
10621          * potentially invalid data. A LayoutManager should scrap/recycle it.
10622          *
10623          * @return true if the view is invalid
10624          */
isViewInvalid()10625         public boolean isViewInvalid() {
10626             return mViewHolder.isInvalid();
10627         }
10628 
10629         /**
10630          * Returns true if the adapter data item corresponding to the view this LayoutParams
10631          * is attached to has been removed from the data set. A LayoutManager may choose to
10632          * treat it differently in order to animate its outgoing or disappearing state.
10633          *
10634          * @return true if the item the view corresponds to was removed from the data set
10635          */
isItemRemoved()10636         public boolean isItemRemoved() {
10637             return mViewHolder.isRemoved();
10638         }
10639 
10640         /**
10641          * Returns true if the adapter data item corresponding to the view this LayoutParams
10642          * is attached to has been changed in the data set. A LayoutManager may choose to
10643          * treat it differently in order to animate its changing state.
10644          *
10645          * @return true if the item the view corresponds to was changed in the data set
10646          */
isItemChanged()10647         public boolean isItemChanged() {
10648             return mViewHolder.isUpdated();
10649         }
10650 
10651         /**
10652          * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
10653          */
10654         @Deprecated
getViewPosition()10655         public int getViewPosition() {
10656             return mViewHolder.getPosition();
10657         }
10658 
10659         /**
10660          * Returns the adapter position that the view this LayoutParams is attached to corresponds
10661          * to as of latest layout calculation.
10662          *
10663          * @return the adapter position this view as of latest layout pass
10664          */
getViewLayoutPosition()10665         public int getViewLayoutPosition() {
10666             return mViewHolder.getLayoutPosition();
10667         }
10668 
10669         /**
10670          * Returns the up-to-date adapter position that the view this LayoutParams is attached to
10671          * corresponds to.
10672          *
10673          * @return the up-to-date adapter position this view. It may return
10674          * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
10675          * its up-to-date position cannot be calculated.
10676          */
getViewAdapterPosition()10677         public int getViewAdapterPosition() {
10678             return mViewHolder.getAdapterPosition();
10679         }
10680     }
10681 
10682     /**
10683      * Observer base class for watching changes to an {@link Adapter}.
10684      * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
10685      */
10686     public abstract static class AdapterDataObserver {
onChanged()10687         public void onChanged() {
10688             // Do nothing
10689         }
10690 
onItemRangeChanged(int positionStart, int itemCount)10691         public void onItemRangeChanged(int positionStart, int itemCount) {
10692             // do nothing
10693         }
10694 
onItemRangeChanged(int positionStart, int itemCount, Object payload)10695         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
10696             // fallback to onItemRangeChanged(positionStart, itemCount) if app
10697             // does not override this method.
10698             onItemRangeChanged(positionStart, itemCount);
10699         }
10700 
onItemRangeInserted(int positionStart, int itemCount)10701         public void onItemRangeInserted(int positionStart, int itemCount) {
10702             // do nothing
10703         }
10704 
onItemRangeRemoved(int positionStart, int itemCount)10705         public void onItemRangeRemoved(int positionStart, int itemCount) {
10706             // do nothing
10707         }
10708 
onItemRangeMoved(int fromPosition, int toPosition, int itemCount)10709         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
10710             // do nothing
10711         }
10712     }
10713 
10714     /**
10715      * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
10716      * provides methods to trigger a programmatic scroll.</p>
10717      *
10718      * @see LinearSmoothScroller
10719      */
10720     public abstract static class SmoothScroller {
10721 
10722         private int mTargetPosition = RecyclerView.NO_POSITION;
10723 
10724         private RecyclerView mRecyclerView;
10725 
10726         private LayoutManager mLayoutManager;
10727 
10728         private boolean mPendingInitialRun;
10729 
10730         private boolean mRunning;
10731 
10732         private View mTargetView;
10733 
10734         private final Action mRecyclingAction;
10735 
SmoothScroller()10736         public SmoothScroller() {
10737             mRecyclingAction = new Action(0, 0);
10738         }
10739 
10740         /**
10741          * Starts a smooth scroll for the given target position.
10742          * <p>In each animation step, {@link RecyclerView} will check
10743          * for the target view and call either
10744          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
10745          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
10746          * SmoothScroller is stopped.</p>
10747          *
10748          * <p>Note that if RecyclerView finds the target view, it will automatically stop the
10749          * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
10750          * stop calling SmoothScroller in each animation step.</p>
10751          */
start(RecyclerView recyclerView, LayoutManager layoutManager)10752         void start(RecyclerView recyclerView, LayoutManager layoutManager) {
10753             mRecyclerView = recyclerView;
10754             mLayoutManager = layoutManager;
10755             if (mTargetPosition == RecyclerView.NO_POSITION) {
10756                 throw new IllegalArgumentException("Invalid target position");
10757             }
10758             mRecyclerView.mState.mTargetPosition = mTargetPosition;
10759             mRunning = true;
10760             mPendingInitialRun = true;
10761             mTargetView = findViewByPosition(getTargetPosition());
10762             onStart();
10763             mRecyclerView.mViewFlinger.postOnAnimation();
10764         }
10765 
setTargetPosition(int targetPosition)10766         public void setTargetPosition(int targetPosition) {
10767             mTargetPosition = targetPosition;
10768         }
10769 
10770         /**
10771          * @return The LayoutManager to which this SmoothScroller is attached. Will return
10772          * <code>null</code> after the SmoothScroller is stopped.
10773          */
10774         @Nullable
getLayoutManager()10775         public LayoutManager getLayoutManager() {
10776             return mLayoutManager;
10777         }
10778 
10779         /**
10780          * Stops running the SmoothScroller in each animation callback. Note that this does not
10781          * cancel any existing {@link Action} updated by
10782          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
10783          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
10784          */
stop()10785         protected final void stop() {
10786             if (!mRunning) {
10787                 return;
10788             }
10789             onStop();
10790             mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
10791             mTargetView = null;
10792             mTargetPosition = RecyclerView.NO_POSITION;
10793             mPendingInitialRun = false;
10794             mRunning = false;
10795             // trigger a cleanup
10796             mLayoutManager.onSmoothScrollerStopped(this);
10797             // clear references to avoid any potential leak by a custom smooth scroller
10798             mLayoutManager = null;
10799             mRecyclerView = null;
10800         }
10801 
10802         /**
10803          * Returns true if SmoothScroller has been started but has not received the first
10804          * animation
10805          * callback yet.
10806          *
10807          * @return True if this SmoothScroller is waiting to start
10808          */
isPendingInitialRun()10809         public boolean isPendingInitialRun() {
10810             return mPendingInitialRun;
10811         }
10812 
10813 
10814         /**
10815          * @return True if SmoothScroller is currently active
10816          */
isRunning()10817         public boolean isRunning() {
10818             return mRunning;
10819         }
10820 
10821         /**
10822          * Returns the adapter position of the target item
10823          *
10824          * @return Adapter position of the target item or
10825          * {@link RecyclerView#NO_POSITION} if no target view is set.
10826          */
getTargetPosition()10827         public int getTargetPosition() {
10828             return mTargetPosition;
10829         }
10830 
onAnimation(int dx, int dy)10831         private void onAnimation(int dx, int dy) {
10832             final RecyclerView recyclerView = mRecyclerView;
10833             if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
10834                 stop();
10835             }
10836             mPendingInitialRun = false;
10837             if (mTargetView != null) {
10838                 // verify target position
10839                 if (getChildPosition(mTargetView) == mTargetPosition) {
10840                     onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
10841                     mRecyclingAction.runIfNecessary(recyclerView);
10842                     stop();
10843                 } else {
10844                     Log.e(TAG, "Passed over target position while smooth scrolling.");
10845                     mTargetView = null;
10846                 }
10847             }
10848             if (mRunning) {
10849                 onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
10850                 boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
10851                 mRecyclingAction.runIfNecessary(recyclerView);
10852                 if (hadJumpTarget) {
10853                     // It is not stopped so needs to be restarted
10854                     if (mRunning) {
10855                         mPendingInitialRun = true;
10856                         recyclerView.mViewFlinger.postOnAnimation();
10857                     } else {
10858                         stop(); // done
10859                     }
10860                 }
10861             }
10862         }
10863 
10864         /**
10865          * @see RecyclerView#getChildLayoutPosition(android.view.View)
10866          */
getChildPosition(View view)10867         public int getChildPosition(View view) {
10868             return mRecyclerView.getChildLayoutPosition(view);
10869         }
10870 
10871         /**
10872          * @see RecyclerView.LayoutManager#getChildCount()
10873          */
getChildCount()10874         public int getChildCount() {
10875             return mRecyclerView.mLayout.getChildCount();
10876         }
10877 
10878         /**
10879          * @see RecyclerView.LayoutManager#findViewByPosition(int)
10880          */
findViewByPosition(int position)10881         public View findViewByPosition(int position) {
10882             return mRecyclerView.mLayout.findViewByPosition(position);
10883         }
10884 
10885         /**
10886          * @see RecyclerView#scrollToPosition(int)
10887          * @deprecated Use {@link Action#jumpTo(int)}.
10888          */
10889         @Deprecated
instantScrollToPosition(int position)10890         public void instantScrollToPosition(int position) {
10891             mRecyclerView.scrollToPosition(position);
10892         }
10893 
onChildAttachedToWindow(View child)10894         protected void onChildAttachedToWindow(View child) {
10895             if (getChildPosition(child) == getTargetPosition()) {
10896                 mTargetView = child;
10897                 if (DEBUG) {
10898                     Log.d(TAG, "smooth scroll target view has been attached");
10899                 }
10900             }
10901         }
10902 
10903         /**
10904          * Normalizes the vector.
10905          * @param scrollVector The vector that points to the target scroll position
10906          */
normalize(PointF scrollVector)10907         protected void normalize(PointF scrollVector) {
10908             final double magnitude = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y
10909                     * scrollVector.y);
10910             scrollVector.x /= magnitude;
10911             scrollVector.y /= magnitude;
10912         }
10913 
10914         /**
10915          * Called when smooth scroll is started. This might be a good time to do setup.
10916          */
10917         protected abstract void onStart();
10918 
10919         /**
10920          * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
10921          * @see #stop()
10922          */
10923         protected abstract void onStop();
10924 
10925         /**
10926          * <p>RecyclerView will call this method each time it scrolls until it can find the target
10927          * position in the layout.</p>
10928          * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
10929          * provided {@link Action} to define the next scroll.</p>
10930          *
10931          * @param dx        Last scroll amount horizontally
10932          * @param dy        Last scroll amount vertically
10933          * @param state     Transient state of RecyclerView
10934          * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
10935          *                  update this object.
10936          */
10937         protected abstract void onSeekTargetStep(int dx, int dy, State state, Action action);
10938 
10939         /**
10940          * Called when the target position is laid out. This is the last callback SmoothScroller
10941          * will receive and it should update the provided {@link Action} to define the scroll
10942          * details towards the target view.
10943          * @param targetView    The view element which render the target position.
10944          * @param state         Transient state of RecyclerView
10945          * @param action        Action instance that you should update to define final scroll action
10946          *                      towards the targetView
10947          */
10948         protected abstract void onTargetFound(View targetView, State state, Action action);
10949 
10950         /**
10951          * Holds information about a smooth scroll request by a {@link SmoothScroller}.
10952          */
10953         public static class Action {
10954 
10955             public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
10956 
10957             private int mDx;
10958 
10959             private int mDy;
10960 
10961             private int mDuration;
10962 
10963             private int mJumpToPosition = NO_POSITION;
10964 
10965             private Interpolator mInterpolator;
10966 
10967             private boolean mChanged = false;
10968 
10969             // we track this variable to inform custom implementer if they are updating the action
10970             // in every animation callback
10971             private int mConsecutiveUpdates = 0;
10972 
10973             /**
10974              * @param dx Pixels to scroll horizontally
10975              * @param dy Pixels to scroll vertically
10976              */
Action(int dx, int dy)10977             public Action(int dx, int dy) {
10978                 this(dx, dy, UNDEFINED_DURATION, null);
10979             }
10980 
10981             /**
10982              * @param dx       Pixels to scroll horizontally
10983              * @param dy       Pixels to scroll vertically
10984              * @param duration Duration of the animation in milliseconds
10985              */
Action(int dx, int dy, int duration)10986             public Action(int dx, int dy, int duration) {
10987                 this(dx, dy, duration, null);
10988             }
10989 
10990             /**
10991              * @param dx           Pixels to scroll horizontally
10992              * @param dy           Pixels to scroll vertically
10993              * @param duration     Duration of the animation in milliseconds
10994              * @param interpolator Interpolator to be used when calculating scroll position in each
10995              *                     animation step
10996              */
Action(int dx, int dy, int duration, Interpolator interpolator)10997             public Action(int dx, int dy, int duration, Interpolator interpolator) {
10998                 mDx = dx;
10999                 mDy = dy;
11000                 mDuration = duration;
11001                 mInterpolator = interpolator;
11002             }
11003 
11004             /**
11005              * Instead of specifying pixels to scroll, use the target position to jump using
11006              * {@link RecyclerView#scrollToPosition(int)}.
11007              * <p>
11008              * You may prefer using this method if scroll target is really far away and you prefer
11009              * to jump to a location and smooth scroll afterwards.
11010              * <p>
11011              * Note that calling this method takes priority over other update methods such as
11012              * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
11013              * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
11014              * {@link #jumpTo(int)}, the other changes will not be considered for this animation
11015              * frame.
11016              *
11017              * @param targetPosition The target item position to scroll to using instant scrolling.
11018              */
jumpTo(int targetPosition)11019             public void jumpTo(int targetPosition) {
11020                 mJumpToPosition = targetPosition;
11021             }
11022 
hasJumpTarget()11023             boolean hasJumpTarget() {
11024                 return mJumpToPosition >= 0;
11025             }
11026 
runIfNecessary(RecyclerView recyclerView)11027             void runIfNecessary(RecyclerView recyclerView) {
11028                 if (mJumpToPosition >= 0) {
11029                     final int position = mJumpToPosition;
11030                     mJumpToPosition = NO_POSITION;
11031                     recyclerView.jumpToPositionForSmoothScroller(position);
11032                     mChanged = false;
11033                     return;
11034                 }
11035                 if (mChanged) {
11036                     validate();
11037                     if (mInterpolator == null) {
11038                         if (mDuration == UNDEFINED_DURATION) {
11039                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
11040                         } else {
11041                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
11042                         }
11043                     } else {
11044                         recyclerView.mViewFlinger.smoothScrollBy(
11045                                 mDx, mDy, mDuration, mInterpolator);
11046                     }
11047                     mConsecutiveUpdates++;
11048                     if (mConsecutiveUpdates > 10) {
11049                         // A new action is being set in every animation step. This looks like a bad
11050                         // implementation. Inform developer.
11051                         Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
11052                                 + " you are not changing it unless necessary");
11053                     }
11054                     mChanged = false;
11055                 } else {
11056                     mConsecutiveUpdates = 0;
11057                 }
11058             }
11059 
validate()11060             private void validate() {
11061                 if (mInterpolator != null && mDuration < 1) {
11062                     throw new IllegalStateException("If you provide an interpolator, you must"
11063                             + " set a positive duration");
11064                 } else if (mDuration < 1) {
11065                     throw new IllegalStateException("Scroll duration must be a positive number");
11066                 }
11067             }
11068 
getDx()11069             public int getDx() {
11070                 return mDx;
11071             }
11072 
setDx(int dx)11073             public void setDx(int dx) {
11074                 mChanged = true;
11075                 mDx = dx;
11076             }
11077 
getDy()11078             public int getDy() {
11079                 return mDy;
11080             }
11081 
setDy(int dy)11082             public void setDy(int dy) {
11083                 mChanged = true;
11084                 mDy = dy;
11085             }
11086 
getDuration()11087             public int getDuration() {
11088                 return mDuration;
11089             }
11090 
setDuration(int duration)11091             public void setDuration(int duration) {
11092                 mChanged = true;
11093                 mDuration = duration;
11094             }
11095 
getInterpolator()11096             public Interpolator getInterpolator() {
11097                 return mInterpolator;
11098             }
11099 
11100             /**
11101              * Sets the interpolator to calculate scroll steps
11102              * @param interpolator The interpolator to use. If you specify an interpolator, you must
11103              *                     also set the duration.
11104              * @see #setDuration(int)
11105              */
setInterpolator(Interpolator interpolator)11106             public void setInterpolator(Interpolator interpolator) {
11107                 mChanged = true;
11108                 mInterpolator = interpolator;
11109             }
11110 
11111             /**
11112              * Updates the action with given parameters.
11113              * @param dx Pixels to scroll horizontally
11114              * @param dy Pixels to scroll vertically
11115              * @param duration Duration of the animation in milliseconds
11116              * @param interpolator Interpolator to be used when calculating scroll position in each
11117              *                     animation step
11118              */
update(int dx, int dy, int duration, Interpolator interpolator)11119             public void update(int dx, int dy, int duration, Interpolator interpolator) {
11120                 mDx = dx;
11121                 mDy = dy;
11122                 mDuration = duration;
11123                 mInterpolator = interpolator;
11124                 mChanged = true;
11125             }
11126         }
11127 
11128         /**
11129          * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
11130          * to provide a hint to a {@link SmoothScroller} about the location of the target position.
11131          */
11132         public interface ScrollVectorProvider {
11133             /**
11134              * Should calculate the vector that points to the direction where the target position
11135              * can be found.
11136              * <p>
11137              * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
11138              * the target position.
11139              * <p>
11140              * The magnitude of the vector is not important. It is always normalized before being
11141              * used by the {@link LinearSmoothScroller}.
11142              * <p>
11143              * LayoutManager should not check whether the position exists in the adapter or not.
11144              *
11145              * @param targetPosition the target position to which the returned vector should point
11146              *
11147              * @return the scroll vector for a given position.
11148              */
11149             PointF computeScrollVectorForPosition(int targetPosition);
11150         }
11151     }
11152 
11153     static class AdapterDataObservable extends Observable<AdapterDataObserver> {
hasObservers()11154         public boolean hasObservers() {
11155             return !mObservers.isEmpty();
11156         }
11157 
notifyChanged()11158         public void notifyChanged() {
11159             // since onChanged() is implemented by the app, it could do anything, including
11160             // removing itself from {@link mObservers} - and that could cause problems if
11161             // an iterator is used on the ArrayList {@link mObservers}.
11162             // to avoid such problems, just march thru the list in the reverse order.
11163             for (int i = mObservers.size() - 1; i >= 0; i--) {
11164                 mObservers.get(i).onChanged();
11165             }
11166         }
11167 
notifyItemRangeChanged(int positionStart, int itemCount)11168         public void notifyItemRangeChanged(int positionStart, int itemCount) {
11169             notifyItemRangeChanged(positionStart, itemCount, null);
11170         }
11171 
notifyItemRangeChanged(int positionStart, int itemCount, Object payload)11172         public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
11173             // since onItemRangeChanged() is implemented by the app, it could do anything, including
11174             // removing itself from {@link mObservers} - and that could cause problems if
11175             // an iterator is used on the ArrayList {@link mObservers}.
11176             // to avoid such problems, just march thru the list in the reverse order.
11177             for (int i = mObservers.size() - 1; i >= 0; i--) {
11178                 mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
11179             }
11180         }
11181 
notifyItemRangeInserted(int positionStart, int itemCount)11182         public void notifyItemRangeInserted(int positionStart, int itemCount) {
11183             // since onItemRangeInserted() is implemented by the app, it could do anything,
11184             // including removing itself from {@link mObservers} - and that could cause problems if
11185             // an iterator is used on the ArrayList {@link mObservers}.
11186             // to avoid such problems, just march thru the list in the reverse order.
11187             for (int i = mObservers.size() - 1; i >= 0; i--) {
11188                 mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
11189             }
11190         }
11191 
notifyItemRangeRemoved(int positionStart, int itemCount)11192         public void notifyItemRangeRemoved(int positionStart, int itemCount) {
11193             // since onItemRangeRemoved() is implemented by the app, it could do anything, including
11194             // removing itself from {@link mObservers} - and that could cause problems if
11195             // an iterator is used on the ArrayList {@link mObservers}.
11196             // to avoid such problems, just march thru the list in the reverse order.
11197             for (int i = mObservers.size() - 1; i >= 0; i--) {
11198                 mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
11199             }
11200         }
11201 
notifyItemMoved(int fromPosition, int toPosition)11202         public void notifyItemMoved(int fromPosition, int toPosition) {
11203             for (int i = mObservers.size() - 1; i >= 0; i--) {
11204                 mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
11205             }
11206         }
11207     }
11208 
11209     /**
11210      * This is public so that the CREATOR can be access on cold launch.
11211      * @hide
11212      */
11213     public static class SavedState extends AbsSavedState {
11214 
11215         Parcelable mLayoutState;
11216 
11217         /**
11218          * called by CREATOR
11219          */
SavedState(Parcel in)11220         SavedState(Parcel in) {
11221             super(in);
11222             mLayoutState = in.readParcelable(LayoutManager.class.getClassLoader());
11223         }
11224 
11225         /**
11226          * Called by onSaveInstanceState
11227          */
SavedState(Parcelable superState)11228         SavedState(Parcelable superState) {
11229             super(superState);
11230         }
11231 
11232         @Override
writeToParcel(Parcel dest, int flags)11233         public void writeToParcel(Parcel dest, int flags) {
11234             super.writeToParcel(dest, flags);
11235             dest.writeParcelable(mLayoutState, 0);
11236         }
11237 
copyFrom(SavedState other)11238         void copyFrom(SavedState other) {
11239             mLayoutState = other.mLayoutState;
11240         }
11241 
11242         public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
11243                     @Override
11244                     public SavedState createFromParcel(Parcel in) {
11245                         return new SavedState(in);
11246                     }
11247 
11248                     @Override
11249                     public SavedState[] newArray(int size) {
11250                         return new SavedState[size];
11251                     }
11252                 };
11253     }
11254     /**
11255      * <p>Contains useful information about the current RecyclerView state like target scroll
11256      * position or view focus. State object can also keep arbitrary data, identified by resource
11257      * ids.</p>
11258      * <p>Often times, RecyclerView components will need to pass information between each other.
11259      * To provide a well defined data bus between components, RecyclerView passes the same State
11260      * object to component callbacks and these components can use it to exchange data.</p>
11261      * <p>If you implement custom components, you can use State's put/get/remove methods to pass
11262      * data between your components without needing to manage their lifecycles.</p>
11263      */
11264     public static class State {
11265         static final int STEP_START = 1;
11266         static final int STEP_LAYOUT = 1 << 1;
11267         static final int STEP_ANIMATIONS = 1 << 2;
11268 
assertLayoutStep(int accepted)11269         void assertLayoutStep(int accepted) {
11270             if ((accepted & mLayoutStep) == 0) {
11271                 throw new IllegalStateException("Layout state should be one of "
11272                         + Integer.toBinaryString(accepted) + " but it is "
11273                         + Integer.toBinaryString(mLayoutStep));
11274             }
11275         }
11276 
11277 
11278         /** Owned by SmoothScroller */
11279         private int mTargetPosition = RecyclerView.NO_POSITION;
11280 
11281         private SparseArray<Object> mData;
11282 
11283         ////////////////////////////////////////////////////////////////////////////////////////////
11284         // Fields below are carried from one layout pass to the next
11285         ////////////////////////////////////////////////////////////////////////////////////////////
11286 
11287         /**
11288          * Number of items adapter had in the previous layout.
11289          */
11290         int mPreviousLayoutItemCount = 0;
11291 
11292         /**
11293          * Number of items that were NOT laid out but has been deleted from the adapter after the
11294          * previous layout.
11295          */
11296         int mDeletedInvisibleItemCountSincePreviousLayout = 0;
11297 
11298         ////////////////////////////////////////////////////////////////////////////////////////////
11299         // Fields below must be updated or cleared before they are used (generally before a pass)
11300         ////////////////////////////////////////////////////////////////////////////////////////////
11301 
11302         @IntDef(flag = true, value = {
11303                 STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
11304         })
11305         @Retention(RetentionPolicy.SOURCE)
11306         @interface LayoutState {}
11307 
11308         @LayoutState
11309         int mLayoutStep = STEP_START;
11310 
11311         /**
11312          * Number of items adapter has.
11313          */
11314         int mItemCount = 0;
11315 
11316         boolean mStructureChanged = false;
11317 
11318         boolean mInPreLayout = false;
11319 
11320         boolean mTrackOldChangeHolders = false;
11321 
11322         boolean mIsMeasuring = false;
11323 
11324         ////////////////////////////////////////////////////////////////////////////////////////////
11325         // Fields below are always reset outside of the pass (or passes) that use them
11326         ////////////////////////////////////////////////////////////////////////////////////////////
11327 
11328         boolean mRunSimpleAnimations = false;
11329 
11330         boolean mRunPredictiveAnimations = false;
11331 
11332         /**
11333          * This data is saved before a layout calculation happens. After the layout is finished,
11334          * if the previously focused view has been replaced with another view for the same item, we
11335          * move the focus to the new item automatically.
11336          */
11337         int mFocusedItemPosition;
11338         long mFocusedItemId;
11339         // when a sub child has focus, record its id and see if we can directly request focus on
11340         // that one instead
11341         int mFocusedSubChildId;
11342 
11343         ////////////////////////////////////////////////////////////////////////////////////////////
11344 
reset()11345         State reset() {
11346             mTargetPosition = RecyclerView.NO_POSITION;
11347             if (mData != null) {
11348                 mData.clear();
11349             }
11350             mItemCount = 0;
11351             mStructureChanged = false;
11352             mIsMeasuring = false;
11353             return this;
11354         }
11355 
11356         /**
11357          * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
11358          * prior to any layout passes.
11359          *
11360          * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
11361          * that Recycler#getViewForPosition() can function safely.</p>
11362          */
prepareForNestedPrefetch(Adapter adapter)11363         void prepareForNestedPrefetch(Adapter adapter) {
11364             mLayoutStep = STEP_START;
11365             mItemCount = adapter.getItemCount();
11366             mStructureChanged = false;
11367             mInPreLayout = false;
11368             mTrackOldChangeHolders = false;
11369             mIsMeasuring = false;
11370         }
11371 
11372         /**
11373          * Returns true if the RecyclerView is currently measuring the layout. This value is
11374          * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
11375          * has non-exact measurement specs.
11376          * <p>
11377          * Note that if the LayoutManager supports predictive animations and it is calculating the
11378          * pre-layout step, this value will be {@code false} even if the RecyclerView is in
11379          * {@code onMeasure} call. This is because pre-layout means the previous state of the
11380          * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
11381          * LayoutManager is always guaranteed to receive another call to
11382          * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
11383          *
11384          * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
11385          */
isMeasuring()11386         public boolean isMeasuring() {
11387             return mIsMeasuring;
11388         }
11389 
11390         /**
11391          * Returns true if
11392          * @return
11393          */
isPreLayout()11394         public boolean isPreLayout() {
11395             return mInPreLayout;
11396         }
11397 
11398         /**
11399          * Returns whether RecyclerView will run predictive animations in this layout pass
11400          * or not.
11401          *
11402          * @return true if RecyclerView is calculating predictive animations to be run at the end
11403          *         of the layout pass.
11404          */
willRunPredictiveAnimations()11405         public boolean willRunPredictiveAnimations() {
11406             return mRunPredictiveAnimations;
11407         }
11408 
11409         /**
11410          * Returns whether RecyclerView will run simple animations in this layout pass
11411          * or not.
11412          *
11413          * @return true if RecyclerView is calculating simple animations to be run at the end of
11414          *         the layout pass.
11415          */
willRunSimpleAnimations()11416         public boolean willRunSimpleAnimations() {
11417             return mRunSimpleAnimations;
11418         }
11419 
11420         /**
11421          * Removes the mapping from the specified id, if there was any.
11422          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
11423          *                   preserve cross functionality and avoid conflicts.
11424          */
remove(int resourceId)11425         public void remove(int resourceId) {
11426             if (mData == null) {
11427                 return;
11428             }
11429             mData.remove(resourceId);
11430         }
11431 
11432         /**
11433          * Gets the Object mapped from the specified id, or <code>null</code>
11434          * if no such data exists.
11435          *
11436          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
11437          *                   to
11438          *                   preserve cross functionality and avoid conflicts.
11439          */
get(int resourceId)11440         public <T> T get(int resourceId) {
11441             if (mData == null) {
11442                 return null;
11443             }
11444             return (T) mData.get(resourceId);
11445         }
11446 
11447         /**
11448          * Adds a mapping from the specified id to the specified value, replacing the previous
11449          * mapping from the specified key if there was one.
11450          *
11451          * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
11452          *                   preserve cross functionality and avoid conflicts.
11453          * @param data       The data you want to associate with the resourceId.
11454          */
put(int resourceId, Object data)11455         public void put(int resourceId, Object data) {
11456             if (mData == null) {
11457                 mData = new SparseArray<Object>();
11458             }
11459             mData.put(resourceId, data);
11460         }
11461 
11462         /**
11463          * If scroll is triggered to make a certain item visible, this value will return the
11464          * adapter index of that item.
11465          * @return Adapter index of the target item or
11466          * {@link RecyclerView#NO_POSITION} if there is no target
11467          * position.
11468          */
getTargetScrollPosition()11469         public int getTargetScrollPosition() {
11470             return mTargetPosition;
11471         }
11472 
11473         /**
11474          * Returns if current scroll has a target position.
11475          * @return true if scroll is being triggered to make a certain position visible
11476          * @see #getTargetScrollPosition()
11477          */
hasTargetScrollPosition()11478         public boolean hasTargetScrollPosition() {
11479             return mTargetPosition != RecyclerView.NO_POSITION;
11480         }
11481 
11482         /**
11483          * @return true if the structure of the data set has changed since the last call to
11484          *         onLayoutChildren, false otherwise
11485          */
didStructureChange()11486         public boolean didStructureChange() {
11487             return mStructureChanged;
11488         }
11489 
11490         /**
11491          * Returns the total number of items that can be laid out. Note that this number is not
11492          * necessarily equal to the number of items in the adapter, so you should always use this
11493          * number for your position calculations and never access the adapter directly.
11494          * <p>
11495          * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
11496          * data changes on existing Views. These calculations are used to decide which animations
11497          * should be run.
11498          * <p>
11499          * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
11500          * present the correct state to LayoutManager in pre-layout pass.
11501          * <p>
11502          * For example, a newly added item is not included in pre-layout item count because
11503          * pre-layout reflects the contents of the adapter before the item is added. Behind the
11504          * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
11505          * LayoutManager does not know about the new item's existence in pre-layout. The item will
11506          * be available in second layout pass and will be included in the item count. Similar
11507          * adjustments are made for moved and removed items as well.
11508          * <p>
11509          * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
11510          *
11511          * @return The number of items currently available
11512          * @see LayoutManager#getItemCount()
11513          */
getItemCount()11514         public int getItemCount() {
11515             return mInPreLayout
11516                     ? (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout)
11517                     : mItemCount;
11518         }
11519 
11520         @Override
toString()11521         public String toString() {
11522             return "State{"
11523                     + "mTargetPosition=" + mTargetPosition
11524                     + ", mData=" + mData
11525                     + ", mItemCount=" + mItemCount
11526                     + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
11527                     + ", mDeletedInvisibleItemCountSincePreviousLayout="
11528                     + mDeletedInvisibleItemCountSincePreviousLayout
11529                     + ", mStructureChanged=" + mStructureChanged
11530                     + ", mInPreLayout=" + mInPreLayout
11531                     + ", mRunSimpleAnimations=" + mRunSimpleAnimations
11532                     + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
11533                     + '}';
11534         }
11535     }
11536 
11537     /**
11538      * This class defines the behavior of fling if the developer wishes to handle it.
11539      * <p>
11540      * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
11541      *
11542      * @see #setOnFlingListener(OnFlingListener)
11543      */
11544     public abstract static class OnFlingListener {
11545 
11546         /**
11547          * Override this to handle a fling given the velocities in both x and y directions.
11548          * Note that this method will only be called if the associated {@link LayoutManager}
11549          * supports scrolling and the fling is not handled by nested scrolls first.
11550          *
11551          * @param velocityX the fling velocity on the X axis
11552          * @param velocityY the fling velocity on the Y axis
11553          *
11554          * @return true if the fling washandled, false otherwise.
11555          */
11556         public abstract boolean onFling(int velocityX, int velocityY);
11557     }
11558 
11559     /**
11560      * Internal listener that manages items after animations finish. This is how items are
11561      * retained (not recycled) during animations, but allowed to be recycled afterwards.
11562      * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
11563      * method on the animator's listener when it is done animating any item.
11564      */
11565     private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
11566 
ItemAnimatorRestoreListener()11567         ItemAnimatorRestoreListener() {
11568         }
11569 
11570         @Override
onAnimationFinished(ViewHolder item)11571         public void onAnimationFinished(ViewHolder item) {
11572             item.setIsRecyclable(true);
11573             if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
11574                 item.mShadowedHolder = null;
11575             }
11576             // always null this because an OldViewHolder can never become NewViewHolder w/o being
11577             // recycled.
11578             item.mShadowingHolder = null;
11579             if (!item.shouldBeKeptAsChild()) {
11580                 if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
11581                     removeDetachedView(item.itemView, false);
11582                 }
11583             }
11584         }
11585     }
11586 
11587     /**
11588      * This class defines the animations that take place on items as changes are made
11589      * to the adapter.
11590      *
11591      * Subclasses of ItemAnimator can be used to implement custom animations for actions on
11592      * ViewHolder items. The RecyclerView will manage retaining these items while they
11593      * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
11594      * when a ViewHolder's animation is finished. In other words, there must be a matching
11595      * {@link #dispatchAnimationFinished(ViewHolder)} call for each
11596      * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
11597      * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11598      * animateChange()}
11599      * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
11600      * and
11601      * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11602      * animateDisappearance()} call.
11603      *
11604      * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
11605      *
11606      * @see #setItemAnimator(ItemAnimator)
11607      */
11608     @SuppressWarnings("UnusedParameters")
11609     public abstract static class ItemAnimator {
11610 
11611         /**
11612          * The Item represented by this ViewHolder is updated.
11613          * <p>
11614          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11615          */
11616         public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
11617 
11618         /**
11619          * The Item represented by this ViewHolder is removed from the adapter.
11620          * <p>
11621          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11622          */
11623         public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
11624 
11625         /**
11626          * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
11627          * represented by this ViewHolder is invalid.
11628          * <p>
11629          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11630          */
11631         public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
11632 
11633         /**
11634          * The position of the Item represented by this ViewHolder has been changed. This flag is
11635          * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
11636          * any adapter change that may have a side effect on this item. (e.g. The item before this
11637          * one has been removed from the Adapter).
11638          * <p>
11639          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11640          */
11641         public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
11642 
11643         /**
11644          * This ViewHolder was not laid out but has been added to the layout in pre-layout state
11645          * by the {@link LayoutManager}. This means that the item was already in the Adapter but
11646          * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
11647          * to add new items in pre-layout to specify their virtual location when they are invisible
11648          * (e.g. to specify the item should <i>animate in</i> from below the visible area).
11649          * <p>
11650          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11651          */
11652         public static final int FLAG_APPEARED_IN_PRE_LAYOUT =
11653                 ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
11654 
11655         /**
11656          * The set of flags that might be passed to
11657          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11658          */
11659         @IntDef(flag = true, value = {
11660                 FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
11661                 FLAG_APPEARED_IN_PRE_LAYOUT
11662         })
11663         @Retention(RetentionPolicy.SOURCE)
11664         public @interface AdapterChanges {}
11665         private ItemAnimatorListener mListener = null;
11666         private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
11667                 new ArrayList<ItemAnimatorFinishedListener>();
11668 
11669         private long mAddDuration = 120;
11670         private long mRemoveDuration = 120;
11671         private long mMoveDuration = 250;
11672         private long mChangeDuration = 250;
11673 
11674         /**
11675          * Gets the current duration for which all move animations will run.
11676          *
11677          * @return The current move duration
11678          */
getMoveDuration()11679         public long getMoveDuration() {
11680             return mMoveDuration;
11681         }
11682 
11683         /**
11684          * Sets the duration for which all move animations will run.
11685          *
11686          * @param moveDuration The move duration
11687          */
setMoveDuration(long moveDuration)11688         public void setMoveDuration(long moveDuration) {
11689             mMoveDuration = moveDuration;
11690         }
11691 
11692         /**
11693          * Gets the current duration for which all add animations will run.
11694          *
11695          * @return The current add duration
11696          */
getAddDuration()11697         public long getAddDuration() {
11698             return mAddDuration;
11699         }
11700 
11701         /**
11702          * Sets the duration for which all add animations will run.
11703          *
11704          * @param addDuration The add duration
11705          */
setAddDuration(long addDuration)11706         public void setAddDuration(long addDuration) {
11707             mAddDuration = addDuration;
11708         }
11709 
11710         /**
11711          * Gets the current duration for which all remove animations will run.
11712          *
11713          * @return The current remove duration
11714          */
getRemoveDuration()11715         public long getRemoveDuration() {
11716             return mRemoveDuration;
11717         }
11718 
11719         /**
11720          * Sets the duration for which all remove animations will run.
11721          *
11722          * @param removeDuration The remove duration
11723          */
setRemoveDuration(long removeDuration)11724         public void setRemoveDuration(long removeDuration) {
11725             mRemoveDuration = removeDuration;
11726         }
11727 
11728         /**
11729          * Gets the current duration for which all change animations will run.
11730          *
11731          * @return The current change duration
11732          */
getChangeDuration()11733         public long getChangeDuration() {
11734             return mChangeDuration;
11735         }
11736 
11737         /**
11738          * Sets the duration for which all change animations will run.
11739          *
11740          * @param changeDuration The change duration
11741          */
setChangeDuration(long changeDuration)11742         public void setChangeDuration(long changeDuration) {
11743             mChangeDuration = changeDuration;
11744         }
11745 
11746         /**
11747          * Internal only:
11748          * Sets the listener that must be called when the animator is finished
11749          * animating the item (or immediately if no animation happens). This is set
11750          * internally and is not intended to be set by external code.
11751          *
11752          * @param listener The listener that must be called.
11753          */
setListener(ItemAnimatorListener listener)11754         void setListener(ItemAnimatorListener listener) {
11755             mListener = listener;
11756         }
11757 
11758         /**
11759          * Called by the RecyclerView before the layout begins. Item animator should record
11760          * necessary information about the View before it is potentially rebound, moved or removed.
11761          * <p>
11762          * The data returned from this method will be passed to the related <code>animate**</code>
11763          * methods.
11764          * <p>
11765          * Note that this method may be called after pre-layout phase if LayoutManager adds new
11766          * Views to the layout in pre-layout pass.
11767          * <p>
11768          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
11769          * the View and the adapter change flags.
11770          *
11771          * @param state       The current State of RecyclerView which includes some useful data
11772          *                    about the layout that will be calculated.
11773          * @param viewHolder  The ViewHolder whose information should be recorded.
11774          * @param changeFlags Additional information about what changes happened in the Adapter
11775          *                    about the Item represented by this ViewHolder. For instance, if
11776          *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
11777          * @param payloads    The payload list that was previously passed to
11778          *                    {@link Adapter#notifyItemChanged(int, Object)} or
11779          *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
11780          *
11781          * @return An ItemHolderInfo instance that preserves necessary information about the
11782          * ViewHolder. This object will be passed back to related <code>animate**</code> methods
11783          * after layout is complete.
11784          *
11785          * @see #recordPostLayoutInformation(State, ViewHolder)
11786          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11787          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11788          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11789          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11790          */
recordPreLayoutInformation(@onNull State state, @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags, @NonNull List<Object> payloads)11791         public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
11792                 @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
11793                 @NonNull List<Object> payloads) {
11794             return obtainHolderInfo().setFrom(viewHolder);
11795         }
11796 
11797         /**
11798          * Called by the RecyclerView after the layout is complete. Item animator should record
11799          * necessary information about the View's final state.
11800          * <p>
11801          * The data returned from this method will be passed to the related <code>animate**</code>
11802          * methods.
11803          * <p>
11804          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
11805          * the View.
11806          *
11807          * @param state      The current State of RecyclerView which includes some useful data about
11808          *                   the layout that will be calculated.
11809          * @param viewHolder The ViewHolder whose information should be recorded.
11810          *
11811          * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
11812          * This object will be passed back to related <code>animate**</code> methods when
11813          * RecyclerView decides how items should be animated.
11814          *
11815          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11816          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11817          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11818          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11819          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11820          */
recordPostLayoutInformation(@onNull State state, @NonNull ViewHolder viewHolder)11821         public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
11822                 @NonNull ViewHolder viewHolder) {
11823             return obtainHolderInfo().setFrom(viewHolder);
11824         }
11825 
11826         /**
11827          * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
11828          * <p>
11829          * This means that the View was a child of the LayoutManager when layout started but has
11830          * been removed by the LayoutManager. It might have been removed from the adapter or simply
11831          * become invisible due to other factors. You can distinguish these two cases by checking
11832          * the change flags that were passed to
11833          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11834          * <p>
11835          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
11836          * animation callback method which will be called by the RecyclerView depends on the
11837          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
11838          * LayoutManager's decision whether to layout the changed version of a disappearing
11839          * ViewHolder or not. RecyclerView will call
11840          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11841          * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
11842          * returns {@code false} from
11843          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
11844          * LayoutManager lays out a new disappearing view that holds the updated information.
11845          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
11846          * <p>
11847          * If LayoutManager supports predictive animations, it might provide a target disappear
11848          * location for the View by laying it out in that location. When that happens,
11849          * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
11850          * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
11851          * <p>
11852          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11853          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11854          * decides not to animate the view).
11855          *
11856          * @param viewHolder    The ViewHolder which should be animated
11857          * @param preLayoutInfo The information that was returned from
11858          *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11859          * @param postLayoutInfo The information that was returned from
11860          *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
11861          *                       null if the LayoutManager did not layout the item.
11862          *
11863          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11864          * false otherwise.
11865          */
11866         public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
11867                 @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
11868 
11869         /**
11870          * Called by the RecyclerView when a ViewHolder is added to the layout.
11871          * <p>
11872          * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
11873          * but has  been added by the LayoutManager. It might be newly added to the adapter or
11874          * simply become visible due to other factors.
11875          * <p>
11876          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11877          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11878          * decides not to animate the view).
11879          *
11880          * @param viewHolder     The ViewHolder which should be animated
11881          * @param preLayoutInfo  The information that was returned from
11882          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11883          *                       Might be null if Item was just added to the adapter or
11884          *                       LayoutManager does not support predictive animations or it could
11885          *                       not predict that this ViewHolder will become visible.
11886          * @param postLayoutInfo The information that was returned from {@link
11887          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11888          *
11889          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11890          * false otherwise.
11891          */
11892         public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
11893                 @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11894 
11895         /**
11896          * Called by the RecyclerView when a ViewHolder is present in both before and after the
11897          * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
11898          * for it or a {@link Adapter#notifyDataSetChanged()} call.
11899          * <p>
11900          * This ViewHolder still represents the same data that it was representing when the layout
11901          * started but its position / size may be changed by the LayoutManager.
11902          * <p>
11903          * If the Item's layout position didn't change, RecyclerView still calls this method because
11904          * it does not track this information (or does not necessarily know that an animation is
11905          * not required). Your ItemAnimator should handle this case and if there is nothing to
11906          * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
11907          * <code>false</code>.
11908          * <p>
11909          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11910          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11911          * decides not to animate the view).
11912          *
11913          * @param viewHolder     The ViewHolder which should be animated
11914          * @param preLayoutInfo  The information that was returned from
11915          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11916          * @param postLayoutInfo The information that was returned from {@link
11917          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11918          *
11919          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11920          * false otherwise.
11921          */
11922         public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
11923                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11924 
11925         /**
11926          * Called by the RecyclerView when an adapter item is present both before and after the
11927          * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
11928          * for it. This method may also be called when
11929          * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
11930          * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
11931          * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
11932          * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
11933          * called for the new ViewHolder and the old one will be recycled.
11934          * <p>
11935          * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
11936          * a good possibility that item contents didn't really change but it is rebound from the
11937          * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
11938          * screen didn't change and your animator should handle this case as well and avoid creating
11939          * unnecessary animations.
11940          * <p>
11941          * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
11942          * previous presentation of the item as-is and supply a new ViewHolder for the updated
11943          * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
11944          * This is useful if you don't know the contents of the Item and would like
11945          * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
11946          * <p>
11947          * When you are writing a custom item animator for your layout, it might be more performant
11948          * and elegant to re-use the same ViewHolder and animate the content changes manually.
11949          * <p>
11950          * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
11951          * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
11952          * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
11953          * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
11954          * which represent the same Item. In that case, only the new ViewHolder is visible
11955          * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
11956          * <p>
11957          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
11958          * ViewHolder when their animation is complete
11959          * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
11960          * animate the view).
11961          * <p>
11962          *  If oldHolder and newHolder are the same instance, you should call
11963          * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
11964          * <p>
11965          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
11966          * animation callback method which will be called by the RecyclerView depends on the
11967          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
11968          * LayoutManager's decision whether to layout the changed version of a disappearing
11969          * ViewHolder or not. RecyclerView will call
11970          * {@code animateChange} instead of
11971          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11972          * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
11973          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
11974          * LayoutManager lays out a new disappearing view that holds the updated information.
11975          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
11976          *
11977          * @param oldHolder     The ViewHolder before the layout is started, might be the same
11978          *                      instance with newHolder.
11979          * @param newHolder     The ViewHolder after the layout is finished, might be the same
11980          *                      instance with oldHolder.
11981          * @param preLayoutInfo  The information that was returned from
11982          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11983          * @param postLayoutInfo The information that was returned from {@link
11984          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11985          *
11986          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11987          * false otherwise.
11988          */
11989         public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
11990                 @NonNull ViewHolder newHolder,
11991                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11992 
buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder)11993         @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
11994             int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
11995             if (viewHolder.isInvalid()) {
11996                 return FLAG_INVALIDATED;
11997             }
11998             if ((flags & FLAG_INVALIDATED) == 0) {
11999                 final int oldPos = viewHolder.getOldPosition();
12000                 final int pos = viewHolder.getAdapterPosition();
12001                 if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
12002                     flags |= FLAG_MOVED;
12003                 }
12004             }
12005             return flags;
12006         }
12007 
12008         /**
12009          * Called when there are pending animations waiting to be started. This state
12010          * is governed by the return values from
12011          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12012          * animateAppearance()},
12013          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12014          * animateChange()}
12015          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12016          * animatePersistence()}, and
12017          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12018          * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
12019          * called later to start the associated animations. runPendingAnimations() will be scheduled
12020          * to be run on the next frame.
12021          */
12022         public abstract void runPendingAnimations();
12023 
12024         /**
12025          * Method called when an animation on a view should be ended immediately.
12026          * This could happen when other events, like scrolling, occur, so that
12027          * animating views can be quickly put into their proper end locations.
12028          * Implementations should ensure that any animations running on the item
12029          * are canceled and affected properties are set to their end values.
12030          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
12031          * animation since the animations are effectively done when this method is called.
12032          *
12033          * @param item The item for which an animation should be stopped.
12034          */
12035         public abstract void endAnimation(ViewHolder item);
12036 
12037         /**
12038          * Method called when all item animations should be ended immediately.
12039          * This could happen when other events, like scrolling, occur, so that
12040          * animating views can be quickly put into their proper end locations.
12041          * Implementations should ensure that any animations running on any items
12042          * are canceled and affected properties are set to their end values.
12043          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
12044          * animation since the animations are effectively done when this method is called.
12045          */
12046         public abstract void endAnimations();
12047 
12048         /**
12049          * Method which returns whether there are any item animations currently running.
12050          * This method can be used to determine whether to delay other actions until
12051          * animations end.
12052          *
12053          * @return true if there are any item animations currently running, false otherwise.
12054          */
12055         public abstract boolean isRunning();
12056 
12057         /**
12058          * Method to be called by subclasses when an animation is finished.
12059          * <p>
12060          * For each call RecyclerView makes to
12061          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12062          * animateAppearance()},
12063          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12064          * animatePersistence()}, or
12065          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12066          * animateDisappearance()}, there
12067          * should
12068          * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
12069          * <p>
12070          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12071          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
12072          * and <code>newHolder</code>  (if they are not the same instance).
12073          *
12074          * @param viewHolder The ViewHolder whose animation is finished.
12075          * @see #onAnimationFinished(ViewHolder)
12076          */
dispatchAnimationFinished(ViewHolder viewHolder)12077         public final void dispatchAnimationFinished(ViewHolder viewHolder) {
12078             onAnimationFinished(viewHolder);
12079             if (mListener != null) {
12080                 mListener.onAnimationFinished(viewHolder);
12081             }
12082         }
12083 
12084         /**
12085          * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
12086          * ItemAnimator.
12087          *
12088          * @param viewHolder The ViewHolder whose animation is finished. There might still be other
12089          *                   animations running on this ViewHolder.
12090          * @see #dispatchAnimationFinished(ViewHolder)
12091          */
onAnimationFinished(ViewHolder viewHolder)12092         public void onAnimationFinished(ViewHolder viewHolder) {
12093         }
12094 
12095         /**
12096          * Method to be called by subclasses when an animation is started.
12097          * <p>
12098          * For each call RecyclerView makes to
12099          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12100          * animateAppearance()},
12101          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12102          * animatePersistence()}, or
12103          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12104          * animateDisappearance()}, there should be a matching
12105          * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
12106          * <p>
12107          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12108          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
12109          * and <code>newHolder</code> (if they are not the same instance).
12110          * <p>
12111          * If your ItemAnimator decides not to animate a ViewHolder, it should call
12112          * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
12113          * {@link #dispatchAnimationStarted(ViewHolder)}.
12114          *
12115          * @param viewHolder The ViewHolder whose animation is starting.
12116          * @see #onAnimationStarted(ViewHolder)
12117          */
dispatchAnimationStarted(ViewHolder viewHolder)12118         public final void dispatchAnimationStarted(ViewHolder viewHolder) {
12119             onAnimationStarted(viewHolder);
12120         }
12121 
12122         /**
12123          * Called when a new animation is started on the given ViewHolder.
12124          *
12125          * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
12126          *                   might already be animating and this might be another animation.
12127          * @see #dispatchAnimationStarted(ViewHolder)
12128          */
onAnimationStarted(ViewHolder viewHolder)12129         public void onAnimationStarted(ViewHolder viewHolder) {
12130 
12131         }
12132 
12133         /**
12134          * Like {@link #isRunning()}, this method returns whether there are any item
12135          * animations currently running. Additionally, the listener passed in will be called
12136          * when there are no item animations running, either immediately (before the method
12137          * returns) if no animations are currently running, or when the currently running
12138          * animations are {@link #dispatchAnimationsFinished() finished}.
12139          *
12140          * <p>Note that the listener is transient - it is either called immediately and not
12141          * stored at all, or stored only until it is called when running animations
12142          * are finished sometime later.</p>
12143          *
12144          * @param listener A listener to be called immediately if no animations are running
12145          * or later when currently-running animations have finished. A null listener is
12146          * equivalent to calling {@link #isRunning()}.
12147          * @return true if there are any item animations currently running, false otherwise.
12148          */
isRunning(ItemAnimatorFinishedListener listener)12149         public final boolean isRunning(ItemAnimatorFinishedListener listener) {
12150             boolean running = isRunning();
12151             if (listener != null) {
12152                 if (!running) {
12153                     listener.onAnimationsFinished();
12154                 } else {
12155                     mFinishedListeners.add(listener);
12156                 }
12157             }
12158             return running;
12159         }
12160 
12161         /**
12162          * When an item is changed, ItemAnimator can decide whether it wants to re-use
12163          * the same ViewHolder for animations or RecyclerView should create a copy of the
12164          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12165          * <p>
12166          * Note that this method will only be called if the {@link ViewHolder} still has the same
12167          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12168          * both {@link ViewHolder}s in the
12169          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12170          * <p>
12171          * If your application is using change payloads, you can override
12172          * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
12173          *
12174          * @param viewHolder The ViewHolder which represents the changed item's old content.
12175          *
12176          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
12177          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
12178          *         ItemAnimator to animate. Default implementation returns <code>true</code>.
12179          *
12180          * @see #canReuseUpdatedViewHolder(ViewHolder, List)
12181          */
canReuseUpdatedViewHolder(@onNull ViewHolder viewHolder)12182         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
12183             return true;
12184         }
12185 
12186         /**
12187          * When an item is changed, ItemAnimator can decide whether it wants to re-use
12188          * the same ViewHolder for animations or RecyclerView should create a copy of the
12189          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12190          * <p>
12191          * Note that this method will only be called if the {@link ViewHolder} still has the same
12192          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12193          * both {@link ViewHolder}s in the
12194          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12195          *
12196          * @param viewHolder The ViewHolder which represents the changed item's old content.
12197          * @param payloads A non-null list of merged payloads that were sent with change
12198          *                 notifications. Can be empty if the adapter is invalidated via
12199          *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
12200          *                 payloads will be passed into
12201          *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
12202          *                 method <b>if</b> this method returns <code>true</code>.
12203          *
12204          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
12205          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
12206          *         ItemAnimator to animate. Default implementation calls
12207          *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
12208          *
12209          * @see #canReuseUpdatedViewHolder(ViewHolder)
12210          */
canReuseUpdatedViewHolder(@onNull ViewHolder viewHolder, @NonNull List<Object> payloads)12211         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
12212                 @NonNull List<Object> payloads) {
12213             return canReuseUpdatedViewHolder(viewHolder);
12214         }
12215 
12216         /**
12217          * This method should be called by ItemAnimator implementations to notify
12218          * any listeners that all pending and active item animations are finished.
12219          */
dispatchAnimationsFinished()12220         public final void dispatchAnimationsFinished() {
12221             final int count = mFinishedListeners.size();
12222             for (int i = 0; i < count; ++i) {
12223                 mFinishedListeners.get(i).onAnimationsFinished();
12224             }
12225             mFinishedListeners.clear();
12226         }
12227 
12228         /**
12229          * Returns a new {@link ItemHolderInfo} which will be used to store information about the
12230          * ViewHolder. This information will later be passed into <code>animate**</code> methods.
12231          * <p>
12232          * You can override this method if you want to extend {@link ItemHolderInfo} and provide
12233          * your own instances.
12234          *
12235          * @return A new {@link ItemHolderInfo}.
12236          */
obtainHolderInfo()12237         public ItemHolderInfo obtainHolderInfo() {
12238             return new ItemHolderInfo();
12239         }
12240 
12241         /**
12242          * The interface to be implemented by listeners to animation events from this
12243          * ItemAnimator. This is used internally and is not intended for developers to
12244          * create directly.
12245          */
12246         interface ItemAnimatorListener {
12247             void onAnimationFinished(ViewHolder item);
12248         }
12249 
12250         /**
12251          * This interface is used to inform listeners when all pending or running animations
12252          * in an ItemAnimator are finished. This can be used, for example, to delay an action
12253          * in a data set until currently-running animations are complete.
12254          *
12255          * @see #isRunning(ItemAnimatorFinishedListener)
12256          */
12257         public interface ItemAnimatorFinishedListener {
12258             /**
12259              * Notifies when all pending or running animations in an ItemAnimator are finished.
12260              */
12261             void onAnimationsFinished();
12262         }
12263 
12264         /**
12265          * A simple data structure that holds information about an item's bounds.
12266          * This information is used in calculating item animations. Default implementation of
12267          * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
12268          * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
12269          * structure. You can extend this class if you would like to keep more information about
12270          * the Views.
12271          * <p>
12272          * If you want to provide your own implementation but still use `super` methods to record
12273          * basic information, you can override {@link #obtainHolderInfo()} to provide your own
12274          * instances.
12275          */
12276         public static class ItemHolderInfo {
12277 
12278             /**
12279              * The left edge of the View (excluding decorations)
12280              */
12281             public int left;
12282 
12283             /**
12284              * The top edge of the View (excluding decorations)
12285              */
12286             public int top;
12287 
12288             /**
12289              * The right edge of the View (excluding decorations)
12290              */
12291             public int right;
12292 
12293             /**
12294              * The bottom edge of the View (excluding decorations)
12295              */
12296             public int bottom;
12297 
12298             /**
12299              * The change flags that were passed to
12300              * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
12301              */
12302             @AdapterChanges
12303             public int changeFlags;
12304 
ItemHolderInfo()12305             public ItemHolderInfo() {
12306             }
12307 
12308             /**
12309              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
12310              * the given ViewHolder. Clears all {@link #changeFlags}.
12311              *
12312              * @param holder The ViewHolder whose bounds should be copied.
12313              * @return This {@link ItemHolderInfo}
12314              */
setFrom(RecyclerView.ViewHolder holder)12315             public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder) {
12316                 return setFrom(holder, 0);
12317             }
12318 
12319             /**
12320              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
12321              * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
12322              *
12323              * @param holder The ViewHolder whose bounds should be copied.
12324              * @param flags  The adapter change flags that were passed into
12325              *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
12326              *               List)}.
12327              * @return This {@link ItemHolderInfo}
12328              */
setFrom(RecyclerView.ViewHolder holder, @AdapterChanges int flags)12329             public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder,
12330                     @AdapterChanges int flags) {
12331                 final View view = holder.itemView;
12332                 this.left = view.getLeft();
12333                 this.top = view.getTop();
12334                 this.right = view.getRight();
12335                 this.bottom = view.getBottom();
12336                 return this;
12337             }
12338         }
12339     }
12340 
12341     @Override
getChildDrawingOrder(int childCount, int i)12342     protected int getChildDrawingOrder(int childCount, int i) {
12343         if (mChildDrawingOrderCallback == null) {
12344             return super.getChildDrawingOrder(childCount, i);
12345         } else {
12346             return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
12347         }
12348     }
12349 
12350     /**
12351      * A callback interface that can be used to alter the drawing order of RecyclerView children.
12352      * <p>
12353      * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
12354      * that applies to that method also applies to this callback. For example, changing the drawing
12355      * order of two views will not have any effect if their elevation values are different since
12356      * elevation overrides the result of this callback.
12357      */
12358     public interface ChildDrawingOrderCallback {
12359         /**
12360          * Returns the index of the child to draw for this iteration. Override this
12361          * if you want to change the drawing order of children. By default, it
12362          * returns i.
12363          *
12364          * @param i The current iteration.
12365          * @return The index of the child to draw this iteration.
12366          *
12367          * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
12368          */
12369         int onGetChildDrawingOrder(int childCount, int i);
12370     }
12371 }
12372