RecyclerView.java revision f97ddbc502678a00306afcd73e7a6bb3bcc4c189
1/*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18package androidx.recyclerview.widget;
19
20import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
21import static androidx.core.view.ViewCompat.TYPE_NON_TOUCH;
22import static androidx.core.view.ViewCompat.TYPE_TOUCH;
23
24import android.annotation.SuppressLint;
25import android.content.Context;
26import android.content.res.Resources;
27import android.content.res.TypedArray;
28import android.database.Observable;
29import android.graphics.Canvas;
30import android.graphics.Matrix;
31import android.graphics.PointF;
32import android.graphics.Rect;
33import android.graphics.RectF;
34import android.graphics.drawable.Drawable;
35import android.graphics.drawable.StateListDrawable;
36import android.os.Build;
37import android.os.Bundle;
38import android.os.Parcel;
39import android.os.Parcelable;
40import android.os.SystemClock;
41import android.util.AttributeSet;
42import android.util.Log;
43import android.util.SparseArray;
44import android.view.Display;
45import android.view.FocusFinder;
46import android.view.InputDevice;
47import android.view.MotionEvent;
48import android.view.VelocityTracker;
49import android.view.View;
50import android.view.ViewConfiguration;
51import android.view.ViewGroup;
52import android.view.ViewParent;
53import android.view.accessibility.AccessibilityEvent;
54import android.view.accessibility.AccessibilityManager;
55import android.view.animation.Interpolator;
56import android.widget.EdgeEffect;
57import android.widget.LinearLayout;
58import android.widget.OverScroller;
59
60import androidx.annotation.CallSuper;
61import androidx.annotation.IntDef;
62import androidx.annotation.NonNull;
63import androidx.annotation.Nullable;
64import androidx.annotation.Px;
65import androidx.annotation.RestrictTo;
66import androidx.annotation.VisibleForTesting;
67import androidx.core.os.TraceCompat;
68import androidx.core.util.Preconditions;
69import androidx.core.view.InputDeviceCompat;
70import androidx.core.view.MotionEventCompat;
71import androidx.core.view.NestedScrollingChild2;
72import androidx.core.view.NestedScrollingChildHelper;
73import androidx.core.view.ScrollingView;
74import androidx.core.view.ViewCompat;
75import androidx.core.view.ViewConfigurationCompat;
76import androidx.core.view.accessibility.AccessibilityEventCompat;
77import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
78import androidx.core.widget.EdgeEffectCompat;
79import androidx.customview.view.AbsSavedState;
80import androidx.recyclerview.R;
81import androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
82import androidx.viewpager.widget.ViewPager;
83
84import java.lang.annotation.Retention;
85import java.lang.annotation.RetentionPolicy;
86import java.lang.ref.WeakReference;
87import java.lang.reflect.Constructor;
88import java.lang.reflect.InvocationTargetException;
89import java.util.ArrayList;
90import java.util.Collections;
91import java.util.List;
92
93
94/**
95 * A flexible view for providing a limited window into a large data set.
96 *
97 * <h3>Glossary of terms:</h3>
98 *
99 * <ul>
100 *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
101 *     that represent items in a data set.</li>
102 *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
103 *     <li><em>Index:</em> The index of an attached child view as used in a call to
104 *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
105 *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
106 *     to a <em>position</em> within the adapter.</li>
107 *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
108 *     position may be placed in a cache for later reuse to display the same type of data again
109 *     later. This can drastically improve performance by skipping initial layout inflation
110 *     or construction.</li>
111 *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
112 *     state during layout. Scrap views may be reused without becoming fully detached
113 *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
114 *     by the adapter if the view was considered <em>dirty</em>.</li>
115 *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
116 *     being displayed.</li>
117 * </ul>
118 *
119 * <h4>Positions in RecyclerView:</h4>
120 * <p>
121 * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
122 * {@link LayoutManager} to be able to detect data set changes in batches during a layout
123 * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
124 * It also helps with performance because all view bindings happen at the same time and unnecessary
125 * bindings are avoided.
126 * <p>
127 * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
128 * <ul>
129 *     <li>layout position: Position of an item in the latest layout calculation. This is the
130 *     position from the LayoutManager's perspective.</li>
131 *     <li>adapter position: Position of an item in the adapter. This is the position from
132 *     the Adapter's perspective.</li>
133 * </ul>
134 * <p>
135 * These two positions are the same except the time between dispatching <code>adapter.notify*
136 * </code> events and calculating the updated layout.
137 * <p>
138 * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
139 * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
140 * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
141 * last layout calculation. You can rely on these positions to be consistent with what user is
142 * currently seeing on the screen. For example, if you have a list of items on the screen and user
143 * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
144 * is seeing.
145 * <p>
146 * The other set of position related methods are in the form of
147 * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
148 * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
149 * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
150 * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
151 * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
152 * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
153 * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
154 * <code>null</code> results from these methods.
155 * <p>
156 * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
157 * writing an {@link Adapter}, you probably want to use adapter positions.
158 *
159 * @attr ref androidx.recyclerview.R.styleable#RecyclerView_layoutManager
160 */
161public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild2 {
162
163    static final String TAG = "RecyclerView";
164
165    static final boolean DEBUG = false;
166
167    static final boolean VERBOSE_TRACING = false;
168
169    private static final int[]  NESTED_SCROLLING_ATTRS =
170            {16843830 /* android.R.attr.nestedScrollingEnabled */};
171
172    private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
173
174    /**
175     * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
176     * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
177     * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
178     * recursively traverses itemView and invalidates display list for each ViewGroup that matches
179     * this criteria.
180     */
181    static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
182            || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
183    /**
184     * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
185     * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
186     * 0 when mode is unspecified.
187     */
188    static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
189
190    static final boolean POST_UPDATES_ON_ANIMATION = Build.VERSION.SDK_INT >= 16;
191
192    /**
193     * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
194     * RenderThread but before the next frame begins. We schedule prefetch work in this window.
195     */
196    private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
197
198    /**
199     * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
200     * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
201     */
202    private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
203
204    /**
205     * on API 15-, a focused child can still be considered a focused child of RV even after
206     * it's being removed or its focusable flag is set to false. This is because when this focused
207     * child is detached, the reference to this child is not removed in clearFocus. API 16 and above
208     * properly handle this case by calling ensureInputFocusOnFirstFocusable or rootViewRequestFocus
209     * to request focus on a new child, which will clear the focus on the old (detached) child as a
210     * side-effect.
211     */
212    private static final boolean IGNORE_DETACHED_FOCUSED_CHILD = Build.VERSION.SDK_INT <= 15;
213
214    static final boolean DISPATCH_TEMP_DETACH = false;
215
216    /** @hide */
217    @RestrictTo(LIBRARY_GROUP)
218    @IntDef({HORIZONTAL, VERTICAL})
219    @Retention(RetentionPolicy.SOURCE)
220    public @interface Orientation {}
221
222    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
223    public static final int VERTICAL = LinearLayout.VERTICAL;
224
225    static final int DEFAULT_ORIENTATION = VERTICAL;
226    public static final int NO_POSITION = -1;
227    public static final long NO_ID = -1;
228    public static final int INVALID_TYPE = -1;
229
230    /**
231     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
232     * that the RecyclerView should use the standard touch slop for smooth,
233     * continuous scrolling.
234     */
235    public static final int TOUCH_SLOP_DEFAULT = 0;
236
237    /**
238     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
239     * that the RecyclerView should use the standard touch slop for scrolling
240     * widgets that snap to a page or other coarse-grained barrier.
241     */
242    public static final int TOUCH_SLOP_PAGING = 1;
243
244    static final int MAX_SCROLL_DURATION = 2000;
245
246    /**
247     * RecyclerView is calculating a scroll.
248     * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
249     * it. Try to avoid using EditText, focusable views or handle them with care.
250     */
251    static final String TRACE_SCROLL_TAG = "RV Scroll";
252
253    /**
254     * OnLayout has been called by the View system.
255     * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
256     * update themselves directly. This will cause a full re-layout but when it happens via the
257     * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
258     */
259    private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
260
261    /**
262     * NotifyDataSetChanged or equal has been called.
263     * If this is taking a long time, try sending granular notify adapter changes instead of just
264     * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
265     * might help.
266     */
267    private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
268
269    /**
270     * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
271     * If this is taking a long time, you may have dispatched too many Adapter updates causing too
272     * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
273     * methods.
274     */
275    private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
276
277    /**
278     * RecyclerView is rebinding a View.
279     * If this is taking a lot of time, consider optimizing your layout or make sure you are not
280     * doing extra operations in onBindViewHolder call.
281     */
282    static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
283
284    /**
285     * RecyclerView is attempting to pre-populate off screen views.
286     */
287    static final String TRACE_PREFETCH_TAG = "RV Prefetch";
288
289    /**
290     * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
291     * RecyclerView.
292     */
293    static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
294
295    /**
296     * RecyclerView is creating a new View.
297     * If too many of these present in Systrace:
298     * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
299     * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
300     * > Adapter#onFailedToRecycleView(ViewHolder)})
301     *
302     * - There might be too many item view types.
303     * > Try merging them
304     *
305     * - There might be too many itemChange animations and not enough space in RecyclerPool.
306     * >Try increasing your pool size and item cache size.
307     */
308    static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
309    private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
310            new Class[]{Context.class, AttributeSet.class, int.class, int.class};
311
312    private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
313
314    final Recycler mRecycler = new Recycler();
315
316    private SavedState mPendingSavedState;
317
318    /**
319     * Handles adapter updates
320     */
321    AdapterHelper mAdapterHelper;
322
323    /**
324     * Handles abstraction between LayoutManager children and RecyclerView children
325     */
326    ChildHelper mChildHelper;
327
328    /**
329     * Keeps data about views to be used for animations
330     */
331    final ViewInfoStore mViewInfoStore = new ViewInfoStore();
332
333    /**
334     * Prior to L, there is no way to query this variable which is why we override the setter and
335     * track it here.
336     */
337    boolean mClipToPadding;
338
339    /**
340     * Note: this Runnable is only ever posted if:
341     * 1) We've been through first layout
342     * 2) We know we have a fixed size (mHasFixedSize)
343     * 3) We're attached
344     */
345    final Runnable mUpdateChildViewsRunnable = new Runnable() {
346        @Override
347        public void run() {
348            if (!mFirstLayoutComplete || isLayoutRequested()) {
349                // a layout request will happen, we should not do layout here.
350                return;
351            }
352            if (!mIsAttached) {
353                requestLayout();
354                // if we are not attached yet, mark us as requiring layout and skip
355                return;
356            }
357            if (mLayoutFrozen) {
358                mLayoutWasDefered = true;
359                return; //we'll process updates when ice age ends.
360            }
361            consumePendingUpdateOperations();
362        }
363    };
364
365    final Rect mTempRect = new Rect();
366    private final Rect mTempRect2 = new Rect();
367    final RectF mTempRectF = new RectF();
368    Adapter mAdapter;
369    @VisibleForTesting LayoutManager mLayout;
370    RecyclerListener mRecyclerListener;
371    final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
372    private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
373            new ArrayList<>();
374    private OnItemTouchListener mActiveOnItemTouchListener;
375    boolean mIsAttached;
376    boolean mHasFixedSize;
377    boolean mEnableFastScroller;
378    @VisibleForTesting boolean mFirstLayoutComplete;
379
380    /**
381     * The current depth of nested calls to {@link #startInterceptRequestLayout()} (number of
382     * calls to {@link #startInterceptRequestLayout()} - number of calls to
383     * {@link #stopInterceptRequestLayout(boolean)} .  This is used to signal whether we
384     * should defer layout operations caused by layout requests from children of
385     * {@link RecyclerView}.
386     */
387    private int mInterceptRequestLayoutDepth = 0;
388
389    /**
390     * True if a call to requestLayout was intercepted and prevented from executing like normal and
391     * we plan on continuing with normal execution later.
392     */
393    boolean mLayoutWasDefered;
394
395    boolean mLayoutFrozen;
396    private boolean mIgnoreMotionEventTillDown;
397
398    // binary OR of change events that were eaten during a layout or scroll.
399    private int mEatenAccessibilityChangeFlags;
400    boolean mAdapterUpdateDuringMeasure;
401
402    private final AccessibilityManager mAccessibilityManager;
403    private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
404
405    /**
406     * True after an event occurs that signals that the entire data set has changed. In that case,
407     * we cannot run any animations since we don't know what happened until layout.
408     *
409     * Attached items are invalid until next layout, at which point layout will animate/replace
410     * items as necessary, building up content from the (effectively) new adapter from scratch.
411     *
412     * Cached items must be discarded when setting this to true, so that the cache may be freely
413     * used by prefetching until the next layout occurs.
414     *
415     * @see #processDataSetCompletelyChanged(boolean)
416     */
417    boolean mDataSetHasChangedAfterLayout = false;
418
419    /**
420     * True after the data set has completely changed and
421     * {@link LayoutManager#onItemsChanged(RecyclerView)} should be called during the subsequent
422     * measure/layout.
423     *
424     * @see #processDataSetCompletelyChanged(boolean)
425     */
426    boolean mDispatchItemsChangedEvent = false;
427
428    /**
429     * This variable is incremented during a dispatchLayout and/or scroll.
430     * Some methods should not be called during these periods (e.g. adapter data change).
431     * Doing so will create hard to find bugs so we better check it and throw an exception.
432     *
433     * @see #assertInLayoutOrScroll(String)
434     * @see #assertNotInLayoutOrScroll(String)
435     */
436    private int mLayoutOrScrollCounter = 0;
437
438    /**
439     * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
440     * (for API compatibility).
441     * <p>
442     * It is a bad practice for a developer to update the data in a scroll callback since it is
443     * potentially called during a layout.
444     */
445    private int mDispatchScrollCounter = 0;
446
447    @NonNull
448    private EdgeEffectFactory mEdgeEffectFactory = new EdgeEffectFactory();
449    private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
450
451    ItemAnimator mItemAnimator = new DefaultItemAnimator();
452
453    private static final int INVALID_POINTER = -1;
454
455    /**
456     * The RecyclerView is not currently scrolling.
457     * @see #getScrollState()
458     */
459    public static final int SCROLL_STATE_IDLE = 0;
460
461    /**
462     * The RecyclerView is currently being dragged by outside input such as user touch input.
463     * @see #getScrollState()
464     */
465    public static final int SCROLL_STATE_DRAGGING = 1;
466
467    /**
468     * The RecyclerView is currently animating to a final position while not under
469     * outside control.
470     * @see #getScrollState()
471     */
472    public static final int SCROLL_STATE_SETTLING = 2;
473
474    static final long FOREVER_NS = Long.MAX_VALUE;
475
476    // Touch/scrolling handling
477
478    private int mScrollState = SCROLL_STATE_IDLE;
479    private int mScrollPointerId = INVALID_POINTER;
480    private VelocityTracker mVelocityTracker;
481    private int mInitialTouchX;
482    private int mInitialTouchY;
483    private int mLastTouchX;
484    private int mLastTouchY;
485    private int mTouchSlop;
486    private OnFlingListener mOnFlingListener;
487    private final int mMinFlingVelocity;
488    private final int mMaxFlingVelocity;
489
490    // This value is used when handling rotary encoder generic motion events.
491    private float mScaledHorizontalScrollFactor = Float.MIN_VALUE;
492    private float mScaledVerticalScrollFactor = Float.MIN_VALUE;
493
494    private boolean mPreserveFocusAfterLayout = true;
495
496    final ViewFlinger mViewFlinger = new ViewFlinger();
497
498    GapWorker mGapWorker;
499    GapWorker.LayoutPrefetchRegistryImpl mPrefetchRegistry =
500            ALLOW_THREAD_GAP_WORK ? new GapWorker.LayoutPrefetchRegistryImpl() : null;
501
502    final State mState = new State();
503
504    private OnScrollListener mScrollListener;
505    private List<OnScrollListener> mScrollListeners;
506
507    // For use in item animations
508    boolean mItemsAddedOrRemoved = false;
509    boolean mItemsChanged = false;
510    private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
511            new ItemAnimatorRestoreListener();
512    boolean mPostedAnimatorRunner = false;
513    RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
514    private ChildDrawingOrderCallback mChildDrawingOrderCallback;
515
516    // simple array to keep min and max child position during a layout calculation
517    // preserved not to create a new one in each layout pass
518    private final int[] mMinMaxLayoutPositions = new int[2];
519
520    private NestedScrollingChildHelper mScrollingChildHelper;
521    private final int[] mScrollOffset = new int[2];
522    private final int[] mScrollConsumed = new int[2];
523    private final int[] mNestedOffsets = new int[2];
524
525    /**
526     * Reusable int array for use in calls to {@link #scrollStep(int, int, int[])} so that the
527     * method may mutate it to "return" 2 ints.
528     */
529    private final int[] mScrollStepConsumed = new int[2];
530
531    /**
532     * These are views that had their a11y importance changed during a layout. We defer these events
533     * until the end of the layout because a11y service may make sync calls back to the RV while
534     * the View's state is undefined.
535     */
536    @VisibleForTesting
537    final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList<>();
538
539    private Runnable mItemAnimatorRunner = new Runnable() {
540        @Override
541        public void run() {
542            if (mItemAnimator != null) {
543                mItemAnimator.runPendingAnimations();
544            }
545            mPostedAnimatorRunner = false;
546        }
547    };
548
549    static final Interpolator sQuinticInterpolator = new Interpolator() {
550        @Override
551        public float getInterpolation(float t) {
552            t -= 1.0f;
553            return t * t * t * t * t + 1.0f;
554        }
555    };
556
557    /**
558     * The callback to convert view info diffs into animations.
559     */
560    private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
561            new ViewInfoStore.ProcessCallback() {
562                @Override
563                public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
564                        @Nullable ItemHolderInfo postInfo) {
565                    mRecycler.unscrapView(viewHolder);
566                    animateDisappearance(viewHolder, info, postInfo);
567                }
568                @Override
569                public void processAppeared(ViewHolder viewHolder,
570                        ItemHolderInfo preInfo, ItemHolderInfo info) {
571                    animateAppearance(viewHolder, preInfo, info);
572                }
573
574                @Override
575                public void processPersistent(ViewHolder viewHolder,
576                        @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
577                    viewHolder.setIsRecyclable(false);
578                    if (mDataSetHasChangedAfterLayout) {
579                        // since it was rebound, use change instead as we'll be mapping them from
580                        // stable ids. If stable ids were false, we would not be running any
581                        // animations
582                        if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo,
583                                postInfo)) {
584                            postAnimationRunner();
585                        }
586                    } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
587                        postAnimationRunner();
588                    }
589                }
590                @Override
591                public void unused(ViewHolder viewHolder) {
592                    mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
593                }
594            };
595
596    public RecyclerView(@NonNull Context context) {
597        this(context, null);
598    }
599
600    public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
601        this(context, attrs, 0);
602    }
603
604    public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
605        super(context, attrs, defStyle);
606        if (attrs != null) {
607            TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
608            mClipToPadding = a.getBoolean(0, true);
609            a.recycle();
610        } else {
611            mClipToPadding = true;
612        }
613        setScrollContainer(true);
614        setFocusableInTouchMode(true);
615
616        final ViewConfiguration vc = ViewConfiguration.get(context);
617        mTouchSlop = vc.getScaledTouchSlop();
618        mScaledHorizontalScrollFactor =
619                ViewConfigurationCompat.getScaledHorizontalScrollFactor(vc, context);
620        mScaledVerticalScrollFactor =
621                ViewConfigurationCompat.getScaledVerticalScrollFactor(vc, context);
622        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
623        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
624        setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
625
626        mItemAnimator.setListener(mItemAnimatorListener);
627        initAdapterManager();
628        initChildrenHelper();
629        initAutofill();
630        // If not explicitly specified this view is important for accessibility.
631        if (ViewCompat.getImportantForAccessibility(this)
632                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
633            ViewCompat.setImportantForAccessibility(this,
634                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
635        }
636        mAccessibilityManager = (AccessibilityManager) getContext()
637                .getSystemService(Context.ACCESSIBILITY_SERVICE);
638        setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
639        // Create the layoutManager if specified.
640
641        boolean nestedScrollingEnabled = true;
642
643        if (attrs != null) {
644            int defStyleRes = 0;
645            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
646                    defStyle, defStyleRes);
647            String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
648            int descendantFocusability = a.getInt(
649                    R.styleable.RecyclerView_android_descendantFocusability, -1);
650            if (descendantFocusability == -1) {
651                setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
652            }
653            mEnableFastScroller = a.getBoolean(R.styleable.RecyclerView_fastScrollEnabled, false);
654            if (mEnableFastScroller) {
655                StateListDrawable verticalThumbDrawable = (StateListDrawable) a
656                        .getDrawable(R.styleable.RecyclerView_fastScrollVerticalThumbDrawable);
657                Drawable verticalTrackDrawable = a
658                        .getDrawable(R.styleable.RecyclerView_fastScrollVerticalTrackDrawable);
659                StateListDrawable horizontalThumbDrawable = (StateListDrawable) a
660                        .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalThumbDrawable);
661                Drawable horizontalTrackDrawable = a
662                        .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalTrackDrawable);
663                initFastScroller(verticalThumbDrawable, verticalTrackDrawable,
664                        horizontalThumbDrawable, horizontalTrackDrawable);
665            }
666            a.recycle();
667            createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
668
669            if (Build.VERSION.SDK_INT >= 21) {
670                a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
671                        defStyle, defStyleRes);
672                nestedScrollingEnabled = a.getBoolean(0, true);
673                a.recycle();
674            }
675        } else {
676            setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
677        }
678
679        // Re-set whether nested scrolling is enabled so that it is set on all API levels
680        setNestedScrollingEnabled(nestedScrollingEnabled);
681    }
682
683    /**
684     * Label appended to all public exception strings, used to help find which RV in an app is
685     * hitting an exception.
686     */
687    String exceptionLabel() {
688        return " " + super.toString()
689                + ", adapter:" + mAdapter
690                + ", layout:" + mLayout
691                + ", context:" + getContext();
692    }
693
694    /**
695     * If not explicitly specified, this view and its children don't support autofill.
696     * <p>
697     * This is done because autofill's means of uniquely identifying views doesn't work out of the
698     * box with View recycling.
699     */
700    @SuppressLint("InlinedApi")
701    private void initAutofill() {
702        if (ViewCompat.getImportantForAutofill(this) == View.IMPORTANT_FOR_AUTOFILL_AUTO) {
703            ViewCompat.setImportantForAutofill(this,
704                    View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
705        }
706    }
707
708    /**
709     * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
710     * @return An instance of AccessibilityDelegateCompat used by RecyclerView
711     */
712    @Nullable
713    public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
714        return mAccessibilityDelegate;
715    }
716
717    /**
718     * Sets the accessibility delegate compatibility implementation used by RecyclerView.
719     * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
720     */
721    public void setAccessibilityDelegateCompat(
722            @Nullable RecyclerViewAccessibilityDelegate accessibilityDelegate) {
723        mAccessibilityDelegate = accessibilityDelegate;
724        ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
725    }
726
727    /**
728     * Instantiate and set a LayoutManager, if specified in the attributes.
729     */
730    private void createLayoutManager(Context context, String className, AttributeSet attrs,
731            int defStyleAttr, int defStyleRes) {
732        if (className != null) {
733            className = className.trim();
734            if (!className.isEmpty()) {
735                className = getFullClassName(context, className);
736                try {
737                    ClassLoader classLoader;
738                    if (isInEditMode()) {
739                        // Stupid layoutlib cannot handle simple class loaders.
740                        classLoader = this.getClass().getClassLoader();
741                    } else {
742                        classLoader = context.getClassLoader();
743                    }
744                    Class<? extends LayoutManager> layoutManagerClass =
745                            classLoader.loadClass(className).asSubclass(LayoutManager.class);
746                    Constructor<? extends LayoutManager> constructor;
747                    Object[] constructorArgs = null;
748                    try {
749                        constructor = layoutManagerClass
750                                .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
751                        constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
752                    } catch (NoSuchMethodException e) {
753                        try {
754                            constructor = layoutManagerClass.getConstructor();
755                        } catch (NoSuchMethodException e1) {
756                            e1.initCause(e);
757                            throw new IllegalStateException(attrs.getPositionDescription()
758                                    + ": Error creating LayoutManager " + className, e1);
759                        }
760                    }
761                    constructor.setAccessible(true);
762                    setLayoutManager(constructor.newInstance(constructorArgs));
763                } catch (ClassNotFoundException e) {
764                    throw new IllegalStateException(attrs.getPositionDescription()
765                            + ": Unable to find LayoutManager " + className, e);
766                } catch (InvocationTargetException e) {
767                    throw new IllegalStateException(attrs.getPositionDescription()
768                            + ": Could not instantiate the LayoutManager: " + className, e);
769                } catch (InstantiationException e) {
770                    throw new IllegalStateException(attrs.getPositionDescription()
771                            + ": Could not instantiate the LayoutManager: " + className, e);
772                } catch (IllegalAccessException e) {
773                    throw new IllegalStateException(attrs.getPositionDescription()
774                            + ": Cannot access non-public constructor " + className, e);
775                } catch (ClassCastException e) {
776                    throw new IllegalStateException(attrs.getPositionDescription()
777                            + ": Class is not a LayoutManager " + className, e);
778                }
779            }
780        }
781    }
782
783    private String getFullClassName(Context context, String className) {
784        if (className.charAt(0) == '.') {
785            return context.getPackageName() + className;
786        }
787        if (className.contains(".")) {
788            return className;
789        }
790        return RecyclerView.class.getPackage().getName() + '.' + className;
791    }
792
793    private void initChildrenHelper() {
794        mChildHelper = new ChildHelper(new ChildHelper.Callback() {
795            @Override
796            public int getChildCount() {
797                return RecyclerView.this.getChildCount();
798            }
799
800            @Override
801            public void addView(View child, int index) {
802                if (VERBOSE_TRACING) {
803                    TraceCompat.beginSection("RV addView");
804                }
805                RecyclerView.this.addView(child, index);
806                if (VERBOSE_TRACING) {
807                    TraceCompat.endSection();
808                }
809                dispatchChildAttached(child);
810            }
811
812            @Override
813            public int indexOfChild(View view) {
814                return RecyclerView.this.indexOfChild(view);
815            }
816
817            @Override
818            public void removeViewAt(int index) {
819                final View child = RecyclerView.this.getChildAt(index);
820                if (child != null) {
821                    dispatchChildDetached(child);
822
823                    // Clear any android.view.animation.Animation that may prevent the item from
824                    // detaching when being removed. If a child is re-added before the
825                    // lazy detach occurs, it will receive invalid attach/detach sequencing.
826                    child.clearAnimation();
827                }
828                if (VERBOSE_TRACING) {
829                    TraceCompat.beginSection("RV removeViewAt");
830                }
831                RecyclerView.this.removeViewAt(index);
832                if (VERBOSE_TRACING) {
833                    TraceCompat.endSection();
834                }
835            }
836
837            @Override
838            public View getChildAt(int offset) {
839                return RecyclerView.this.getChildAt(offset);
840            }
841
842            @Override
843            public void removeAllViews() {
844                final int count = getChildCount();
845                for (int i = 0; i < count; i++) {
846                    View child = getChildAt(i);
847                    dispatchChildDetached(child);
848
849                    // Clear any android.view.animation.Animation that may prevent the item from
850                    // detaching when being removed. If a child is re-added before the
851                    // lazy detach occurs, it will receive invalid attach/detach sequencing.
852                    child.clearAnimation();
853                }
854                RecyclerView.this.removeAllViews();
855            }
856
857            @Override
858            public ViewHolder getChildViewHolder(View view) {
859                return getChildViewHolderInt(view);
860            }
861
862            @Override
863            public void attachViewToParent(View child, int index,
864                    ViewGroup.LayoutParams layoutParams) {
865                final ViewHolder vh = getChildViewHolderInt(child);
866                if (vh != null) {
867                    if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
868                        throw new IllegalArgumentException("Called attach on a child which is not"
869                                + " detached: " + vh + exceptionLabel());
870                    }
871                    if (DEBUG) {
872                        Log.d(TAG, "reAttach " + vh);
873                    }
874                    vh.clearTmpDetachFlag();
875                }
876                RecyclerView.this.attachViewToParent(child, index, layoutParams);
877            }
878
879            @Override
880            public void detachViewFromParent(int offset) {
881                final View view = getChildAt(offset);
882                if (view != null) {
883                    final ViewHolder vh = getChildViewHolderInt(view);
884                    if (vh != null) {
885                        if (vh.isTmpDetached() && !vh.shouldIgnore()) {
886                            throw new IllegalArgumentException("called detach on an already"
887                                    + " detached child " + vh + exceptionLabel());
888                        }
889                        if (DEBUG) {
890                            Log.d(TAG, "tmpDetach " + vh);
891                        }
892                        vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
893                    }
894                }
895                RecyclerView.this.detachViewFromParent(offset);
896            }
897
898            @Override
899            public void onEnteredHiddenState(View child) {
900                final ViewHolder vh = getChildViewHolderInt(child);
901                if (vh != null) {
902                    vh.onEnteredHiddenState(RecyclerView.this);
903                }
904            }
905
906            @Override
907            public void onLeftHiddenState(View child) {
908                final ViewHolder vh = getChildViewHolderInt(child);
909                if (vh != null) {
910                    vh.onLeftHiddenState(RecyclerView.this);
911                }
912            }
913        });
914    }
915
916    void initAdapterManager() {
917        mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
918            @Override
919            public ViewHolder findViewHolder(int position) {
920                final ViewHolder vh = findViewHolderForPosition(position, true);
921                if (vh == null) {
922                    return null;
923                }
924                // ensure it is not hidden because for adapter helper, the only thing matter is that
925                // LM thinks view is a child.
926                if (mChildHelper.isHidden(vh.itemView)) {
927                    if (DEBUG) {
928                        Log.d(TAG, "assuming view holder cannot be find because it is hidden");
929                    }
930                    return null;
931                }
932                return vh;
933            }
934
935            @Override
936            public void offsetPositionsForRemovingInvisible(int start, int count) {
937                offsetPositionRecordsForRemove(start, count, true);
938                mItemsAddedOrRemoved = true;
939                mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
940            }
941
942            @Override
943            public void offsetPositionsForRemovingLaidOutOrNewView(
944                    int positionStart, int itemCount) {
945                offsetPositionRecordsForRemove(positionStart, itemCount, false);
946                mItemsAddedOrRemoved = true;
947            }
948
949
950            @Override
951            public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
952                viewRangeUpdate(positionStart, itemCount, payload);
953                mItemsChanged = true;
954            }
955
956            @Override
957            public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
958                dispatchUpdate(op);
959            }
960
961            void dispatchUpdate(AdapterHelper.UpdateOp op) {
962                switch (op.cmd) {
963                    case AdapterHelper.UpdateOp.ADD:
964                        mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
965                        break;
966                    case AdapterHelper.UpdateOp.REMOVE:
967                        mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
968                        break;
969                    case AdapterHelper.UpdateOp.UPDATE:
970                        mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
971                                op.payload);
972                        break;
973                    case AdapterHelper.UpdateOp.MOVE:
974                        mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
975                        break;
976                }
977            }
978
979            @Override
980            public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
981                dispatchUpdate(op);
982            }
983
984            @Override
985            public void offsetPositionsForAdd(int positionStart, int itemCount) {
986                offsetPositionRecordsForInsert(positionStart, itemCount);
987                mItemsAddedOrRemoved = true;
988            }
989
990            @Override
991            public void offsetPositionsForMove(int from, int to) {
992                offsetPositionRecordsForMove(from, to);
993                // should we create mItemsMoved ?
994                mItemsAddedOrRemoved = true;
995            }
996        });
997    }
998
999    /**
1000     * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
1001     * size is not affected by the adapter contents. RecyclerView can still change its size based
1002     * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
1003     * size of its children or contents of its adapter (except the number of items in the adapter).
1004     * <p>
1005     * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
1006     * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
1007     *
1008     * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
1009     */
1010    public void setHasFixedSize(boolean hasFixedSize) {
1011        mHasFixedSize = hasFixedSize;
1012    }
1013
1014    /**
1015     * @return true if the app has specified that changes in adapter content cannot change
1016     * the size of the RecyclerView itself.
1017     */
1018    public boolean hasFixedSize() {
1019        return mHasFixedSize;
1020    }
1021
1022    @Override
1023    public void setClipToPadding(boolean clipToPadding) {
1024        if (clipToPadding != mClipToPadding) {
1025            invalidateGlows();
1026        }
1027        mClipToPadding = clipToPadding;
1028        super.setClipToPadding(clipToPadding);
1029        if (mFirstLayoutComplete) {
1030            requestLayout();
1031        }
1032    }
1033
1034    /**
1035     * Returns whether this RecyclerView will clip its children to its padding, and resize (but
1036     * not clip) any EdgeEffect to the padded region, if padding is present.
1037     * <p>
1038     * By default, children are clipped to the padding of their parent
1039     * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
1040     *
1041     * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
1042     *         clip) any EdgeEffect to the padded region, false otherwise.
1043     *
1044     * @attr name android:clipToPadding
1045     */
1046    @Override
1047    public boolean getClipToPadding() {
1048        return mClipToPadding;
1049    }
1050
1051    /**
1052     * Configure the scrolling touch slop for a specific use case.
1053     *
1054     * Set up the RecyclerView's scrolling motion threshold based on common usages.
1055     * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
1056     *
1057     * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
1058     *                     the intended usage of this RecyclerView
1059     */
1060    public void setScrollingTouchSlop(int slopConstant) {
1061        final ViewConfiguration vc = ViewConfiguration.get(getContext());
1062        switch (slopConstant) {
1063            default:
1064                Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
1065                        + slopConstant + "; using default value");
1066                // fall-through
1067            case TOUCH_SLOP_DEFAULT:
1068                mTouchSlop = vc.getScaledTouchSlop();
1069                break;
1070
1071            case TOUCH_SLOP_PAGING:
1072                mTouchSlop = vc.getScaledPagingTouchSlop();
1073                break;
1074        }
1075    }
1076
1077    /**
1078     * Swaps the current adapter with the provided one. It is similar to
1079     * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
1080     * {@link ViewHolder} and does not clear the RecycledViewPool.
1081     * <p>
1082     * Note that it still calls onAdapterChanged callbacks.
1083     *
1084     * @param adapter The new adapter to set, or null to set no adapter.
1085     * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
1086     *                                      Views. If adapters have stable ids and/or you want to
1087     *                                      animate the disappearing views, you may prefer to set
1088     *                                      this to false.
1089     * @see #setAdapter(Adapter)
1090     */
1091    public void swapAdapter(@Nullable Adapter adapter, boolean removeAndRecycleExistingViews) {
1092        // bail out if layout is frozen
1093        setLayoutFrozen(false);
1094        setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
1095        processDataSetCompletelyChanged(true);
1096        requestLayout();
1097    }
1098    /**
1099     * Set a new adapter to provide child views on demand.
1100     * <p>
1101     * When adapter is changed, all existing views are recycled back to the pool. If the pool has
1102     * only one adapter, it will be cleared.
1103     *
1104     * @param adapter The new adapter to set, or null to set no adapter.
1105     * @see #swapAdapter(Adapter, boolean)
1106     */
1107    public void setAdapter(@Nullable Adapter adapter) {
1108        // bail out if layout is frozen
1109        setLayoutFrozen(false);
1110        setAdapterInternal(adapter, false, true);
1111        processDataSetCompletelyChanged(false);
1112        requestLayout();
1113    }
1114
1115    /**
1116     * Removes and recycles all views - both those currently attached, and those in the Recycler.
1117     */
1118    void removeAndRecycleViews() {
1119        // end all running animations
1120        if (mItemAnimator != null) {
1121            mItemAnimator.endAnimations();
1122        }
1123        // Since animations are ended, mLayout.children should be equal to
1124        // recyclerView.children. This may not be true if item animator's end does not work as
1125        // expected. (e.g. not release children instantly). It is safer to use mLayout's child
1126        // count.
1127        if (mLayout != null) {
1128            mLayout.removeAndRecycleAllViews(mRecycler);
1129            mLayout.removeAndRecycleScrapInt(mRecycler);
1130        }
1131        // we should clear it here before adapters are swapped to ensure correct callbacks.
1132        mRecycler.clear();
1133    }
1134
1135    /**
1136     * Replaces the current adapter with the new one and triggers listeners.
1137     * @param adapter The new adapter
1138     * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
1139     *                               item types with the current adapter (helps us avoid cache
1140     *                               invalidation).
1141     * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
1142     *                               compatibleWithPrevious is false, this parameter is ignored.
1143     */
1144    private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
1145            boolean removeAndRecycleViews) {
1146        if (mAdapter != null) {
1147            mAdapter.unregisterAdapterDataObserver(mObserver);
1148            mAdapter.onDetachedFromRecyclerView(this);
1149        }
1150        if (!compatibleWithPrevious || removeAndRecycleViews) {
1151            removeAndRecycleViews();
1152        }
1153        mAdapterHelper.reset();
1154        final Adapter oldAdapter = mAdapter;
1155        mAdapter = adapter;
1156        if (adapter != null) {
1157            adapter.registerAdapterDataObserver(mObserver);
1158            adapter.onAttachedToRecyclerView(this);
1159        }
1160        if (mLayout != null) {
1161            mLayout.onAdapterChanged(oldAdapter, mAdapter);
1162        }
1163        mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
1164        mState.mStructureChanged = true;
1165    }
1166
1167    /**
1168     * Retrieves the previously set adapter or null if no adapter is set.
1169     *
1170     * @return The previously set adapter
1171     * @see #setAdapter(Adapter)
1172     */
1173    @Nullable
1174    public Adapter getAdapter() {
1175        return mAdapter;
1176    }
1177
1178    /**
1179     * Register a listener that will be notified whenever a child view is recycled.
1180     *
1181     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1182     * that a child view is no longer needed. If an application associates expensive
1183     * or heavyweight data with item views, this may be a good place to release
1184     * or free those resources.</p>
1185     *
1186     * @param listener Listener to register, or null to clear
1187     */
1188    public void setRecyclerListener(@Nullable RecyclerListener listener) {
1189        mRecyclerListener = listener;
1190    }
1191
1192    /**
1193     * <p>Return the offset of the RecyclerView's text baseline from the its top
1194     * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
1195     * this method returns -1.</p>
1196     *
1197     * @return the offset of the baseline within the RecyclerView's bounds or -1
1198     *         if baseline alignment is not supported
1199     */
1200    @Override
1201    public int getBaseline() {
1202        if (mLayout != null) {
1203            return mLayout.getBaseline();
1204        } else {
1205            return super.getBaseline();
1206        }
1207    }
1208
1209    /**
1210     * Register a listener that will be notified whenever a child view is attached to or detached
1211     * from RecyclerView.
1212     *
1213     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1214     * that a child view is no longer needed. If an application associates expensive
1215     * or heavyweight data with item views, this may be a good place to release
1216     * or free those resources.</p>
1217     *
1218     * @param listener Listener to register
1219     */
1220    public void addOnChildAttachStateChangeListener(
1221            @NonNull OnChildAttachStateChangeListener listener) {
1222        if (mOnChildAttachStateListeners == null) {
1223            mOnChildAttachStateListeners = new ArrayList<>();
1224        }
1225        mOnChildAttachStateListeners.add(listener);
1226    }
1227
1228    /**
1229     * Removes the provided listener from child attached state listeners list.
1230     *
1231     * @param listener Listener to unregister
1232     */
1233    public void removeOnChildAttachStateChangeListener(
1234            @NonNull OnChildAttachStateChangeListener listener) {
1235        if (mOnChildAttachStateListeners == null) {
1236            return;
1237        }
1238        mOnChildAttachStateListeners.remove(listener);
1239    }
1240
1241    /**
1242     * Removes all listeners that were added via
1243     * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
1244     */
1245    public void clearOnChildAttachStateChangeListeners() {
1246        if (mOnChildAttachStateListeners != null) {
1247            mOnChildAttachStateListeners.clear();
1248        }
1249    }
1250
1251    /**
1252     * Set the {@link LayoutManager} that this RecyclerView will use.
1253     *
1254     * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
1255     * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
1256     * layout arrangements for child views. These arrangements are controlled by the
1257     * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
1258     *
1259     * <p>Several default strategies are provided for common uses such as lists and grids.</p>
1260     *
1261     * @param layout LayoutManager to use
1262     */
1263    public void setLayoutManager(@Nullable LayoutManager layout) {
1264        if (layout == mLayout) {
1265            return;
1266        }
1267        stopScroll();
1268        // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
1269        // chance that LayoutManagers will re-use views.
1270        if (mLayout != null) {
1271            // end all running animations
1272            if (mItemAnimator != null) {
1273                mItemAnimator.endAnimations();
1274            }
1275            mLayout.removeAndRecycleAllViews(mRecycler);
1276            mLayout.removeAndRecycleScrapInt(mRecycler);
1277            mRecycler.clear();
1278
1279            if (mIsAttached) {
1280                mLayout.dispatchDetachedFromWindow(this, mRecycler);
1281            }
1282            mLayout.setRecyclerView(null);
1283            mLayout = null;
1284        } else {
1285            mRecycler.clear();
1286        }
1287        // this is just a defensive measure for faulty item animators.
1288        mChildHelper.removeAllViewsUnfiltered();
1289        mLayout = layout;
1290        if (layout != null) {
1291            if (layout.mRecyclerView != null) {
1292                throw new IllegalArgumentException("LayoutManager " + layout
1293                        + " is already attached to a RecyclerView:"
1294                        + layout.mRecyclerView.exceptionLabel());
1295            }
1296            mLayout.setRecyclerView(this);
1297            if (mIsAttached) {
1298                mLayout.dispatchAttachedToWindow(this);
1299            }
1300        }
1301        mRecycler.updateViewCacheSize();
1302        requestLayout();
1303    }
1304
1305    /**
1306     * Set a {@link OnFlingListener} for this {@link RecyclerView}.
1307     * <p>
1308     * If the {@link OnFlingListener} is set then it will receive
1309     * calls to {@link #fling(int,int)} and will be able to intercept them.
1310     *
1311     * @param onFlingListener The {@link OnFlingListener} instance.
1312     */
1313    public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
1314        mOnFlingListener = onFlingListener;
1315    }
1316
1317    /**
1318     * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
1319     *
1320     * @return The {@link OnFlingListener} instance currently set (can be null).
1321     */
1322    @Nullable
1323    public OnFlingListener getOnFlingListener() {
1324        return mOnFlingListener;
1325    }
1326
1327    @Override
1328    protected Parcelable onSaveInstanceState() {
1329        SavedState state = new SavedState(super.onSaveInstanceState());
1330        if (mPendingSavedState != null) {
1331            state.copyFrom(mPendingSavedState);
1332        } else if (mLayout != null) {
1333            state.mLayoutState = mLayout.onSaveInstanceState();
1334        } else {
1335            state.mLayoutState = null;
1336        }
1337
1338        return state;
1339    }
1340
1341    @Override
1342    protected void onRestoreInstanceState(Parcelable state) {
1343        if (!(state instanceof SavedState)) {
1344            super.onRestoreInstanceState(state);
1345            return;
1346        }
1347
1348        mPendingSavedState = (SavedState) state;
1349        super.onRestoreInstanceState(mPendingSavedState.getSuperState());
1350        if (mLayout != null && mPendingSavedState.mLayoutState != null) {
1351            mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
1352        }
1353    }
1354
1355    /**
1356     * Override to prevent freezing of any views created by the adapter.
1357     */
1358    @Override
1359    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1360        dispatchFreezeSelfOnly(container);
1361    }
1362
1363    /**
1364     * Override to prevent thawing of any views created by the adapter.
1365     */
1366    @Override
1367    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1368        dispatchThawSelfOnly(container);
1369    }
1370
1371    /**
1372     * Adds a view to the animatingViews list.
1373     * mAnimatingViews holds the child views that are currently being kept around
1374     * purely for the purpose of being animated out of view. They are drawn as a regular
1375     * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
1376     * as they are managed separately from the regular child views.
1377     * @param viewHolder The ViewHolder to be removed
1378     */
1379    private void addAnimatingView(ViewHolder viewHolder) {
1380        final View view = viewHolder.itemView;
1381        final boolean alreadyParented = view.getParent() == this;
1382        mRecycler.unscrapView(getChildViewHolder(view));
1383        if (viewHolder.isTmpDetached()) {
1384            // re-attach
1385            mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
1386        } else if (!alreadyParented) {
1387            mChildHelper.addView(view, true);
1388        } else {
1389            mChildHelper.hide(view);
1390        }
1391    }
1392
1393    /**
1394     * Removes a view from the animatingViews list.
1395     * @param view The view to be removed
1396     * @see #addAnimatingView(RecyclerView.ViewHolder)
1397     * @return true if an animating view is removed
1398     */
1399    boolean removeAnimatingView(View view) {
1400        startInterceptRequestLayout();
1401        final boolean removed = mChildHelper.removeViewIfHidden(view);
1402        if (removed) {
1403            final ViewHolder viewHolder = getChildViewHolderInt(view);
1404            mRecycler.unscrapView(viewHolder);
1405            mRecycler.recycleViewHolderInternal(viewHolder);
1406            if (DEBUG) {
1407                Log.d(TAG, "after removing animated view: " + view + ", " + this);
1408            }
1409        }
1410        // only clear request eaten flag if we removed the view.
1411        stopInterceptRequestLayout(!removed);
1412        return removed;
1413    }
1414
1415    /**
1416     * Return the {@link LayoutManager} currently responsible for
1417     * layout policy for this RecyclerView.
1418     *
1419     * @return The currently bound LayoutManager
1420     */
1421    @Nullable
1422    public LayoutManager getLayoutManager() {
1423        return mLayout;
1424    }
1425
1426    /**
1427     * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
1428     * if no pool is set for this view a new one will be created. See
1429     * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
1430     *
1431     * @return The pool used to store recycled item views for reuse.
1432     * @see #setRecycledViewPool(RecycledViewPool)
1433     */
1434    @NonNull
1435    public RecycledViewPool getRecycledViewPool() {
1436        return mRecycler.getRecycledViewPool();
1437    }
1438
1439    /**
1440     * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
1441     * This can be useful if you have multiple RecyclerViews with adapters that use the same
1442     * view types, for example if you have several data sets with the same kinds of item views
1443     * displayed by a {@link ViewPager ViewPager}.
1444     *
1445     * @param pool Pool to set. If this parameter is null a new pool will be created and used.
1446     */
1447    public void setRecycledViewPool(@Nullable RecycledViewPool pool) {
1448        mRecycler.setRecycledViewPool(pool);
1449    }
1450
1451    /**
1452     * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
1453     *
1454     * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
1455     *
1456     * @see ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)
1457     */
1458    public void setViewCacheExtension(@Nullable ViewCacheExtension extension) {
1459        mRecycler.setViewCacheExtension(extension);
1460    }
1461
1462    /**
1463     * Set the number of offscreen views to retain before adding them to the potentially shared
1464     * {@link #getRecycledViewPool() recycled view pool}.
1465     *
1466     * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
1467     * a LayoutManager to reuse those views unmodified without needing to return to the adapter
1468     * to rebind them.</p>
1469     *
1470     * @param size Number of views to cache offscreen before returning them to the general
1471     *             recycled view pool
1472     */
1473    public void setItemViewCacheSize(int size) {
1474        mRecycler.setViewCacheSize(size);
1475    }
1476
1477    /**
1478     * Return the current scrolling state of the RecyclerView.
1479     *
1480     * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
1481     * {@link #SCROLL_STATE_SETTLING}
1482     */
1483    public int getScrollState() {
1484        return mScrollState;
1485    }
1486
1487    void setScrollState(int state) {
1488        if (state == mScrollState) {
1489            return;
1490        }
1491        if (DEBUG) {
1492            Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
1493                    new Exception());
1494        }
1495        mScrollState = state;
1496        if (state != SCROLL_STATE_SETTLING) {
1497            stopScrollersInternal();
1498        }
1499        dispatchOnScrollStateChanged(state);
1500    }
1501
1502    /**
1503     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1504     * affect both measurement and drawing of individual item views.
1505     *
1506     * <p>Item decorations are ordered. Decorations placed earlier in the list will
1507     * be run/queried/drawn first for their effects on item views. Padding added to views
1508     * will be nested; a padding added by an earlier decoration will mean further
1509     * item decorations in the list will be asked to draw/pad within the previous decoration's
1510     * given area.</p>
1511     *
1512     * @param decor Decoration to add
1513     * @param index Position in the decoration chain to insert this decoration at. If this value
1514     *              is negative the decoration will be added at the end.
1515     */
1516    public void addItemDecoration(@NonNull ItemDecoration decor, int index) {
1517        if (mLayout != null) {
1518            mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
1519                    + " layout");
1520        }
1521        if (mItemDecorations.isEmpty()) {
1522            setWillNotDraw(false);
1523        }
1524        if (index < 0) {
1525            mItemDecorations.add(decor);
1526        } else {
1527            mItemDecorations.add(index, decor);
1528        }
1529        markItemDecorInsetsDirty();
1530        requestLayout();
1531    }
1532
1533    /**
1534     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1535     * affect both measurement and drawing of individual item views.
1536     *
1537     * <p>Item decorations are ordered. Decorations placed earlier in the list will
1538     * be run/queried/drawn first for their effects on item views. Padding added to views
1539     * will be nested; a padding added by an earlier decoration will mean further
1540     * item decorations in the list will be asked to draw/pad within the previous decoration's
1541     * given area.</p>
1542     *
1543     * @param decor Decoration to add
1544     */
1545    public void addItemDecoration(@NonNull ItemDecoration decor) {
1546        addItemDecoration(decor, -1);
1547    }
1548
1549    /**
1550     * Returns an {@link ItemDecoration} previously added to this RecyclerView.
1551     *
1552     * @param index The index position of the desired ItemDecoration.
1553     * @return the ItemDecoration at index position
1554     * @throws IndexOutOfBoundsException on invalid index
1555     */
1556    @NonNull
1557    public ItemDecoration getItemDecorationAt(int index) {
1558        final int size = getItemDecorationCount();
1559        if (index < 0 || index >= size) {
1560            throw new IndexOutOfBoundsException(index + " is an invalid index for size " + size);
1561        }
1562
1563        return mItemDecorations.get(index);
1564    }
1565
1566    /**
1567     * Returns the number of {@link ItemDecoration} currently added to this RecyclerView.
1568     *
1569     * @return number of ItemDecorations currently added added to this RecyclerView.
1570     */
1571    public int getItemDecorationCount() {
1572        return mItemDecorations.size();
1573    }
1574
1575    /**
1576     * Removes the {@link ItemDecoration} associated with the supplied index position.
1577     *
1578     * @param index The index position of the ItemDecoration to be removed.
1579     */
1580    public void removeItemDecorationAt(int index) {
1581        final int size = getItemDecorationCount();
1582        if (index < 0 || index >= size) {
1583            throw new IndexOutOfBoundsException(index + " is an invalid index for size " + size);
1584        }
1585
1586        removeItemDecoration(getItemDecorationAt(index));
1587    }
1588
1589    /**
1590     * Remove an {@link ItemDecoration} from this RecyclerView.
1591     *
1592     * <p>The given decoration will no longer impact the measurement and drawing of
1593     * item views.</p>
1594     *
1595     * @param decor Decoration to remove
1596     * @see #addItemDecoration(ItemDecoration)
1597     */
1598    public void removeItemDecoration(@NonNull ItemDecoration decor) {
1599        if (mLayout != null) {
1600            mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
1601                    + " layout");
1602        }
1603        mItemDecorations.remove(decor);
1604        if (mItemDecorations.isEmpty()) {
1605            setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
1606        }
1607        markItemDecorInsetsDirty();
1608        requestLayout();
1609    }
1610
1611    /**
1612     * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
1613     * <p>
1614     * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
1615     * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
1616     * true if childDrawingOrderCallback is not null, false otherwise.
1617     * <p>
1618     * Note that child drawing order may be overridden by View's elevation.
1619     *
1620     * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
1621     *                                  system.
1622     */
1623    public void setChildDrawingOrderCallback(
1624            @Nullable ChildDrawingOrderCallback childDrawingOrderCallback) {
1625        if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
1626            return;
1627        }
1628        mChildDrawingOrderCallback = childDrawingOrderCallback;
1629        setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
1630    }
1631
1632    /**
1633     * Set a listener that will be notified of any changes in scroll state or position.
1634     *
1635     * @param listener Listener to set or null to clear
1636     *
1637     * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
1638     *             {@link #removeOnScrollListener(OnScrollListener)}
1639     */
1640    @Deprecated
1641    public void setOnScrollListener(@Nullable OnScrollListener listener) {
1642        mScrollListener = listener;
1643    }
1644
1645    /**
1646     * Add a listener that will be notified of any changes in scroll state or position.
1647     *
1648     * <p>Components that add a listener should take care to remove it when finished.
1649     * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
1650     * to remove all attached listeners.</p>
1651     *
1652     * @param listener listener to set
1653     */
1654    public void addOnScrollListener(@NonNull OnScrollListener listener) {
1655        if (mScrollListeners == null) {
1656            mScrollListeners = new ArrayList<>();
1657        }
1658        mScrollListeners.add(listener);
1659    }
1660
1661    /**
1662     * Remove a listener that was notified of any changes in scroll state or position.
1663     *
1664     * @param listener listener to set or null to clear
1665     */
1666    public void removeOnScrollListener(@NonNull OnScrollListener listener) {
1667        if (mScrollListeners != null) {
1668            mScrollListeners.remove(listener);
1669        }
1670    }
1671
1672    /**
1673     * Remove all secondary listener that were notified of any changes in scroll state or position.
1674     */
1675    public void clearOnScrollListeners() {
1676        if (mScrollListeners != null) {
1677            mScrollListeners.clear();
1678        }
1679    }
1680
1681    /**
1682     * Convenience method to scroll to a certain position.
1683     *
1684     * RecyclerView does not implement scrolling logic, rather forwards the call to
1685     * {@link RecyclerView.LayoutManager#scrollToPosition(int)}
1686     * @param position Scroll to this adapter position
1687     * @see RecyclerView.LayoutManager#scrollToPosition(int)
1688     */
1689    public void scrollToPosition(int position) {
1690        if (mLayoutFrozen) {
1691            return;
1692        }
1693        stopScroll();
1694        if (mLayout == null) {
1695            Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
1696                    + "Call setLayoutManager with a non-null argument.");
1697            return;
1698        }
1699        mLayout.scrollToPosition(position);
1700        awakenScrollBars();
1701    }
1702
1703    void jumpToPositionForSmoothScroller(int position) {
1704        if (mLayout == null) {
1705            return;
1706        }
1707        mLayout.scrollToPosition(position);
1708        awakenScrollBars();
1709    }
1710
1711    /**
1712     * Starts a smooth scroll to an adapter position.
1713     * <p>
1714     * To support smooth scrolling, you must override
1715     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
1716     * {@link SmoothScroller}.
1717     * <p>
1718     * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
1719     * provide a custom smooth scroll logic, override
1720     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
1721     * LayoutManager.
1722     *
1723     * @param position The adapter position to scroll to
1724     * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
1725     */
1726    public void smoothScrollToPosition(int position) {
1727        if (mLayoutFrozen) {
1728            return;
1729        }
1730        if (mLayout == null) {
1731            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
1732                    + "Call setLayoutManager with a non-null argument.");
1733            return;
1734        }
1735        mLayout.smoothScrollToPosition(this, mState, position);
1736    }
1737
1738    @Override
1739    public void scrollTo(int x, int y) {
1740        Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
1741                + "Use scrollToPosition instead");
1742    }
1743
1744    @Override
1745    public void scrollBy(int x, int y) {
1746        if (mLayout == null) {
1747            Log.e(TAG, "Cannot scroll without a LayoutManager set. "
1748                    + "Call setLayoutManager with a non-null argument.");
1749            return;
1750        }
1751        if (mLayoutFrozen) {
1752            return;
1753        }
1754        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1755        final boolean canScrollVertical = mLayout.canScrollVertically();
1756        if (canScrollHorizontal || canScrollVertical) {
1757            scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
1758        }
1759    }
1760
1761    /**
1762     * Scrolls the RV by 'dx' and 'dy' via calls to
1763     * {@link LayoutManager#scrollHorizontallyBy(int, Recycler, State)} and
1764     * {@link LayoutManager#scrollVerticallyBy(int, Recycler, State)}.
1765     *
1766     * Also sets how much of the scroll was actually consumed in 'consumed' parameter (indexes 0 and
1767     * 1 for the x axis and y axis, respectively).
1768     *
1769     * This method should only be called in the context of an existing scroll operation such that
1770     * any other necessary operations (such as a call to {@link #consumePendingUpdateOperations()})
1771     * is already handled.
1772     */
1773    private void scrollStep(int dx, int dy, @Nullable int[] consumed) {
1774        startInterceptRequestLayout();
1775        onEnterLayoutOrScroll();
1776
1777        TraceCompat.beginSection(TRACE_SCROLL_TAG);
1778        fillRemainingScrollValues(mState);
1779
1780        int consumedX = 0;
1781        int consumedY = 0;
1782        if (dx != 0) {
1783            consumedX = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
1784        }
1785        if (dy != 0) {
1786            consumedY = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
1787        }
1788
1789        TraceCompat.endSection();
1790        repositionShadowingViews();
1791
1792        onExitLayoutOrScroll();
1793        stopInterceptRequestLayout(false);
1794
1795        if (consumed != null) {
1796            consumed[0] = consumedX;
1797            consumed[1] = consumedY;
1798        }
1799    }
1800
1801    /**
1802     * Helper method reflect data changes to the state.
1803     * <p>
1804     * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
1805     * but data actually changed.
1806     * <p>
1807     * This method consumes all deferred changes to avoid that case.
1808     */
1809    void consumePendingUpdateOperations() {
1810        if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
1811            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1812            dispatchLayout();
1813            TraceCompat.endSection();
1814            return;
1815        }
1816        if (!mAdapterHelper.hasPendingUpdates()) {
1817            return;
1818        }
1819
1820        // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
1821        // of the visible items is affected and if not, just ignore the change.
1822        if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
1823                .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
1824                        | AdapterHelper.UpdateOp.MOVE)) {
1825            TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
1826            startInterceptRequestLayout();
1827            onEnterLayoutOrScroll();
1828            mAdapterHelper.preProcess();
1829            if (!mLayoutWasDefered) {
1830                if (hasUpdatedView()) {
1831                    dispatchLayout();
1832                } else {
1833                    // no need to layout, clean state
1834                    mAdapterHelper.consumePostponedUpdates();
1835                }
1836            }
1837            stopInterceptRequestLayout(true);
1838            onExitLayoutOrScroll();
1839            TraceCompat.endSection();
1840        } else if (mAdapterHelper.hasPendingUpdates()) {
1841            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1842            dispatchLayout();
1843            TraceCompat.endSection();
1844        }
1845    }
1846
1847    /**
1848     * @return True if an existing view holder needs to be updated
1849     */
1850    private boolean hasUpdatedView() {
1851        final int childCount = mChildHelper.getChildCount();
1852        for (int i = 0; i < childCount; i++) {
1853            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
1854            if (holder == null || holder.shouldIgnore()) {
1855                continue;
1856            }
1857            if (holder.isUpdated()) {
1858                return true;
1859            }
1860        }
1861        return false;
1862    }
1863
1864    /**
1865     * Does not perform bounds checking. Used by internal methods that have already validated input.
1866     * <p>
1867     * It also reports any unused scroll request to the related EdgeEffect.
1868     *
1869     * @param x The amount of horizontal scroll request
1870     * @param y The amount of vertical scroll request
1871     * @param ev The originating MotionEvent, or null if not from a touch event.
1872     *
1873     * @return Whether any scroll was consumed in either direction.
1874     */
1875    boolean scrollByInternal(int x, int y, MotionEvent ev) {
1876        int unconsumedX = 0, unconsumedY = 0;
1877        int consumedX = 0, consumedY = 0;
1878
1879        consumePendingUpdateOperations();
1880        if (mAdapter != null) {
1881            scrollStep(x, y, mScrollStepConsumed);
1882            consumedX = mScrollStepConsumed[0];
1883            consumedY = mScrollStepConsumed[1];
1884            unconsumedX = x - consumedX;
1885            unconsumedY = y - consumedY;
1886        }
1887        if (!mItemDecorations.isEmpty()) {
1888            invalidate();
1889        }
1890
1891        if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset,
1892                TYPE_TOUCH)) {
1893            // Update the last touch co-ords, taking any scroll offset into account
1894            mLastTouchX -= mScrollOffset[0];
1895            mLastTouchY -= mScrollOffset[1];
1896            if (ev != null) {
1897                ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
1898            }
1899            mNestedOffsets[0] += mScrollOffset[0];
1900            mNestedOffsets[1] += mScrollOffset[1];
1901        } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
1902            if (ev != null && !MotionEventCompat.isFromSource(ev, InputDevice.SOURCE_MOUSE)) {
1903                pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
1904            }
1905            considerReleasingGlowsOnScroll(x, y);
1906        }
1907        if (consumedX != 0 || consumedY != 0) {
1908            dispatchOnScrolled(consumedX, consumedY);
1909        }
1910        if (!awakenScrollBars()) {
1911            invalidate();
1912        }
1913        return consumedX != 0 || consumedY != 0;
1914    }
1915
1916    /**
1917     * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
1918     * range. This value is used to compute the length of the thumb within the scrollbar's track.
1919     * </p>
1920     *
1921     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1922     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
1923     *
1924     * <p>Default implementation returns 0.</p>
1925     *
1926     * <p>If you want to support scroll bars, override
1927     * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
1928     * LayoutManager. </p>
1929     *
1930     * @return The horizontal offset of the scrollbar's thumb
1931     * @see RecyclerView.LayoutManager#computeHorizontalScrollOffset
1932     * (RecyclerView.State)
1933     */
1934    @Override
1935    public int computeHorizontalScrollOffset() {
1936        if (mLayout == null) {
1937            return 0;
1938        }
1939        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
1940    }
1941
1942    /**
1943     * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
1944     * horizontal range. This value is used to compute the length of the thumb within the
1945     * scrollbar's track.</p>
1946     *
1947     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1948     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
1949     *
1950     * <p>Default implementation returns 0.</p>
1951     *
1952     * <p>If you want to support scroll bars, override
1953     * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
1954     * LayoutManager.</p>
1955     *
1956     * @return The horizontal extent of the scrollbar's thumb
1957     * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
1958     */
1959    @Override
1960    public int computeHorizontalScrollExtent() {
1961        if (mLayout == null) {
1962            return 0;
1963        }
1964        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
1965    }
1966
1967    /**
1968     * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
1969     *
1970     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1971     * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
1972     *
1973     * <p>Default implementation returns 0.</p>
1974     *
1975     * <p>If you want to support scroll bars, override
1976     * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
1977     * LayoutManager.</p>
1978     *
1979     * @return The total horizontal range represented by the vertical scrollbar
1980     * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
1981     */
1982    @Override
1983    public int computeHorizontalScrollRange() {
1984        if (mLayout == null) {
1985            return 0;
1986        }
1987        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
1988    }
1989
1990    /**
1991     * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
1992     * This value is used to compute the length of the thumb within the scrollbar's track. </p>
1993     *
1994     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1995     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
1996     *
1997     * <p>Default implementation returns 0.</p>
1998     *
1999     * <p>If you want to support scroll bars, override
2000     * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
2001     * LayoutManager.</p>
2002     *
2003     * @return The vertical offset of the scrollbar's thumb
2004     * @see RecyclerView.LayoutManager#computeVerticalScrollOffset
2005     * (RecyclerView.State)
2006     */
2007    @Override
2008    public int computeVerticalScrollOffset() {
2009        if (mLayout == null) {
2010            return 0;
2011        }
2012        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
2013    }
2014
2015    /**
2016     * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
2017     * This value is used to compute the length of the thumb within the scrollbar's track.</p>
2018     *
2019     * <p>The range is expressed in arbitrary units that must be the same as the units used by
2020     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
2021     *
2022     * <p>Default implementation returns 0.</p>
2023     *
2024     * <p>If you want to support scroll bars, override
2025     * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
2026     * LayoutManager.</p>
2027     *
2028     * @return The vertical extent of the scrollbar's thumb
2029     * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
2030     */
2031    @Override
2032    public int computeVerticalScrollExtent() {
2033        if (mLayout == null) {
2034            return 0;
2035        }
2036        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
2037    }
2038
2039    /**
2040     * <p>Compute the vertical range that the vertical scrollbar represents.</p>
2041     *
2042     * <p>The range is expressed in arbitrary units that must be the same as the units used by
2043     * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
2044     *
2045     * <p>Default implementation returns 0.</p>
2046     *
2047     * <p>If you want to support scroll bars, override
2048     * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
2049     * LayoutManager.</p>
2050     *
2051     * @return The total vertical range represented by the vertical scrollbar
2052     * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
2053     */
2054    @Override
2055    public int computeVerticalScrollRange() {
2056        if (mLayout == null) {
2057            return 0;
2058        }
2059        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
2060    }
2061
2062    /**
2063     * This method should be called before any code that may trigger a child view to cause a call to
2064     * {@link RecyclerView#requestLayout()}.  Doing so enables {@link RecyclerView} to avoid
2065     * reacting to additional redundant calls to {@link #requestLayout()}.
2066     * <p>
2067     * A call to this method must always be accompanied by a call to
2068     * {@link #stopInterceptRequestLayout(boolean)} that follows the code that may trigger a
2069     * child View to cause a call to {@link RecyclerView#requestLayout()}.
2070     *
2071     * @see #stopInterceptRequestLayout(boolean)
2072     */
2073    void startInterceptRequestLayout() {
2074        mInterceptRequestLayoutDepth++;
2075        if (mInterceptRequestLayoutDepth == 1 && !mLayoutFrozen) {
2076            mLayoutWasDefered = false;
2077        }
2078    }
2079
2080    /**
2081     * This method should be called after any code that may trigger a child view to cause a call to
2082     * {@link RecyclerView#requestLayout()}.
2083     * <p>
2084     * A call to this method must always be accompanied by a call to
2085     * {@link #startInterceptRequestLayout()} that precedes the code that may trigger a child
2086     * View to cause a call to {@link RecyclerView#requestLayout()}.
2087     *
2088     * @see #startInterceptRequestLayout()
2089     */
2090    void stopInterceptRequestLayout(boolean performLayoutChildren) {
2091        if (mInterceptRequestLayoutDepth < 1) {
2092            //noinspection PointlessBooleanExpression
2093            if (DEBUG) {
2094                throw new IllegalStateException("stopInterceptRequestLayout was called more "
2095                        + "times than startInterceptRequestLayout."
2096                        + exceptionLabel());
2097            }
2098            mInterceptRequestLayoutDepth = 1;
2099        }
2100        if (!performLayoutChildren && !mLayoutFrozen) {
2101            // Reset the layout request eaten counter.
2102            // This is necessary since eatRequest calls can be nested in which case the other
2103            // call will override the inner one.
2104            // for instance:
2105            // eat layout for process adapter updates
2106            //   eat layout for dispatchLayout
2107            //     a bunch of req layout calls arrive
2108
2109            mLayoutWasDefered = false;
2110        }
2111        if (mInterceptRequestLayoutDepth == 1) {
2112            // when layout is frozen we should delay dispatchLayout()
2113            if (performLayoutChildren && mLayoutWasDefered && !mLayoutFrozen
2114                    && mLayout != null && mAdapter != null) {
2115                dispatchLayout();
2116            }
2117            if (!mLayoutFrozen) {
2118                mLayoutWasDefered = false;
2119            }
2120        }
2121        mInterceptRequestLayoutDepth--;
2122    }
2123
2124    /**
2125     * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
2126     * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
2127     * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
2128     * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
2129     * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
2130     * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
2131     * called.
2132     *
2133     * <p>
2134     * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
2135     * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
2136     * RecyclerView, State, int)}.
2137     * <p>
2138     * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
2139     * stop frozen.
2140     * <p>
2141     * Note: Running ItemAnimator is not stopped automatically,  it's caller's
2142     * responsibility to call ItemAnimator.end().
2143     *
2144     * @param frozen   true to freeze layout and scroll, false to re-enable.
2145     */
2146    public void setLayoutFrozen(boolean frozen) {
2147        if (frozen != mLayoutFrozen) {
2148            assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
2149            if (!frozen) {
2150                mLayoutFrozen = false;
2151                if (mLayoutWasDefered && mLayout != null && mAdapter != null) {
2152                    requestLayout();
2153                }
2154                mLayoutWasDefered = false;
2155            } else {
2156                final long now = SystemClock.uptimeMillis();
2157                MotionEvent cancelEvent = MotionEvent.obtain(now, now,
2158                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2159                onTouchEvent(cancelEvent);
2160                mLayoutFrozen = true;
2161                mIgnoreMotionEventTillDown = true;
2162                stopScroll();
2163            }
2164        }
2165    }
2166
2167    /**
2168     * Returns true if layout and scroll are frozen.
2169     *
2170     * @return true if layout and scroll are frozen
2171     * @see #setLayoutFrozen(boolean)
2172     */
2173    public boolean isLayoutFrozen() {
2174        return mLayoutFrozen;
2175    }
2176
2177    /**
2178     * Animate a scroll by the given amount of pixels along either axis.
2179     *
2180     * @param dx Pixels to scroll horizontally
2181     * @param dy Pixels to scroll vertically
2182     */
2183    public void smoothScrollBy(@Px int dx, @Px int dy) {
2184        smoothScrollBy(dx, dy, null);
2185    }
2186
2187    /**
2188     * Animate a scroll by the given amount of pixels along either axis.
2189     *
2190     * @param dx Pixels to scroll horizontally
2191     * @param dy Pixels to scroll vertically
2192     * @param interpolator {@link Interpolator} to be used for scrolling. If it is
2193     *                     {@code null}, RecyclerView is going to use the default interpolator.
2194     */
2195    public void smoothScrollBy(@Px int dx, @Px int dy, @Nullable Interpolator interpolator) {
2196        if (mLayout == null) {
2197            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
2198                    + "Call setLayoutManager with a non-null argument.");
2199            return;
2200        }
2201        if (mLayoutFrozen) {
2202            return;
2203        }
2204        if (!mLayout.canScrollHorizontally()) {
2205            dx = 0;
2206        }
2207        if (!mLayout.canScrollVertically()) {
2208            dy = 0;
2209        }
2210        if (dx != 0 || dy != 0) {
2211            mViewFlinger.smoothScrollBy(dx, dy, interpolator);
2212        }
2213    }
2214
2215    /**
2216     * Begin a standard fling with an initial velocity along each axis in pixels per second.
2217     * If the velocity given is below the system-defined minimum this method will return false
2218     * and no fling will occur.
2219     *
2220     * @param velocityX Initial horizontal velocity in pixels per second
2221     * @param velocityY Initial vertical velocity in pixels per second
2222     * @return true if the fling was started, false if the velocity was too low to fling or
2223     * LayoutManager does not support scrolling in the axis fling is issued.
2224     *
2225     * @see LayoutManager#canScrollVertically()
2226     * @see LayoutManager#canScrollHorizontally()
2227     */
2228    public boolean fling(int velocityX, int velocityY) {
2229        if (mLayout == null) {
2230            Log.e(TAG, "Cannot fling without a LayoutManager set. "
2231                    + "Call setLayoutManager with a non-null argument.");
2232            return false;
2233        }
2234        if (mLayoutFrozen) {
2235            return false;
2236        }
2237
2238        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
2239        final boolean canScrollVertical = mLayout.canScrollVertically();
2240
2241        if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
2242            velocityX = 0;
2243        }
2244        if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
2245            velocityY = 0;
2246        }
2247        if (velocityX == 0 && velocityY == 0) {
2248            // If we don't have any velocity, return false
2249            return false;
2250        }
2251
2252        if (!dispatchNestedPreFling(velocityX, velocityY)) {
2253            final boolean canScroll = canScrollHorizontal || canScrollVertical;
2254            dispatchNestedFling(velocityX, velocityY, canScroll);
2255
2256            if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
2257                return true;
2258            }
2259
2260            if (canScroll) {
2261                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2262                if (canScrollHorizontal) {
2263                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2264                }
2265                if (canScrollVertical) {
2266                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2267                }
2268                startNestedScroll(nestedScrollAxis, TYPE_NON_TOUCH);
2269
2270                velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
2271                velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
2272                mViewFlinger.fling(velocityX, velocityY);
2273                return true;
2274            }
2275        }
2276        return false;
2277    }
2278
2279    /**
2280     * Stop any current scroll in progress, such as one started by
2281     * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
2282     */
2283    public void stopScroll() {
2284        setScrollState(SCROLL_STATE_IDLE);
2285        stopScrollersInternal();
2286    }
2287
2288    /**
2289     * Similar to {@link #stopScroll()} but does not set the state.
2290     */
2291    private void stopScrollersInternal() {
2292        mViewFlinger.stop();
2293        if (mLayout != null) {
2294            mLayout.stopSmoothScroller();
2295        }
2296    }
2297
2298    /**
2299     * Returns the minimum velocity to start a fling.
2300     *
2301     * @return The minimum velocity to start a fling
2302     */
2303    public int getMinFlingVelocity() {
2304        return mMinFlingVelocity;
2305    }
2306
2307
2308    /**
2309     * Returns the maximum fling velocity used by this RecyclerView.
2310     *
2311     * @return The maximum fling velocity used by this RecyclerView.
2312     */
2313    public int getMaxFlingVelocity() {
2314        return mMaxFlingVelocity;
2315    }
2316
2317    /**
2318     * Apply a pull to relevant overscroll glow effects
2319     */
2320    private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
2321        boolean invalidate = false;
2322        if (overscrollX < 0) {
2323            ensureLeftGlow();
2324            EdgeEffectCompat.onPull(mLeftGlow, -overscrollX / getWidth(), 1f - y  / getHeight());
2325            invalidate = true;
2326        } else if (overscrollX > 0) {
2327            ensureRightGlow();
2328            EdgeEffectCompat.onPull(mRightGlow, overscrollX / getWidth(), y / getHeight());
2329            invalidate = true;
2330        }
2331
2332        if (overscrollY < 0) {
2333            ensureTopGlow();
2334            EdgeEffectCompat.onPull(mTopGlow, -overscrollY / getHeight(), x / getWidth());
2335            invalidate = true;
2336        } else if (overscrollY > 0) {
2337            ensureBottomGlow();
2338            EdgeEffectCompat.onPull(mBottomGlow, overscrollY / getHeight(), 1f - x / getWidth());
2339            invalidate = true;
2340        }
2341
2342        if (invalidate || overscrollX != 0 || overscrollY != 0) {
2343            ViewCompat.postInvalidateOnAnimation(this);
2344        }
2345    }
2346
2347    private void releaseGlows() {
2348        boolean needsInvalidate = false;
2349        if (mLeftGlow != null) {
2350            mLeftGlow.onRelease();
2351            needsInvalidate = mLeftGlow.isFinished();
2352        }
2353        if (mTopGlow != null) {
2354            mTopGlow.onRelease();
2355            needsInvalidate |= mTopGlow.isFinished();
2356        }
2357        if (mRightGlow != null) {
2358            mRightGlow.onRelease();
2359            needsInvalidate |= mRightGlow.isFinished();
2360        }
2361        if (mBottomGlow != null) {
2362            mBottomGlow.onRelease();
2363            needsInvalidate |= mBottomGlow.isFinished();
2364        }
2365        if (needsInvalidate) {
2366            ViewCompat.postInvalidateOnAnimation(this);
2367        }
2368    }
2369
2370    void considerReleasingGlowsOnScroll(int dx, int dy) {
2371        boolean needsInvalidate = false;
2372        if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
2373            mLeftGlow.onRelease();
2374            needsInvalidate = mLeftGlow.isFinished();
2375        }
2376        if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
2377            mRightGlow.onRelease();
2378            needsInvalidate |= mRightGlow.isFinished();
2379        }
2380        if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
2381            mTopGlow.onRelease();
2382            needsInvalidate |= mTopGlow.isFinished();
2383        }
2384        if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
2385            mBottomGlow.onRelease();
2386            needsInvalidate |= mBottomGlow.isFinished();
2387        }
2388        if (needsInvalidate) {
2389            ViewCompat.postInvalidateOnAnimation(this);
2390        }
2391    }
2392
2393    void absorbGlows(int velocityX, int velocityY) {
2394        if (velocityX < 0) {
2395            ensureLeftGlow();
2396            mLeftGlow.onAbsorb(-velocityX);
2397        } else if (velocityX > 0) {
2398            ensureRightGlow();
2399            mRightGlow.onAbsorb(velocityX);
2400        }
2401
2402        if (velocityY < 0) {
2403            ensureTopGlow();
2404            mTopGlow.onAbsorb(-velocityY);
2405        } else if (velocityY > 0) {
2406            ensureBottomGlow();
2407            mBottomGlow.onAbsorb(velocityY);
2408        }
2409
2410        if (velocityX != 0 || velocityY != 0) {
2411            ViewCompat.postInvalidateOnAnimation(this);
2412        }
2413    }
2414
2415    void ensureLeftGlow() {
2416        if (mLeftGlow != null) {
2417            return;
2418        }
2419        mLeftGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_LEFT);
2420        if (mClipToPadding) {
2421            mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2422                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2423        } else {
2424            mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2425        }
2426    }
2427
2428    void ensureRightGlow() {
2429        if (mRightGlow != null) {
2430            return;
2431        }
2432        mRightGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_RIGHT);
2433        if (mClipToPadding) {
2434            mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2435                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2436        } else {
2437            mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2438        }
2439    }
2440
2441    void ensureTopGlow() {
2442        if (mTopGlow != null) {
2443            return;
2444        }
2445        mTopGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_TOP);
2446        if (mClipToPadding) {
2447            mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2448                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2449        } else {
2450            mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2451        }
2452
2453    }
2454
2455    void ensureBottomGlow() {
2456        if (mBottomGlow != null) {
2457            return;
2458        }
2459        mBottomGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_BOTTOM);
2460        if (mClipToPadding) {
2461            mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2462                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2463        } else {
2464            mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2465        }
2466    }
2467
2468    void invalidateGlows() {
2469        mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
2470    }
2471
2472    /**
2473     * Set a {@link EdgeEffectFactory} for this {@link RecyclerView}.
2474     * <p>
2475     * When a new {@link EdgeEffectFactory} is set, any existing over-scroll effects are cleared
2476     * and new effects are created as needed using
2477     * {@link EdgeEffectFactory#createEdgeEffect(RecyclerView, int)}
2478     *
2479     * @param edgeEffectFactory The {@link EdgeEffectFactory} instance.
2480     */
2481    public void setEdgeEffectFactory(@NonNull EdgeEffectFactory edgeEffectFactory) {
2482        Preconditions.checkNotNull(edgeEffectFactory);
2483        mEdgeEffectFactory = edgeEffectFactory;
2484        invalidateGlows();
2485    }
2486
2487    /**
2488     * Retrieves the previously set {@link EdgeEffectFactory} or the default factory if nothing
2489     * was set.
2490     *
2491     * @return The previously set {@link EdgeEffectFactory}
2492     * @see #setEdgeEffectFactory(EdgeEffectFactory)
2493     */
2494    @NonNull
2495    public EdgeEffectFactory getEdgeEffectFactory() {
2496        return mEdgeEffectFactory;
2497    }
2498
2499    /**
2500     * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
2501     * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
2502     * that differs from other ViewGroups.
2503     * <p>
2504     * It first does a focus search within the RecyclerView. If this search finds a View that is in
2505     * the focus direction with respect to the currently focused View, RecyclerView returns that
2506     * child as the next focus target. When it cannot find such child, it calls
2507     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
2508     * in the focus search direction. If LayoutManager adds a View that matches the
2509     * focus search criteria, it will be returned as the focus search result. Otherwise,
2510     * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
2511     * <p>
2512     * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
2513     * is not in the focus direction is still valid focus target which may not be the desired
2514     * behavior if the Adapter has more children in the focus direction. To handle this case,
2515     * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
2516     * focus search in that direction. If there are no Views to gain focus, it will call
2517     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
2518     * focus search with the original (relative) direction. This allows RecyclerView to provide
2519     * better candidates to the focus search while still allowing the view system to take focus from
2520     * the RecyclerView and give it to a more suitable child if such child exists.
2521     *
2522     * @param focused The view that currently has focus
2523     * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
2524     * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
2525     * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
2526     *
2527     * @return A new View that can be the next focus after the focused View
2528     */
2529    @Override
2530    public View focusSearch(View focused, int direction) {
2531        View result = mLayout.onInterceptFocusSearch(focused, direction);
2532        if (result != null) {
2533            return result;
2534        }
2535        final boolean canRunFocusFailure = mAdapter != null && mLayout != null
2536                && !isComputingLayout() && !mLayoutFrozen;
2537
2538        final FocusFinder ff = FocusFinder.getInstance();
2539        if (canRunFocusFailure
2540                && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
2541            // convert direction to absolute direction and see if we have a view there and if not
2542            // tell LayoutManager to add if it can.
2543            boolean needsFocusFailureLayout = false;
2544            if (mLayout.canScrollVertically()) {
2545                final int absDir =
2546                        direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
2547                final View found = ff.findNextFocus(this, focused, absDir);
2548                needsFocusFailureLayout = found == null;
2549                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2550                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2551                    direction = absDir;
2552                }
2553            }
2554            if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
2555                boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
2556                final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
2557                        ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2558                final View found = ff.findNextFocus(this, focused, absDir);
2559                needsFocusFailureLayout = found == null;
2560                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2561                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2562                    direction = absDir;
2563                }
2564            }
2565            if (needsFocusFailureLayout) {
2566                consumePendingUpdateOperations();
2567                final View focusedItemView = findContainingItemView(focused);
2568                if (focusedItemView == null) {
2569                    // panic, focused view is not a child anymore, cannot call super.
2570                    return null;
2571                }
2572                startInterceptRequestLayout();
2573                mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2574                stopInterceptRequestLayout(false);
2575            }
2576            result = ff.findNextFocus(this, focused, direction);
2577        } else {
2578            result = ff.findNextFocus(this, focused, direction);
2579            if (result == null && canRunFocusFailure) {
2580                consumePendingUpdateOperations();
2581                final View focusedItemView = findContainingItemView(focused);
2582                if (focusedItemView == null) {
2583                    // panic, focused view is not a child anymore, cannot call super.
2584                    return null;
2585                }
2586                startInterceptRequestLayout();
2587                result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2588                stopInterceptRequestLayout(false);
2589            }
2590        }
2591        if (result != null && !result.hasFocusable()) {
2592            if (getFocusedChild() == null) {
2593                // Scrolling to this unfocusable view is not meaningful since there is no currently
2594                // focused view which RV needs to keep visible.
2595                return super.focusSearch(focused, direction);
2596            }
2597            // If the next view returned by onFocusSearchFailed in layout manager has no focusable
2598            // views, we still scroll to that view in order to make it visible on the screen.
2599            // If it's focusable, framework already calls RV's requestChildFocus which handles
2600            // bringing this newly focused item onto the screen.
2601            requestChildOnScreen(result, null);
2602            return focused;
2603        }
2604        return isPreferredNextFocus(focused, result, direction)
2605                ? result : super.focusSearch(focused, direction);
2606    }
2607
2608    /**
2609     * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
2610     * assign it as the next focus View instead of letting view hierarchy decide.
2611     * A good candidate means a View that is aligned in the focus direction wrt the focused View
2612     * and is not the RecyclerView itself.
2613     * When this method returns false, RecyclerView will let the parent make the decision so the
2614     * same View may still get the focus as a result of that search.
2615     */
2616    private boolean isPreferredNextFocus(View focused, View next, int direction) {
2617        if (next == null || next == this) {
2618            return false;
2619        }
2620        // panic, result view is not a child anymore, maybe workaround b/37864393
2621        if (findContainingItemView(next) == null) {
2622            return false;
2623        }
2624        if (focused == null) {
2625            return true;
2626        }
2627        // panic, focused view is not a child anymore, maybe workaround b/37864393
2628        if (findContainingItemView(focused) == null) {
2629            return true;
2630        }
2631
2632        mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2633        mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
2634        offsetDescendantRectToMyCoords(focused, mTempRect);
2635        offsetDescendantRectToMyCoords(next, mTempRect2);
2636        final int rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL ? -1 : 1;
2637        int rightness = 0;
2638        if ((mTempRect.left < mTempRect2.left
2639                || mTempRect.right <= mTempRect2.left)
2640                && mTempRect.right < mTempRect2.right) {
2641            rightness = 1;
2642        } else if ((mTempRect.right > mTempRect2.right
2643                || mTempRect.left >= mTempRect2.right)
2644                && mTempRect.left > mTempRect2.left) {
2645            rightness = -1;
2646        }
2647        int downness = 0;
2648        if ((mTempRect.top < mTempRect2.top
2649                || mTempRect.bottom <= mTempRect2.top)
2650                && mTempRect.bottom < mTempRect2.bottom) {
2651            downness = 1;
2652        } else if ((mTempRect.bottom > mTempRect2.bottom
2653                || mTempRect.top >= mTempRect2.bottom)
2654                && mTempRect.top > mTempRect2.top) {
2655            downness = -1;
2656        }
2657        switch (direction) {
2658            case View.FOCUS_LEFT:
2659                return rightness < 0;
2660            case View.FOCUS_RIGHT:
2661                return rightness > 0;
2662            case View.FOCUS_UP:
2663                return downness < 0;
2664            case View.FOCUS_DOWN:
2665                return downness > 0;
2666            case View.FOCUS_FORWARD:
2667                return downness > 0 || (downness == 0 && rightness * rtl >= 0);
2668            case View.FOCUS_BACKWARD:
2669                return downness < 0 || (downness == 0 && rightness * rtl <= 0);
2670        }
2671        throw new IllegalArgumentException("Invalid direction: " + direction + exceptionLabel());
2672    }
2673
2674    @Override
2675    public void requestChildFocus(View child, View focused) {
2676        if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
2677            requestChildOnScreen(child, focused);
2678        }
2679        super.requestChildFocus(child, focused);
2680    }
2681
2682    /**
2683     * Requests that the given child of the RecyclerView be positioned onto the screen. This method
2684     * can be called for both unfocusable and focusable child views. For unfocusable child views,
2685     * the {@param focused} parameter passed is null, whereas for a focusable child, this parameter
2686     * indicates the actual descendant view within this child view that holds the focus.
2687     * @param child The child view of this RecyclerView that wants to come onto the screen.
2688     * @param focused The descendant view that actually has the focus if child is focusable, null
2689     *                otherwise.
2690     */
2691    private void requestChildOnScreen(@NonNull View child, @Nullable View focused) {
2692        View rectView = (focused != null) ? focused : child;
2693        mTempRect.set(0, 0, rectView.getWidth(), rectView.getHeight());
2694
2695        // get item decor offsets w/o refreshing. If they are invalid, there will be another
2696        // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
2697        // View in viewport.
2698        final ViewGroup.LayoutParams focusedLayoutParams = rectView.getLayoutParams();
2699        if (focusedLayoutParams instanceof LayoutParams) {
2700            // if focused child has item decors, use them. Otherwise, ignore.
2701            final LayoutParams lp = (LayoutParams) focusedLayoutParams;
2702            if (!lp.mInsetsDirty) {
2703                final Rect insets = lp.mDecorInsets;
2704                mTempRect.left -= insets.left;
2705                mTempRect.right += insets.right;
2706                mTempRect.top -= insets.top;
2707                mTempRect.bottom += insets.bottom;
2708            }
2709        }
2710
2711        if (focused != null) {
2712            offsetDescendantRectToMyCoords(focused, mTempRect);
2713            offsetRectIntoDescendantCoords(child, mTempRect);
2714        }
2715        mLayout.requestChildRectangleOnScreen(this, child, mTempRect, !mFirstLayoutComplete,
2716                (focused == null));
2717    }
2718
2719    @Override
2720    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
2721        return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
2722    }
2723
2724    @Override
2725    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
2726        if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
2727            super.addFocusables(views, direction, focusableMode);
2728        }
2729    }
2730
2731    @Override
2732    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
2733        if (isComputingLayout()) {
2734            // if we are in the middle of a layout calculation, don't let any child take focus.
2735            // RV will handle it after layout calculation is finished.
2736            return false;
2737        }
2738        return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
2739    }
2740
2741    @Override
2742    protected void onAttachedToWindow() {
2743        super.onAttachedToWindow();
2744        mLayoutOrScrollCounter = 0;
2745        mIsAttached = true;
2746        mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
2747        if (mLayout != null) {
2748            mLayout.dispatchAttachedToWindow(this);
2749        }
2750        mPostedAnimatorRunner = false;
2751
2752        if (ALLOW_THREAD_GAP_WORK) {
2753            // Register with gap worker
2754            mGapWorker = GapWorker.sGapWorker.get();
2755            if (mGapWorker == null) {
2756                mGapWorker = new GapWorker();
2757
2758                // break 60 fps assumption if data from display appears valid
2759                // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
2760                Display display = ViewCompat.getDisplay(this);
2761                float refreshRate = 60.0f;
2762                if (!isInEditMode() && display != null) {
2763                    float displayRefreshRate = display.getRefreshRate();
2764                    if (displayRefreshRate >= 30.0f) {
2765                        refreshRate = displayRefreshRate;
2766                    }
2767                }
2768                mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
2769                GapWorker.sGapWorker.set(mGapWorker);
2770            }
2771            mGapWorker.add(this);
2772        }
2773    }
2774
2775    @Override
2776    protected void onDetachedFromWindow() {
2777        super.onDetachedFromWindow();
2778        if (mItemAnimator != null) {
2779            mItemAnimator.endAnimations();
2780        }
2781        stopScroll();
2782        mIsAttached = false;
2783        if (mLayout != null) {
2784            mLayout.dispatchDetachedFromWindow(this, mRecycler);
2785        }
2786        mPendingAccessibilityImportanceChange.clear();
2787        removeCallbacks(mItemAnimatorRunner);
2788        mViewInfoStore.onDetach();
2789
2790        if (ALLOW_THREAD_GAP_WORK && mGapWorker != null) {
2791            // Unregister with gap worker
2792            mGapWorker.remove(this);
2793            mGapWorker = null;
2794        }
2795    }
2796
2797    /**
2798     * Returns true if RecyclerView is attached to window.
2799     */
2800    @Override
2801    public boolean isAttachedToWindow() {
2802        return mIsAttached;
2803    }
2804
2805    /**
2806     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2807     * {@link IllegalStateException} if it <b>is not</b>.
2808     *
2809     * @param message The message for the exception. Can be null.
2810     * @see #assertNotInLayoutOrScroll(String)
2811     */
2812    void assertInLayoutOrScroll(String message) {
2813        if (!isComputingLayout()) {
2814            if (message == null) {
2815                throw new IllegalStateException("Cannot call this method unless RecyclerView is "
2816                        + "computing a layout or scrolling" + exceptionLabel());
2817            }
2818            throw new IllegalStateException(message + exceptionLabel());
2819
2820        }
2821    }
2822
2823    /**
2824     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2825     * {@link IllegalStateException} if it <b>is</b>.
2826     *
2827     * @param message The message for the exception. Can be null.
2828     * @see #assertInLayoutOrScroll(String)
2829     */
2830    void assertNotInLayoutOrScroll(String message) {
2831        if (isComputingLayout()) {
2832            if (message == null) {
2833                throw new IllegalStateException("Cannot call this method while RecyclerView is "
2834                        + "computing a layout or scrolling" + exceptionLabel());
2835            }
2836            throw new IllegalStateException(message);
2837        }
2838        if (mDispatchScrollCounter > 0) {
2839            Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might"
2840                            + "be run during a measure & layout pass where you cannot change the"
2841                            + "RecyclerView data. Any method call that might change the structure"
2842                            + "of the RecyclerView or the adapter contents should be postponed to"
2843                            + "the next frame.",
2844                    new IllegalStateException("" + exceptionLabel()));
2845        }
2846    }
2847
2848    /**
2849     * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
2850     * to child views or this view's standard scrolling behavior.
2851     *
2852     * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
2853     * returns true from
2854     * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
2855     * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
2856     * for each incoming MotionEvent until the end of the gesture.</p>
2857     *
2858     * @param listener Listener to add
2859     * @see SimpleOnItemTouchListener
2860     */
2861    public void addOnItemTouchListener(@NonNull OnItemTouchListener listener) {
2862        mOnItemTouchListeners.add(listener);
2863    }
2864
2865    /**
2866     * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
2867     *
2868     * @param listener Listener to remove
2869     */
2870    public void removeOnItemTouchListener(@NonNull OnItemTouchListener listener) {
2871        mOnItemTouchListeners.remove(listener);
2872        if (mActiveOnItemTouchListener == listener) {
2873            mActiveOnItemTouchListener = null;
2874        }
2875    }
2876
2877    private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
2878        final int action = e.getAction();
2879        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
2880            mActiveOnItemTouchListener = null;
2881        }
2882
2883        final int listenerCount = mOnItemTouchListeners.size();
2884        for (int i = 0; i < listenerCount; i++) {
2885            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2886            if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
2887                mActiveOnItemTouchListener = listener;
2888                return true;
2889            }
2890        }
2891        return false;
2892    }
2893
2894    private boolean dispatchOnItemTouch(MotionEvent e) {
2895        final int action = e.getAction();
2896        if (mActiveOnItemTouchListener != null) {
2897            if (action == MotionEvent.ACTION_DOWN) {
2898                // Stale state from a previous gesture, we're starting a new one. Clear it.
2899                mActiveOnItemTouchListener = null;
2900            } else {
2901                mActiveOnItemTouchListener.onTouchEvent(this, e);
2902                if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
2903                    // Clean up for the next gesture.
2904                    mActiveOnItemTouchListener = null;
2905                }
2906                return true;
2907            }
2908        }
2909
2910        // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
2911        // as called from onInterceptTouchEvent; skip it.
2912        if (action != MotionEvent.ACTION_DOWN) {
2913            final int listenerCount = mOnItemTouchListeners.size();
2914            for (int i = 0; i < listenerCount; i++) {
2915                final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2916                if (listener.onInterceptTouchEvent(this, e)) {
2917                    mActiveOnItemTouchListener = listener;
2918                    return true;
2919                }
2920            }
2921        }
2922        return false;
2923    }
2924
2925    @Override
2926    public boolean onInterceptTouchEvent(MotionEvent e) {
2927        if (mLayoutFrozen) {
2928            // When layout is frozen,  RV does not intercept the motion event.
2929            // A child view e.g. a button may still get the click.
2930            return false;
2931        }
2932        if (dispatchOnItemTouchIntercept(e)) {
2933            cancelTouch();
2934            return true;
2935        }
2936
2937        if (mLayout == null) {
2938            return false;
2939        }
2940
2941        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2942        final boolean canScrollVertically = mLayout.canScrollVertically();
2943
2944        if (mVelocityTracker == null) {
2945            mVelocityTracker = VelocityTracker.obtain();
2946        }
2947        mVelocityTracker.addMovement(e);
2948
2949        final int action = e.getActionMasked();
2950        final int actionIndex = e.getActionIndex();
2951
2952        switch (action) {
2953            case MotionEvent.ACTION_DOWN:
2954                if (mIgnoreMotionEventTillDown) {
2955                    mIgnoreMotionEventTillDown = false;
2956                }
2957                mScrollPointerId = e.getPointerId(0);
2958                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2959                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2960
2961                if (mScrollState == SCROLL_STATE_SETTLING) {
2962                    getParent().requestDisallowInterceptTouchEvent(true);
2963                    setScrollState(SCROLL_STATE_DRAGGING);
2964                }
2965
2966                // Clear the nested offsets
2967                mNestedOffsets[0] = mNestedOffsets[1] = 0;
2968
2969                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2970                if (canScrollHorizontally) {
2971                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2972                }
2973                if (canScrollVertically) {
2974                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2975                }
2976                startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
2977                break;
2978
2979            case MotionEvent.ACTION_POINTER_DOWN:
2980                mScrollPointerId = e.getPointerId(actionIndex);
2981                mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2982                mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2983                break;
2984
2985            case MotionEvent.ACTION_MOVE: {
2986                final int index = e.findPointerIndex(mScrollPointerId);
2987                if (index < 0) {
2988                    Log.e(TAG, "Error processing scroll; pointer index for id "
2989                            + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2990                    return false;
2991                }
2992
2993                final int x = (int) (e.getX(index) + 0.5f);
2994                final int y = (int) (e.getY(index) + 0.5f);
2995                if (mScrollState != SCROLL_STATE_DRAGGING) {
2996                    final int dx = x - mInitialTouchX;
2997                    final int dy = y - mInitialTouchY;
2998                    boolean startScroll = false;
2999                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
3000                        mLastTouchX = x;
3001                        startScroll = true;
3002                    }
3003                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
3004                        mLastTouchY = y;
3005                        startScroll = true;
3006                    }
3007                    if (startScroll) {
3008                        setScrollState(SCROLL_STATE_DRAGGING);
3009                    }
3010                }
3011            } break;
3012
3013            case MotionEvent.ACTION_POINTER_UP: {
3014                onPointerUp(e);
3015            } break;
3016
3017            case MotionEvent.ACTION_UP: {
3018                mVelocityTracker.clear();
3019                stopNestedScroll(TYPE_TOUCH);
3020            } break;
3021
3022            case MotionEvent.ACTION_CANCEL: {
3023                cancelTouch();
3024            }
3025        }
3026        return mScrollState == SCROLL_STATE_DRAGGING;
3027    }
3028
3029    @Override
3030    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
3031        final int listenerCount = mOnItemTouchListeners.size();
3032        for (int i = 0; i < listenerCount; i++) {
3033            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
3034            listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
3035        }
3036        super.requestDisallowInterceptTouchEvent(disallowIntercept);
3037    }
3038
3039    @Override
3040    public boolean onTouchEvent(MotionEvent e) {
3041        if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
3042            return false;
3043        }
3044        if (dispatchOnItemTouch(e)) {
3045            cancelTouch();
3046            return true;
3047        }
3048
3049        if (mLayout == null) {
3050            return false;
3051        }
3052
3053        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
3054        final boolean canScrollVertically = mLayout.canScrollVertically();
3055
3056        if (mVelocityTracker == null) {
3057            mVelocityTracker = VelocityTracker.obtain();
3058        }
3059        boolean eventAddedToVelocityTracker = false;
3060
3061        final MotionEvent vtev = MotionEvent.obtain(e);
3062        final int action = e.getActionMasked();
3063        final int actionIndex = e.getActionIndex();
3064
3065        if (action == MotionEvent.ACTION_DOWN) {
3066            mNestedOffsets[0] = mNestedOffsets[1] = 0;
3067        }
3068        vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
3069
3070        switch (action) {
3071            case MotionEvent.ACTION_DOWN: {
3072                mScrollPointerId = e.getPointerId(0);
3073                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
3074                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
3075
3076                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
3077                if (canScrollHorizontally) {
3078                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
3079                }
3080                if (canScrollVertically) {
3081                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
3082                }
3083                startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
3084            } break;
3085
3086            case MotionEvent.ACTION_POINTER_DOWN: {
3087                mScrollPointerId = e.getPointerId(actionIndex);
3088                mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
3089                mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
3090            } break;
3091
3092            case MotionEvent.ACTION_MOVE: {
3093                final int index = e.findPointerIndex(mScrollPointerId);
3094                if (index < 0) {
3095                    Log.e(TAG, "Error processing scroll; pointer index for id "
3096                            + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
3097                    return false;
3098                }
3099
3100                final int x = (int) (e.getX(index) + 0.5f);
3101                final int y = (int) (e.getY(index) + 0.5f);
3102                int dx = mLastTouchX - x;
3103                int dy = mLastTouchY - y;
3104
3105                if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset, TYPE_TOUCH)) {
3106                    dx -= mScrollConsumed[0];
3107                    dy -= mScrollConsumed[1];
3108                    vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
3109                    // Updated the nested offsets
3110                    mNestedOffsets[0] += mScrollOffset[0];
3111                    mNestedOffsets[1] += mScrollOffset[1];
3112                }
3113
3114                if (mScrollState != SCROLL_STATE_DRAGGING) {
3115                    boolean startScroll = false;
3116                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
3117                        if (dx > 0) {
3118                            dx -= mTouchSlop;
3119                        } else {
3120                            dx += mTouchSlop;
3121                        }
3122                        startScroll = true;
3123                    }
3124                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
3125                        if (dy > 0) {
3126                            dy -= mTouchSlop;
3127                        } else {
3128                            dy += mTouchSlop;
3129                        }
3130                        startScroll = true;
3131                    }
3132                    if (startScroll) {
3133                        setScrollState(SCROLL_STATE_DRAGGING);
3134                    }
3135                }
3136
3137                if (mScrollState == SCROLL_STATE_DRAGGING) {
3138                    mLastTouchX = x - mScrollOffset[0];
3139                    mLastTouchY = y - mScrollOffset[1];
3140
3141                    if (scrollByInternal(
3142                            canScrollHorizontally ? dx : 0,
3143                            canScrollVertically ? dy : 0,
3144                            vtev)) {
3145                        getParent().requestDisallowInterceptTouchEvent(true);
3146                    }
3147                    if (mGapWorker != null && (dx != 0 || dy != 0)) {
3148                        mGapWorker.postFromTraversal(this, dx, dy);
3149                    }
3150                }
3151            } break;
3152
3153            case MotionEvent.ACTION_POINTER_UP: {
3154                onPointerUp(e);
3155            } break;
3156
3157            case MotionEvent.ACTION_UP: {
3158                mVelocityTracker.addMovement(vtev);
3159                eventAddedToVelocityTracker = true;
3160                mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
3161                final float xvel = canScrollHorizontally
3162                        ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
3163                final float yvel = canScrollVertically
3164                        ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
3165                if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
3166                    setScrollState(SCROLL_STATE_IDLE);
3167                }
3168                resetTouch();
3169            } break;
3170
3171            case MotionEvent.ACTION_CANCEL: {
3172                cancelTouch();
3173            } break;
3174        }
3175
3176        if (!eventAddedToVelocityTracker) {
3177            mVelocityTracker.addMovement(vtev);
3178        }
3179        vtev.recycle();
3180
3181        return true;
3182    }
3183
3184    private void resetTouch() {
3185        if (mVelocityTracker != null) {
3186            mVelocityTracker.clear();
3187        }
3188        stopNestedScroll(TYPE_TOUCH);
3189        releaseGlows();
3190    }
3191
3192    private void cancelTouch() {
3193        resetTouch();
3194        setScrollState(SCROLL_STATE_IDLE);
3195    }
3196
3197    private void onPointerUp(MotionEvent e) {
3198        final int actionIndex = e.getActionIndex();
3199        if (e.getPointerId(actionIndex) == mScrollPointerId) {
3200            // Pick a new pointer to pick up the slack.
3201            final int newIndex = actionIndex == 0 ? 1 : 0;
3202            mScrollPointerId = e.getPointerId(newIndex);
3203            mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
3204            mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
3205        }
3206    }
3207
3208    @Override
3209    public boolean onGenericMotionEvent(MotionEvent event) {
3210        if (mLayout == null) {
3211            return false;
3212        }
3213        if (mLayoutFrozen) {
3214            return false;
3215        }
3216        if (event.getAction() == MotionEventCompat.ACTION_SCROLL) {
3217            final float vScroll, hScroll;
3218            if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
3219                if (mLayout.canScrollVertically()) {
3220                    // Inverse the sign of the vertical scroll to align the scroll orientation
3221                    // with AbsListView.
3222                    vScroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
3223                } else {
3224                    vScroll = 0f;
3225                }
3226                if (mLayout.canScrollHorizontally()) {
3227                    hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
3228                } else {
3229                    hScroll = 0f;
3230                }
3231            } else if ((event.getSource() & InputDeviceCompat.SOURCE_ROTARY_ENCODER) != 0) {
3232                final float axisScroll = event.getAxisValue(MotionEventCompat.AXIS_SCROLL);
3233                if (mLayout.canScrollVertically()) {
3234                    // Invert the sign of the vertical scroll to align the scroll orientation
3235                    // with AbsListView.
3236                    vScroll = -axisScroll;
3237                    hScroll = 0f;
3238                } else if (mLayout.canScrollHorizontally()) {
3239                    vScroll = 0f;
3240                    hScroll = axisScroll;
3241                } else {
3242                    vScroll = 0f;
3243                    hScroll = 0f;
3244                }
3245            } else {
3246                vScroll = 0f;
3247                hScroll = 0f;
3248            }
3249
3250            if (vScroll != 0 || hScroll != 0) {
3251                scrollByInternal((int) (hScroll * mScaledHorizontalScrollFactor),
3252                        (int) (vScroll * mScaledVerticalScrollFactor), event);
3253            }
3254        }
3255        return false;
3256    }
3257
3258    @Override
3259    protected void onMeasure(int widthSpec, int heightSpec) {
3260        if (mLayout == null) {
3261            defaultOnMeasure(widthSpec, heightSpec);
3262            return;
3263        }
3264        if (mLayout.isAutoMeasureEnabled()) {
3265            final int widthMode = MeasureSpec.getMode(widthSpec);
3266            final int heightMode = MeasureSpec.getMode(heightSpec);
3267
3268            /**
3269             * This specific call should be considered deprecated and replaced with
3270             * {@link #defaultOnMeasure(int, int)}. It can't actually be replaced as it could
3271             * break existing third party code but all documentation directs developers to not
3272             * override {@link LayoutManager#onMeasure(int, int)} when
3273             * {@link LayoutManager#isAutoMeasureEnabled()} returns true.
3274             */
3275            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3276
3277            final boolean measureSpecModeIsExactly =
3278                    widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
3279            if (measureSpecModeIsExactly || mAdapter == null) {
3280                return;
3281            }
3282
3283            if (mState.mLayoutStep == State.STEP_START) {
3284                dispatchLayoutStep1();
3285            }
3286            // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
3287            // consistency
3288            mLayout.setMeasureSpecs(widthSpec, heightSpec);
3289            mState.mIsMeasuring = true;
3290            dispatchLayoutStep2();
3291
3292            // now we can get the width and height from the children.
3293            mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
3294
3295            // if RecyclerView has non-exact width and height and if there is at least one child
3296            // which also has non-exact width & height, we have to re-measure.
3297            if (mLayout.shouldMeasureTwice()) {
3298                mLayout.setMeasureSpecs(
3299                        MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
3300                        MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
3301                mState.mIsMeasuring = true;
3302                dispatchLayoutStep2();
3303                // now we can get the width and height from the children.
3304                mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
3305            }
3306        } else {
3307            if (mHasFixedSize) {
3308                mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3309                return;
3310            }
3311            // custom onMeasure
3312            if (mAdapterUpdateDuringMeasure) {
3313                startInterceptRequestLayout();
3314                onEnterLayoutOrScroll();
3315                processAdapterUpdatesAndSetAnimationFlags();
3316                onExitLayoutOrScroll();
3317
3318                if (mState.mRunPredictiveAnimations) {
3319                    mState.mInPreLayout = true;
3320                } else {
3321                    // consume remaining updates to provide a consistent state with the layout pass.
3322                    mAdapterHelper.consumeUpdatesInOnePass();
3323                    mState.mInPreLayout = false;
3324                }
3325                mAdapterUpdateDuringMeasure = false;
3326                stopInterceptRequestLayout(false);
3327            } else if (mState.mRunPredictiveAnimations) {
3328                // If mAdapterUpdateDuringMeasure is false and mRunPredictiveAnimations is true:
3329                // this means there is already an onMeasure() call performed to handle the pending
3330                // adapter change, two onMeasure() calls can happen if RV is a child of LinearLayout
3331                // with layout_width=MATCH_PARENT. RV cannot call LM.onMeasure() second time
3332                // because getViewForPosition() will crash when LM uses a child to measure.
3333                setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
3334                return;
3335            }
3336
3337            if (mAdapter != null) {
3338                mState.mItemCount = mAdapter.getItemCount();
3339            } else {
3340                mState.mItemCount = 0;
3341            }
3342            startInterceptRequestLayout();
3343            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3344            stopInterceptRequestLayout(false);
3345            mState.mInPreLayout = false; // clear
3346        }
3347    }
3348
3349    /**
3350     * An implementation of {@link View#onMeasure(int, int)} to fall back to in various scenarios
3351     * where this RecyclerView is otherwise lacking better information.
3352     */
3353    void defaultOnMeasure(int widthSpec, int heightSpec) {
3354        // calling LayoutManager here is not pretty but that API is already public and it is better
3355        // than creating another method since this is internal.
3356        final int width = LayoutManager.chooseSize(widthSpec,
3357                getPaddingLeft() + getPaddingRight(),
3358                ViewCompat.getMinimumWidth(this));
3359        final int height = LayoutManager.chooseSize(heightSpec,
3360                getPaddingTop() + getPaddingBottom(),
3361                ViewCompat.getMinimumHeight(this));
3362
3363        setMeasuredDimension(width, height);
3364    }
3365
3366    @Override
3367    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
3368        super.onSizeChanged(w, h, oldw, oldh);
3369        if (w != oldw || h != oldh) {
3370            invalidateGlows();
3371            // layout's w/h are updated during measure/layout steps.
3372        }
3373    }
3374
3375    /**
3376     * Sets the {@link ItemAnimator} that will handle animations involving changes
3377     * to the items in this RecyclerView. By default, RecyclerView instantiates and
3378     * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
3379     * enabled for the RecyclerView depends on the ItemAnimator and whether
3380     * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
3381     * supports item animations}.
3382     *
3383     * @param animator The ItemAnimator being set. If null, no animations will occur
3384     * when changes occur to the items in this RecyclerView.
3385     */
3386    public void setItemAnimator(@Nullable ItemAnimator animator) {
3387        if (mItemAnimator != null) {
3388            mItemAnimator.endAnimations();
3389            mItemAnimator.setListener(null);
3390        }
3391        mItemAnimator = animator;
3392        if (mItemAnimator != null) {
3393            mItemAnimator.setListener(mItemAnimatorListener);
3394        }
3395    }
3396
3397    void onEnterLayoutOrScroll() {
3398        mLayoutOrScrollCounter++;
3399    }
3400
3401    void onExitLayoutOrScroll() {
3402        onExitLayoutOrScroll(true);
3403    }
3404
3405    void onExitLayoutOrScroll(boolean enableChangeEvents) {
3406        mLayoutOrScrollCounter--;
3407        if (mLayoutOrScrollCounter < 1) {
3408            if (DEBUG && mLayoutOrScrollCounter < 0) {
3409                throw new IllegalStateException("layout or scroll counter cannot go below zero."
3410                        + "Some calls are not matching" + exceptionLabel());
3411            }
3412            mLayoutOrScrollCounter = 0;
3413            if (enableChangeEvents) {
3414                dispatchContentChangedIfNecessary();
3415                dispatchPendingImportantForAccessibilityChanges();
3416            }
3417        }
3418    }
3419
3420    boolean isAccessibilityEnabled() {
3421        return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
3422    }
3423
3424    private void dispatchContentChangedIfNecessary() {
3425        final int flags = mEatenAccessibilityChangeFlags;
3426        mEatenAccessibilityChangeFlags = 0;
3427        if (flags != 0 && isAccessibilityEnabled()) {
3428            final AccessibilityEvent event = AccessibilityEvent.obtain();
3429            event.setEventType(AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
3430            AccessibilityEventCompat.setContentChangeTypes(event, flags);
3431            sendAccessibilityEventUnchecked(event);
3432        }
3433    }
3434
3435    /**
3436     * Returns whether RecyclerView is currently computing a layout.
3437     * <p>
3438     * If this method returns true, it means that RecyclerView is in a lockdown state and any
3439     * attempt to update adapter contents will result in an exception because adapter contents
3440     * cannot be changed while RecyclerView is trying to compute the layout.
3441     * <p>
3442     * It is very unlikely that your code will be running during this state as it is
3443     * called by the framework when a layout traversal happens or RecyclerView starts to scroll
3444     * in response to system events (touch, accessibility etc).
3445     * <p>
3446     * This case may happen if you have some custom logic to change adapter contents in
3447     * response to a View callback (e.g. focus change callback) which might be triggered during a
3448     * layout calculation. In these cases, you should just postpone the change using a Handler or a
3449     * similar mechanism.
3450     *
3451     * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
3452     *         otherwise
3453     */
3454    public boolean isComputingLayout() {
3455        return mLayoutOrScrollCounter > 0;
3456    }
3457
3458    /**
3459     * Returns true if an accessibility event should not be dispatched now. This happens when an
3460     * accessibility request arrives while RecyclerView does not have a stable state which is very
3461     * hard to handle for a LayoutManager. Instead, this method records necessary information about
3462     * the event and dispatches a window change event after the critical section is finished.
3463     *
3464     * @return True if the accessibility event should be postponed.
3465     */
3466    boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
3467        if (isComputingLayout()) {
3468            int type = 0;
3469            if (event != null) {
3470                type = AccessibilityEventCompat.getContentChangeTypes(event);
3471            }
3472            if (type == 0) {
3473                type = AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED;
3474            }
3475            mEatenAccessibilityChangeFlags |= type;
3476            return true;
3477        }
3478        return false;
3479    }
3480
3481    @Override
3482    public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
3483        if (shouldDeferAccessibilityEvent(event)) {
3484            return;
3485        }
3486        super.sendAccessibilityEventUnchecked(event);
3487    }
3488
3489    /**
3490     * Gets the current ItemAnimator for this RecyclerView. A null return value
3491     * indicates that there is no animator and that item changes will happen without
3492     * any animations. By default, RecyclerView instantiates and
3493     * uses an instance of {@link DefaultItemAnimator}.
3494     *
3495     * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
3496     * when changes occur to the items in this RecyclerView.
3497     */
3498    @Nullable
3499    public ItemAnimator getItemAnimator() {
3500        return mItemAnimator;
3501    }
3502
3503    /**
3504     * Post a runnable to the next frame to run pending item animations. Only the first such
3505     * request will be posted, governed by the mPostedAnimatorRunner flag.
3506     */
3507    void postAnimationRunner() {
3508        if (!mPostedAnimatorRunner && mIsAttached) {
3509            ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
3510            mPostedAnimatorRunner = true;
3511        }
3512    }
3513
3514    private boolean predictiveItemAnimationsEnabled() {
3515        return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
3516    }
3517
3518    /**
3519     * Consumes adapter updates and calculates which type of animations we want to run.
3520     * Called in onMeasure and dispatchLayout.
3521     * <p>
3522     * This method may process only the pre-layout state of updates or all of them.
3523     */
3524    private void processAdapterUpdatesAndSetAnimationFlags() {
3525        if (mDataSetHasChangedAfterLayout) {
3526            // Processing these items have no value since data set changed unexpectedly.
3527            // Instead, we just reset it.
3528            mAdapterHelper.reset();
3529            if (mDispatchItemsChangedEvent) {
3530                mLayout.onItemsChanged(this);
3531            }
3532        }
3533        // simple animations are a subset of advanced animations (which will cause a
3534        // pre-layout step)
3535        // If layout supports predictive animations, pre-process to decide if we want to run them
3536        if (predictiveItemAnimationsEnabled()) {
3537            mAdapterHelper.preProcess();
3538        } else {
3539            mAdapterHelper.consumeUpdatesInOnePass();
3540        }
3541        boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
3542        mState.mRunSimpleAnimations = mFirstLayoutComplete
3543                && mItemAnimator != null
3544                && (mDataSetHasChangedAfterLayout
3545                || animationTypeSupported
3546                || mLayout.mRequestedSimpleAnimations)
3547                && (!mDataSetHasChangedAfterLayout
3548                || mAdapter.hasStableIds());
3549        mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
3550                && animationTypeSupported
3551                && !mDataSetHasChangedAfterLayout
3552                && predictiveItemAnimationsEnabled();
3553    }
3554
3555    /**
3556     * Wrapper around layoutChildren() that handles animating changes caused by layout.
3557     * Animations work on the assumption that there are five different kinds of items
3558     * in play:
3559     * PERSISTENT: items are visible before and after layout
3560     * REMOVED: items were visible before layout and were removed by the app
3561     * ADDED: items did not exist before layout and were added by the app
3562     * DISAPPEARING: items exist in the data set before/after, but changed from
3563     * visible to non-visible in the process of layout (they were moved off
3564     * screen as a side-effect of other changes)
3565     * APPEARING: items exist in the data set before/after, but changed from
3566     * non-visible to visible in the process of layout (they were moved on
3567     * screen as a side-effect of other changes)
3568     * The overall approach figures out what items exist before/after layout and
3569     * infers one of the five above states for each of the items. Then the animations
3570     * are set up accordingly:
3571     * PERSISTENT views are animated via
3572     * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3573     * DISAPPEARING views are animated via
3574     * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3575     * APPEARING views are animated via
3576     * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3577     * and changed views are animated via
3578     * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
3579     */
3580    void dispatchLayout() {
3581        if (mAdapter == null) {
3582            Log.e(TAG, "No adapter attached; skipping layout");
3583            // leave the state in START
3584            return;
3585        }
3586        if (mLayout == null) {
3587            Log.e(TAG, "No layout manager attached; skipping layout");
3588            // leave the state in START
3589            return;
3590        }
3591        mState.mIsMeasuring = false;
3592        if (mState.mLayoutStep == State.STEP_START) {
3593            dispatchLayoutStep1();
3594            mLayout.setExactMeasureSpecsFrom(this);
3595            dispatchLayoutStep2();
3596        } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
3597                || mLayout.getHeight() != getHeight()) {
3598            // First 2 steps are done in onMeasure but looks like we have to run again due to
3599            // changed size.
3600            mLayout.setExactMeasureSpecsFrom(this);
3601            dispatchLayoutStep2();
3602        } else {
3603            // always make sure we sync them (to ensure mode is exact)
3604            mLayout.setExactMeasureSpecsFrom(this);
3605        }
3606        dispatchLayoutStep3();
3607    }
3608
3609    private void saveFocusInfo() {
3610        View child = null;
3611        if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
3612            child = getFocusedChild();
3613        }
3614
3615        final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
3616        if (focusedVh == null) {
3617            resetFocusInfo();
3618        } else {
3619            mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
3620            // mFocusedItemPosition should hold the current adapter position of the previously
3621            // focused item. If the item is removed, we store the previous adapter position of the
3622            // removed item.
3623            mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION
3624                    : (focusedVh.isRemoved() ? focusedVh.mOldPosition
3625                            : focusedVh.getAdapterPosition());
3626            mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
3627        }
3628    }
3629
3630    private void resetFocusInfo() {
3631        mState.mFocusedItemId = NO_ID;
3632        mState.mFocusedItemPosition = NO_POSITION;
3633        mState.mFocusedSubChildId = View.NO_ID;
3634    }
3635
3636    /**
3637     * Finds the best view candidate to request focus on using mFocusedItemPosition index of the
3638     * previously focused item. It first traverses the adapter forward to find a focusable candidate
3639     * and if no such candidate is found, it reverses the focus search direction for the items
3640     * before the mFocusedItemPosition'th index;
3641     * @return The best candidate to request focus on, or null if no such candidate exists. Null
3642     * indicates all the existing adapter items are unfocusable.
3643     */
3644    @Nullable
3645    private View findNextViewToFocus() {
3646        int startFocusSearchIndex = mState.mFocusedItemPosition != -1 ? mState.mFocusedItemPosition
3647                : 0;
3648        ViewHolder nextFocus;
3649        final int itemCount = mState.getItemCount();
3650        for (int i = startFocusSearchIndex; i < itemCount; i++) {
3651            nextFocus = findViewHolderForAdapterPosition(i);
3652            if (nextFocus == null) {
3653                break;
3654            }
3655            if (nextFocus.itemView.hasFocusable()) {
3656                return nextFocus.itemView;
3657            }
3658        }
3659        final int limit = Math.min(itemCount, startFocusSearchIndex);
3660        for (int i = limit - 1; i >= 0; i--) {
3661            nextFocus = findViewHolderForAdapterPosition(i);
3662            if (nextFocus == null) {
3663                return null;
3664            }
3665            if (nextFocus.itemView.hasFocusable()) {
3666                return nextFocus.itemView;
3667            }
3668        }
3669        return null;
3670    }
3671
3672    private void recoverFocusFromState() {
3673        if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()
3674                || getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
3675                || (getDescendantFocusability() == FOCUS_BEFORE_DESCENDANTS && isFocused())) {
3676            // No-op if either of these cases happens:
3677            // 1. RV has no focus, or 2. RV blocks focus to its children, or 3. RV takes focus
3678            // before its children and is focused (i.e. it already stole the focus away from its
3679            // descendants).
3680            return;
3681        }
3682        // only recover focus if RV itself has the focus or the focused view is hidden
3683        if (!isFocused()) {
3684            final View focusedChild = getFocusedChild();
3685            if (IGNORE_DETACHED_FOCUSED_CHILD
3686                    && (focusedChild.getParent() == null || !focusedChild.hasFocus())) {
3687                // Special handling of API 15-. A focused child can be invalid because mFocus is not
3688                // cleared when the child is detached (mParent = null),
3689                // This happens because clearFocus on API 15- does not invalidate mFocus of its
3690                // parent when this child is detached.
3691                // For API 16+, this is not an issue because requestFocus takes care of clearing the
3692                // prior detached focused child. For API 15- the problem happens in 2 cases because
3693                // clearChild does not call clearChildFocus on RV: 1. setFocusable(false) is called
3694                // for the current focused item which calls clearChild or 2. when the prior focused
3695                // child is removed, removeDetachedView called in layout step 3 which calls
3696                // clearChild. We should ignore this invalid focused child in all our calculations
3697                // for the next view to receive focus, and apply the focus recovery logic instead.
3698                if (mChildHelper.getChildCount() == 0) {
3699                    // No children left. Request focus on the RV itself since one of its children
3700                    // was holding focus previously.
3701                    requestFocus();
3702                    return;
3703                }
3704            } else if (!mChildHelper.isHidden(focusedChild)) {
3705                // If the currently focused child is hidden, apply the focus recovery logic.
3706                // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
3707                return;
3708            }
3709        }
3710        ViewHolder focusTarget = null;
3711        // RV first attempts to locate the previously focused item to request focus on using
3712        // mFocusedItemId. If such an item no longer exists, it then makes a best-effort attempt to
3713        // find the next best candidate to request focus on based on mFocusedItemPosition.
3714        if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
3715            focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
3716        }
3717        View viewToFocus = null;
3718        if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView)
3719                || !focusTarget.itemView.hasFocusable()) {
3720            if (mChildHelper.getChildCount() > 0) {
3721                // At this point, RV has focus and either of these conditions are true:
3722                // 1. There's no previously focused item either because RV received focused before
3723                // layout, or the previously focused item was removed, or RV doesn't have stable IDs
3724                // 2. Previous focus child is hidden, or 3. Previous focused child is no longer
3725                // focusable. In either of these cases, we make sure that RV still passes down the
3726                // focus to one of its focusable children using a best-effort algorithm.
3727                viewToFocus = findNextViewToFocus();
3728            }
3729        } else {
3730            // looks like the focused item has been replaced with another view that represents the
3731            // same item in the adapter. Request focus on that.
3732            viewToFocus = focusTarget.itemView;
3733        }
3734
3735        if (viewToFocus != null) {
3736            if (mState.mFocusedSubChildId != NO_ID) {
3737                View child = viewToFocus.findViewById(mState.mFocusedSubChildId);
3738                if (child != null && child.isFocusable()) {
3739                    viewToFocus = child;
3740                }
3741            }
3742            viewToFocus.requestFocus();
3743        }
3744    }
3745
3746    private int getDeepestFocusedViewWithId(View view) {
3747        int lastKnownId = view.getId();
3748        while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
3749            view = ((ViewGroup) view).getFocusedChild();
3750            final int id = view.getId();
3751            if (id != View.NO_ID) {
3752                lastKnownId = view.getId();
3753            }
3754        }
3755        return lastKnownId;
3756    }
3757
3758    final void fillRemainingScrollValues(State state) {
3759        if (getScrollState() == SCROLL_STATE_SETTLING) {
3760            final OverScroller scroller = mViewFlinger.mScroller;
3761            state.mRemainingScrollHorizontal = scroller.getFinalX() - scroller.getCurrX();
3762            state.mRemainingScrollVertical = scroller.getFinalY() - scroller.getCurrY();
3763        } else {
3764            state.mRemainingScrollHorizontal = 0;
3765            state.mRemainingScrollVertical = 0;
3766        }
3767    }
3768
3769    /**
3770     * The first step of a layout where we;
3771     * - process adapter updates
3772     * - decide which animation should run
3773     * - save information about current views
3774     * - If necessary, run predictive layout and save its information
3775     */
3776    private void dispatchLayoutStep1() {
3777        mState.assertLayoutStep(State.STEP_START);
3778        fillRemainingScrollValues(mState);
3779        mState.mIsMeasuring = false;
3780        startInterceptRequestLayout();
3781        mViewInfoStore.clear();
3782        onEnterLayoutOrScroll();
3783        processAdapterUpdatesAndSetAnimationFlags();
3784        saveFocusInfo();
3785        mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
3786        mItemsAddedOrRemoved = mItemsChanged = false;
3787        mState.mInPreLayout = mState.mRunPredictiveAnimations;
3788        mState.mItemCount = mAdapter.getItemCount();
3789        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3790
3791        if (mState.mRunSimpleAnimations) {
3792            // Step 0: Find out where all non-removed items are, pre-layout
3793            int count = mChildHelper.getChildCount();
3794            for (int i = 0; i < count; ++i) {
3795                final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3796                if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
3797                    continue;
3798                }
3799                final ItemHolderInfo animationInfo = mItemAnimator
3800                        .recordPreLayoutInformation(mState, holder,
3801                                ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
3802                                holder.getUnmodifiedPayloads());
3803                mViewInfoStore.addToPreLayout(holder, animationInfo);
3804                if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
3805                        && !holder.shouldIgnore() && !holder.isInvalid()) {
3806                    long key = getChangedHolderKey(holder);
3807                    // This is NOT the only place where a ViewHolder is added to old change holders
3808                    // list. There is another case where:
3809                    //    * A VH is currently hidden but not deleted
3810                    //    * The hidden item is changed in the adapter
3811                    //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
3812                    // When this case is detected, RV will un-hide that view and add to the old
3813                    // change holders list.
3814                    mViewInfoStore.addToOldChangeHolders(key, holder);
3815                }
3816            }
3817        }
3818        if (mState.mRunPredictiveAnimations) {
3819            // Step 1: run prelayout: This will use the old positions of items. The layout manager
3820            // is expected to layout everything, even removed items (though not to add removed
3821            // items back to the container). This gives the pre-layout position of APPEARING views
3822            // which come into existence as part of the real layout.
3823
3824            // Save old positions so that LayoutManager can run its mapping logic.
3825            saveOldPositions();
3826            final boolean didStructureChange = mState.mStructureChanged;
3827            mState.mStructureChanged = false;
3828            // temporarily disable flag because we are asking for previous layout
3829            mLayout.onLayoutChildren(mRecycler, mState);
3830            mState.mStructureChanged = didStructureChange;
3831
3832            for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
3833                final View child = mChildHelper.getChildAt(i);
3834                final ViewHolder viewHolder = getChildViewHolderInt(child);
3835                if (viewHolder.shouldIgnore()) {
3836                    continue;
3837                }
3838                if (!mViewInfoStore.isInPreLayout(viewHolder)) {
3839                    int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
3840                    boolean wasHidden = viewHolder
3841                            .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3842                    if (!wasHidden) {
3843                        flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
3844                    }
3845                    final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
3846                            mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
3847                    if (wasHidden) {
3848                        recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
3849                    } else {
3850                        mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
3851                    }
3852                }
3853            }
3854            // we don't process disappearing list because they may re-appear in post layout pass.
3855            clearOldPositions();
3856        } else {
3857            clearOldPositions();
3858        }
3859        onExitLayoutOrScroll();
3860        stopInterceptRequestLayout(false);
3861        mState.mLayoutStep = State.STEP_LAYOUT;
3862    }
3863
3864    /**
3865     * The second layout step where we do the actual layout of the views for the final state.
3866     * This step might be run multiple times if necessary (e.g. measure).
3867     */
3868    private void dispatchLayoutStep2() {
3869        startInterceptRequestLayout();
3870        onEnterLayoutOrScroll();
3871        mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
3872        mAdapterHelper.consumeUpdatesInOnePass();
3873        mState.mItemCount = mAdapter.getItemCount();
3874        mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
3875
3876        // Step 2: Run layout
3877        mState.mInPreLayout = false;
3878        mLayout.onLayoutChildren(mRecycler, mState);
3879
3880        mState.mStructureChanged = false;
3881        mPendingSavedState = null;
3882
3883        // onLayoutChildren may have caused client code to disable item animations; re-check
3884        mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
3885        mState.mLayoutStep = State.STEP_ANIMATIONS;
3886        onExitLayoutOrScroll();
3887        stopInterceptRequestLayout(false);
3888    }
3889
3890    /**
3891     * The final step of the layout where we save the information about views for animations,
3892     * trigger animations and do any necessary cleanup.
3893     */
3894    private void dispatchLayoutStep3() {
3895        mState.assertLayoutStep(State.STEP_ANIMATIONS);
3896        startInterceptRequestLayout();
3897        onEnterLayoutOrScroll();
3898        mState.mLayoutStep = State.STEP_START;
3899        if (mState.mRunSimpleAnimations) {
3900            // Step 3: Find out where things are now, and process change animations.
3901            // traverse list in reverse because we may call animateChange in the loop which may
3902            // remove the target view holder.
3903            for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
3904                ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3905                if (holder.shouldIgnore()) {
3906                    continue;
3907                }
3908                long key = getChangedHolderKey(holder);
3909                final ItemHolderInfo animationInfo = mItemAnimator
3910                        .recordPostLayoutInformation(mState, holder);
3911                ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
3912                if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
3913                    // run a change animation
3914
3915                    // If an Item is CHANGED but the updated version is disappearing, it creates
3916                    // a conflicting case.
3917                    // Since a view that is marked as disappearing is likely to be going out of
3918                    // bounds, we run a change animation. Both views will be cleaned automatically
3919                    // once their animations finish.
3920                    // On the other hand, if it is the same view holder instance, we run a
3921                    // disappearing animation instead because we are not going to rebind the updated
3922                    // VH unless it is enforced by the layout manager.
3923                    final boolean oldDisappearing = mViewInfoStore.isDisappearing(
3924                            oldChangeViewHolder);
3925                    final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
3926                    if (oldDisappearing && oldChangeViewHolder == holder) {
3927                        // run disappear animation instead of change
3928                        mViewInfoStore.addToPostLayout(holder, animationInfo);
3929                    } else {
3930                        final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
3931                                oldChangeViewHolder);
3932                        // we add and remove so that any post info is merged.
3933                        mViewInfoStore.addToPostLayout(holder, animationInfo);
3934                        ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
3935                        if (preInfo == null) {
3936                            handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
3937                        } else {
3938                            animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
3939                                    oldDisappearing, newDisappearing);
3940                        }
3941                    }
3942                } else {
3943                    mViewInfoStore.addToPostLayout(holder, animationInfo);
3944                }
3945            }
3946
3947            // Step 4: Process view info lists and trigger animations
3948            mViewInfoStore.process(mViewInfoProcessCallback);
3949        }
3950
3951        mLayout.removeAndRecycleScrapInt(mRecycler);
3952        mState.mPreviousLayoutItemCount = mState.mItemCount;
3953        mDataSetHasChangedAfterLayout = false;
3954        mDispatchItemsChangedEvent = false;
3955        mState.mRunSimpleAnimations = false;
3956
3957        mState.mRunPredictiveAnimations = false;
3958        mLayout.mRequestedSimpleAnimations = false;
3959        if (mRecycler.mChangedScrap != null) {
3960            mRecycler.mChangedScrap.clear();
3961        }
3962        if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
3963            // Initial prefetch has expanded cache, so reset until next prefetch.
3964            // This prevents initial prefetches from expanding the cache permanently.
3965            mLayout.mPrefetchMaxCountObserved = 0;
3966            mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
3967            mRecycler.updateViewCacheSize();
3968        }
3969
3970        mLayout.onLayoutCompleted(mState);
3971        onExitLayoutOrScroll();
3972        stopInterceptRequestLayout(false);
3973        mViewInfoStore.clear();
3974        if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
3975            dispatchOnScrolled(0, 0);
3976        }
3977        recoverFocusFromState();
3978        resetFocusInfo();
3979    }
3980
3981    /**
3982     * This handles the case where there is an unexpected VH missing in the pre-layout map.
3983     * <p>
3984     * We might be able to detect the error in the application which will help the developer to
3985     * resolve the issue.
3986     * <p>
3987     * If it is not an expected error, we at least print an error to notify the developer and ignore
3988     * the animation.
3989     *
3990     * https://code.google.com/p/android/issues/detail?id=193958
3991     *
3992     * @param key The change key
3993     * @param holder Current ViewHolder
3994     * @param oldChangeViewHolder Changed ViewHolder
3995     */
3996    private void handleMissingPreInfoForChangeError(long key,
3997            ViewHolder holder, ViewHolder oldChangeViewHolder) {
3998        // check if two VH have the same key, if so, print that as an error
3999        final int childCount = mChildHelper.getChildCount();
4000        for (int i = 0; i < childCount; i++) {
4001            View view = mChildHelper.getChildAt(i);
4002            ViewHolder other = getChildViewHolderInt(view);
4003            if (other == holder) {
4004                continue;
4005            }
4006            final long otherKey = getChangedHolderKey(other);
4007            if (otherKey == key) {
4008                if (mAdapter != null && mAdapter.hasStableIds()) {
4009                    throw new IllegalStateException("Two different ViewHolders have the same stable"
4010                            + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
4011                            + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
4012                            + exceptionLabel());
4013                } else {
4014                    throw new IllegalStateException("Two different ViewHolders have the same change"
4015                            + " ID. This might happen due to inconsistent Adapter update events or"
4016                            + " if the LayoutManager lays out the same View multiple times."
4017                            + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
4018                            + exceptionLabel());
4019                }
4020            }
4021        }
4022        // Very unlikely to happen but if it does, notify the developer.
4023        Log.e(TAG, "Problem while matching changed view holders with the new"
4024                + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
4025                + " cannot be found but it is necessary for " + holder + exceptionLabel());
4026    }
4027
4028    /**
4029     * Records the animation information for a view holder that was bounced from hidden list. It
4030     * also clears the bounce back flag.
4031     */
4032    void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
4033            ItemHolderInfo animationInfo) {
4034        // looks like this view bounced back from hidden list!
4035        viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
4036        if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
4037                && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
4038            long key = getChangedHolderKey(viewHolder);
4039            mViewInfoStore.addToOldChangeHolders(key, viewHolder);
4040        }
4041        mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
4042    }
4043
4044    private void findMinMaxChildLayoutPositions(int[] into) {
4045        final int count = mChildHelper.getChildCount();
4046        if (count == 0) {
4047            into[0] = NO_POSITION;
4048            into[1] = NO_POSITION;
4049            return;
4050        }
4051        int minPositionPreLayout = Integer.MAX_VALUE;
4052        int maxPositionPreLayout = Integer.MIN_VALUE;
4053        for (int i = 0; i < count; ++i) {
4054            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
4055            if (holder.shouldIgnore()) {
4056                continue;
4057            }
4058            final int pos = holder.getLayoutPosition();
4059            if (pos < minPositionPreLayout) {
4060                minPositionPreLayout = pos;
4061            }
4062            if (pos > maxPositionPreLayout) {
4063                maxPositionPreLayout = pos;
4064            }
4065        }
4066        into[0] = minPositionPreLayout;
4067        into[1] = maxPositionPreLayout;
4068    }
4069
4070    private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
4071        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
4072        return mMinMaxLayoutPositions[0] != minPositionPreLayout
4073                || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
4074    }
4075
4076    @Override
4077    protected void removeDetachedView(View child, boolean animate) {
4078        ViewHolder vh = getChildViewHolderInt(child);
4079        if (vh != null) {
4080            if (vh.isTmpDetached()) {
4081                vh.clearTmpDetachFlag();
4082            } else if (!vh.shouldIgnore()) {
4083                throw new IllegalArgumentException("Called removeDetachedView with a view which"
4084                        + " is not flagged as tmp detached." + vh + exceptionLabel());
4085            }
4086        }
4087
4088        // Clear any android.view.animation.Animation that may prevent the item from
4089        // detaching when being removed. If a child is re-added before the
4090        // lazy detach occurs, it will receive invalid attach/detach sequencing.
4091        child.clearAnimation();
4092
4093        dispatchChildDetached(child);
4094        super.removeDetachedView(child, animate);
4095    }
4096
4097    /**
4098     * Returns a unique key to be used while handling change animations.
4099     * It might be child's position or stable id depending on the adapter type.
4100     */
4101    long getChangedHolderKey(ViewHolder holder) {
4102        return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
4103    }
4104
4105    void animateAppearance(@NonNull ViewHolder itemHolder,
4106            @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
4107        itemHolder.setIsRecyclable(false);
4108        if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
4109            postAnimationRunner();
4110        }
4111    }
4112
4113    void animateDisappearance(@NonNull ViewHolder holder,
4114            @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
4115        addAnimatingView(holder);
4116        holder.setIsRecyclable(false);
4117        if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
4118            postAnimationRunner();
4119        }
4120    }
4121
4122    private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
4123            @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
4124            boolean oldHolderDisappearing, boolean newHolderDisappearing) {
4125        oldHolder.setIsRecyclable(false);
4126        if (oldHolderDisappearing) {
4127            addAnimatingView(oldHolder);
4128        }
4129        if (oldHolder != newHolder) {
4130            if (newHolderDisappearing) {
4131                addAnimatingView(newHolder);
4132            }
4133            oldHolder.mShadowedHolder = newHolder;
4134            // old holder should disappear after animation ends
4135            addAnimatingView(oldHolder);
4136            mRecycler.unscrapView(oldHolder);
4137            newHolder.setIsRecyclable(false);
4138            newHolder.mShadowingHolder = oldHolder;
4139        }
4140        if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
4141            postAnimationRunner();
4142        }
4143    }
4144
4145    @Override
4146    protected void onLayout(boolean changed, int l, int t, int r, int b) {
4147        TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
4148        dispatchLayout();
4149        TraceCompat.endSection();
4150        mFirstLayoutComplete = true;
4151    }
4152
4153    @Override
4154    public void requestLayout() {
4155        if (mInterceptRequestLayoutDepth == 0 && !mLayoutFrozen) {
4156            super.requestLayout();
4157        } else {
4158            mLayoutWasDefered = true;
4159        }
4160    }
4161
4162    void markItemDecorInsetsDirty() {
4163        final int childCount = mChildHelper.getUnfilteredChildCount();
4164        for (int i = 0; i < childCount; i++) {
4165            final View child = mChildHelper.getUnfilteredChildAt(i);
4166            ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
4167        }
4168        mRecycler.markItemDecorInsetsDirty();
4169    }
4170
4171    @Override
4172    public void draw(Canvas c) {
4173        super.draw(c);
4174
4175        final int count = mItemDecorations.size();
4176        for (int i = 0; i < count; i++) {
4177            mItemDecorations.get(i).onDrawOver(c, this, mState);
4178        }
4179        // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
4180        // need find children closest to edges. Not sure if it is worth the effort.
4181        boolean needsInvalidate = false;
4182        if (mLeftGlow != null && !mLeftGlow.isFinished()) {
4183            final int restore = c.save();
4184            final int padding = mClipToPadding ? getPaddingBottom() : 0;
4185            c.rotate(270);
4186            c.translate(-getHeight() + padding, 0);
4187            needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
4188            c.restoreToCount(restore);
4189        }
4190        if (mTopGlow != null && !mTopGlow.isFinished()) {
4191            final int restore = c.save();
4192            if (mClipToPadding) {
4193                c.translate(getPaddingLeft(), getPaddingTop());
4194            }
4195            needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
4196            c.restoreToCount(restore);
4197        }
4198        if (mRightGlow != null && !mRightGlow.isFinished()) {
4199            final int restore = c.save();
4200            final int width = getWidth();
4201            final int padding = mClipToPadding ? getPaddingTop() : 0;
4202            c.rotate(90);
4203            c.translate(-padding, -width);
4204            needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
4205            c.restoreToCount(restore);
4206        }
4207        if (mBottomGlow != null && !mBottomGlow.isFinished()) {
4208            final int restore = c.save();
4209            c.rotate(180);
4210            if (mClipToPadding) {
4211                c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
4212            } else {
4213                c.translate(-getWidth(), -getHeight());
4214            }
4215            needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
4216            c.restoreToCount(restore);
4217        }
4218
4219        // If some views are animating, ItemDecorators are likely to move/change with them.
4220        // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
4221        // display lists are not invalidated.
4222        if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
4223                && mItemAnimator.isRunning()) {
4224            needsInvalidate = true;
4225        }
4226
4227        if (needsInvalidate) {
4228            ViewCompat.postInvalidateOnAnimation(this);
4229        }
4230    }
4231
4232    @Override
4233    public void onDraw(Canvas c) {
4234        super.onDraw(c);
4235
4236        final int count = mItemDecorations.size();
4237        for (int i = 0; i < count; i++) {
4238            mItemDecorations.get(i).onDraw(c, this, mState);
4239        }
4240    }
4241
4242    @Override
4243    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
4244        return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
4245    }
4246
4247    @Override
4248    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
4249        if (mLayout == null) {
4250            throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
4251        }
4252        return mLayout.generateDefaultLayoutParams();
4253    }
4254
4255    @Override
4256    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
4257        if (mLayout == null) {
4258            throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
4259        }
4260        return mLayout.generateLayoutParams(getContext(), attrs);
4261    }
4262
4263    @Override
4264    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
4265        if (mLayout == null) {
4266            throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
4267        }
4268        return mLayout.generateLayoutParams(p);
4269    }
4270
4271    /**
4272     * Returns true if RecyclerView is currently running some animations.
4273     * <p>
4274     * If you want to be notified when animations are finished, use
4275     * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
4276     *
4277     * @return True if there are some item animations currently running or waiting to be started.
4278     */
4279    public boolean isAnimating() {
4280        return mItemAnimator != null && mItemAnimator.isRunning();
4281    }
4282
4283    void saveOldPositions() {
4284        final int childCount = mChildHelper.getUnfilteredChildCount();
4285        for (int i = 0; i < childCount; i++) {
4286            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4287            if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
4288                throw new IllegalStateException("view holder cannot have position -1 unless it"
4289                        + " is removed" + exceptionLabel());
4290            }
4291            if (!holder.shouldIgnore()) {
4292                holder.saveOldPosition();
4293            }
4294        }
4295    }
4296
4297    void clearOldPositions() {
4298        final int childCount = mChildHelper.getUnfilteredChildCount();
4299        for (int i = 0; i < childCount; i++) {
4300            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4301            if (!holder.shouldIgnore()) {
4302                holder.clearOldPosition();
4303            }
4304        }
4305        mRecycler.clearOldPositions();
4306    }
4307
4308    void offsetPositionRecordsForMove(int from, int to) {
4309        final int childCount = mChildHelper.getUnfilteredChildCount();
4310        final int start, end, inBetweenOffset;
4311        if (from < to) {
4312            start = from;
4313            end = to;
4314            inBetweenOffset = -1;
4315        } else {
4316            start = to;
4317            end = from;
4318            inBetweenOffset = 1;
4319        }
4320
4321        for (int i = 0; i < childCount; i++) {
4322            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4323            if (holder == null || holder.mPosition < start || holder.mPosition > end) {
4324                continue;
4325            }
4326            if (DEBUG) {
4327                Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
4328                        + holder);
4329            }
4330            if (holder.mPosition == from) {
4331                holder.offsetPosition(to - from, false);
4332            } else {
4333                holder.offsetPosition(inBetweenOffset, false);
4334            }
4335
4336            mState.mStructureChanged = true;
4337        }
4338        mRecycler.offsetPositionRecordsForMove(from, to);
4339        requestLayout();
4340    }
4341
4342    void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
4343        final int childCount = mChildHelper.getUnfilteredChildCount();
4344        for (int i = 0; i < childCount; i++) {
4345            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4346            if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
4347                if (DEBUG) {
4348                    Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
4349                            + holder + " now at position " + (holder.mPosition + itemCount));
4350                }
4351                holder.offsetPosition(itemCount, false);
4352                mState.mStructureChanged = true;
4353            }
4354        }
4355        mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
4356        requestLayout();
4357    }
4358
4359    void offsetPositionRecordsForRemove(int positionStart, int itemCount,
4360            boolean applyToPreLayout) {
4361        final int positionEnd = positionStart + itemCount;
4362        final int childCount = mChildHelper.getUnfilteredChildCount();
4363        for (int i = 0; i < childCount; i++) {
4364            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4365            if (holder != null && !holder.shouldIgnore()) {
4366                if (holder.mPosition >= positionEnd) {
4367                    if (DEBUG) {
4368                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4369                                + " holder " + holder + " now at position "
4370                                + (holder.mPosition - itemCount));
4371                    }
4372                    holder.offsetPosition(-itemCount, applyToPreLayout);
4373                    mState.mStructureChanged = true;
4374                } else if (holder.mPosition >= positionStart) {
4375                    if (DEBUG) {
4376                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4377                                + " holder " + holder + " now REMOVED");
4378                    }
4379                    holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
4380                            applyToPreLayout);
4381                    mState.mStructureChanged = true;
4382                }
4383            }
4384        }
4385        mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
4386        requestLayout();
4387    }
4388
4389    /**
4390     * Rebind existing views for the given range, or create as needed.
4391     *
4392     * @param positionStart Adapter position to start at
4393     * @param itemCount Number of views that must explicitly be rebound
4394     */
4395    void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
4396        final int childCount = mChildHelper.getUnfilteredChildCount();
4397        final int positionEnd = positionStart + itemCount;
4398
4399        for (int i = 0; i < childCount; i++) {
4400            final View child = mChildHelper.getUnfilteredChildAt(i);
4401            final ViewHolder holder = getChildViewHolderInt(child);
4402            if (holder == null || holder.shouldIgnore()) {
4403                continue;
4404            }
4405            if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
4406                // We re-bind these view holders after pre-processing is complete so that
4407                // ViewHolders have their final positions assigned.
4408                holder.addFlags(ViewHolder.FLAG_UPDATE);
4409                holder.addChangePayload(payload);
4410                // lp cannot be null since we get ViewHolder from it.
4411                ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
4412            }
4413        }
4414        mRecycler.viewRangeUpdate(positionStart, itemCount);
4415    }
4416
4417    boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
4418        return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
4419                viewHolder.getUnmodifiedPayloads());
4420    }
4421
4422    /**
4423     * Processes the fact that, as far as we can tell, the data set has completely changed.
4424     *
4425     * <ul>
4426     *   <li>Once layout occurs, all attached items should be discarded or animated.
4427     *   <li>Attached items are labeled as invalid.
4428     *   <li>Because items may still be prefetched between a "data set completely changed"
4429     *       event and a layout event, all cached items are discarded.
4430     * </ul>
4431     *
4432     * @param dispatchItemsChanged Whether to call
4433     * {@link LayoutManager#onItemsChanged(RecyclerView)} during measure/layout.
4434     */
4435    void processDataSetCompletelyChanged(boolean dispatchItemsChanged) {
4436        mDispatchItemsChangedEvent |= dispatchItemsChanged;
4437        mDataSetHasChangedAfterLayout = true;
4438        markKnownViewsInvalid();
4439    }
4440
4441    /**
4442     * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
4443     * data change event.
4444     */
4445    void markKnownViewsInvalid() {
4446        final int childCount = mChildHelper.getUnfilteredChildCount();
4447        for (int i = 0; i < childCount; i++) {
4448            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4449            if (holder != null && !holder.shouldIgnore()) {
4450                holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
4451            }
4452        }
4453        markItemDecorInsetsDirty();
4454        mRecycler.markKnownViewsInvalid();
4455    }
4456
4457    /**
4458     * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
4459     * will trigger a {@link #requestLayout()} call.
4460     */
4461    public void invalidateItemDecorations() {
4462        if (mItemDecorations.size() == 0) {
4463            return;
4464        }
4465        if (mLayout != null) {
4466            mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
4467                    + " or layout");
4468        }
4469        markItemDecorInsetsDirty();
4470        requestLayout();
4471    }
4472
4473    /**
4474     * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
4475     * focus even if the View representing the Item is replaced during a layout calculation.
4476     * <p>
4477     * By default, this value is {@code true}.
4478     *
4479     * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
4480     * focus.
4481     *
4482     * @see #setPreserveFocusAfterLayout(boolean)
4483     */
4484    public boolean getPreserveFocusAfterLayout() {
4485        return mPreserveFocusAfterLayout;
4486    }
4487
4488    /**
4489     * Set whether the RecyclerView should try to keep the same Item focused after a layout
4490     * calculation or not.
4491     * <p>
4492     * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
4493     * views may lose focus during a layout calculation as their state changes or they are replaced
4494     * with another view due to type change or animation. In these cases, RecyclerView can request
4495     * focus on the new view automatically.
4496     *
4497     * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
4498     *                                 layout calculations. Defaults to true.
4499     *
4500     * @see #getPreserveFocusAfterLayout()
4501     */
4502    public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
4503        mPreserveFocusAfterLayout = preserveFocusAfterLayout;
4504    }
4505
4506    /**
4507     * Retrieve the {@link ViewHolder} for the given child view.
4508     *
4509     * @param child Child of this RecyclerView to query for its ViewHolder
4510     * @return The child view's ViewHolder
4511     */
4512    public ViewHolder getChildViewHolder(@NonNull View child) {
4513        final ViewParent parent = child.getParent();
4514        if (parent != null && parent != this) {
4515            throw new IllegalArgumentException("View " + child + " is not a direct child of "
4516                    + this);
4517        }
4518        return getChildViewHolderInt(child);
4519    }
4520
4521    /**
4522     * Traverses the ancestors of the given view and returns the item view that contains it and
4523     * also a direct child of the RecyclerView. This returned view can be used to get the
4524     * ViewHolder by calling {@link #getChildViewHolder(View)}.
4525     *
4526     * @param view The view that is a descendant of the RecyclerView.
4527     *
4528     * @return The direct child of the RecyclerView which contains the given view or null if the
4529     * provided view is not a descendant of this RecyclerView.
4530     *
4531     * @see #getChildViewHolder(View)
4532     * @see #findContainingViewHolder(View)
4533     */
4534    @Nullable
4535    public View findContainingItemView(@NonNull View view) {
4536        ViewParent parent = view.getParent();
4537        while (parent != null && parent != this && parent instanceof View) {
4538            view = (View) parent;
4539            parent = view.getParent();
4540        }
4541        return parent == this ? view : null;
4542    }
4543
4544    /**
4545     * Returns the ViewHolder that contains the given view.
4546     *
4547     * @param view The view that is a descendant of the RecyclerView.
4548     *
4549     * @return The ViewHolder that contains the given view or null if the provided view is not a
4550     * descendant of this RecyclerView.
4551     */
4552    @Nullable
4553    public ViewHolder findContainingViewHolder(@NonNull View view) {
4554        View itemView = findContainingItemView(view);
4555        return itemView == null ? null : getChildViewHolder(itemView);
4556    }
4557
4558
4559    static ViewHolder getChildViewHolderInt(View child) {
4560        if (child == null) {
4561            return null;
4562        }
4563        return ((LayoutParams) child.getLayoutParams()).mViewHolder;
4564    }
4565
4566    /**
4567     * @deprecated use {@link #getChildAdapterPosition(View)} or
4568     * {@link #getChildLayoutPosition(View)}.
4569     */
4570    @Deprecated
4571    public int getChildPosition(@NonNull View child) {
4572        return getChildAdapterPosition(child);
4573    }
4574
4575    /**
4576     * Return the adapter position that the given child view corresponds to.
4577     *
4578     * @param child Child View to query
4579     * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
4580     */
4581    public int getChildAdapterPosition(@NonNull View child) {
4582        final ViewHolder holder = getChildViewHolderInt(child);
4583        return holder != null ? holder.getAdapterPosition() : NO_POSITION;
4584    }
4585
4586    /**
4587     * Return the adapter position of the given child view as of the latest completed layout pass.
4588     * <p>
4589     * This position may not be equal to Item's adapter position if there are pending changes
4590     * in the adapter which have not been reflected to the layout yet.
4591     *
4592     * @param child Child View to query
4593     * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
4594     * the View is representing a removed item.
4595     */
4596    public int getChildLayoutPosition(@NonNull View child) {
4597        final ViewHolder holder = getChildViewHolderInt(child);
4598        return holder != null ? holder.getLayoutPosition() : NO_POSITION;
4599    }
4600
4601    /**
4602     * Return the stable item id that the given child view corresponds to.
4603     *
4604     * @param child Child View to query
4605     * @return Item id corresponding to the given view or {@link #NO_ID}
4606     */
4607    public long getChildItemId(@NonNull View child) {
4608        if (mAdapter == null || !mAdapter.hasStableIds()) {
4609            return NO_ID;
4610        }
4611        final ViewHolder holder = getChildViewHolderInt(child);
4612        return holder != null ? holder.getItemId() : NO_ID;
4613    }
4614
4615    /**
4616     * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
4617     * {@link #findViewHolderForAdapterPosition(int)}
4618     */
4619    @Deprecated
4620    public ViewHolder findViewHolderForPosition(int position) {
4621        return findViewHolderForPosition(position, false);
4622    }
4623
4624    /**
4625     * Return the ViewHolder for the item in the given position of the data set as of the latest
4626     * layout pass.
4627     * <p>
4628     * This method checks only the children of RecyclerView. If the item at the given
4629     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4630     * <p>
4631     * Note that when Adapter contents change, ViewHolder positions are not updated until the
4632     * next layout calculation. If there are pending adapter updates, the return value of this
4633     * method may not match your adapter contents. You can use
4634     * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
4635     * <p>
4636     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4637     * with the same layout position representing the same Item. In this case, the updated
4638     * ViewHolder will be returned.
4639     *
4640     * @param position The position of the item in the data set of the adapter
4641     * @return The ViewHolder at <code>position</code> or null if there is no such item
4642     */
4643    public ViewHolder findViewHolderForLayoutPosition(int position) {
4644        return findViewHolderForPosition(position, false);
4645    }
4646
4647    /**
4648     * Return the ViewHolder for the item in the given position of the data set. Unlike
4649     * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
4650     * adapter changes that may not be reflected to the layout yet. On the other hand, if
4651     * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
4652     * calculated yet, this method will return <code>null</code> since the new positions of views
4653     * are unknown until the layout is calculated.
4654     * <p>
4655     * This method checks only the children of RecyclerView. If the item at the given
4656     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4657     * <p>
4658     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4659     * representing the same Item. In this case, the updated ViewHolder will be returned.
4660     *
4661     * @param position The position of the item in the data set of the adapter
4662     * @return The ViewHolder at <code>position</code> or null if there is no such item
4663     */
4664    public ViewHolder findViewHolderForAdapterPosition(int position) {
4665        if (mDataSetHasChangedAfterLayout) {
4666            return null;
4667        }
4668        final int childCount = mChildHelper.getUnfilteredChildCount();
4669        // hidden VHs are not preferred but if that is the only one we find, we rather return it
4670        ViewHolder hidden = null;
4671        for (int i = 0; i < childCount; i++) {
4672            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4673            if (holder != null && !holder.isRemoved()
4674                    && getAdapterPositionFor(holder) == position) {
4675                if (mChildHelper.isHidden(holder.itemView)) {
4676                    hidden = holder;
4677                } else {
4678                    return holder;
4679                }
4680            }
4681        }
4682        return hidden;
4683    }
4684
4685    ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
4686        final int childCount = mChildHelper.getUnfilteredChildCount();
4687        ViewHolder hidden = null;
4688        for (int i = 0; i < childCount; i++) {
4689            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4690            if (holder != null && !holder.isRemoved()) {
4691                if (checkNewPosition) {
4692                    if (holder.mPosition != position) {
4693                        continue;
4694                    }
4695                } else if (holder.getLayoutPosition() != position) {
4696                    continue;
4697                }
4698                if (mChildHelper.isHidden(holder.itemView)) {
4699                    hidden = holder;
4700                } else {
4701                    return holder;
4702                }
4703            }
4704        }
4705        // This method should not query cached views. It creates a problem during adapter updates
4706        // when we are dealing with already laid out views. Also, for the public method, it is more
4707        // reasonable to return null if position is not laid out.
4708        return hidden;
4709    }
4710
4711    /**
4712     * Return the ViewHolder for the item with the given id. The RecyclerView must
4713     * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
4714     * return a non-null value.
4715     * <p>
4716     * This method checks only the children of RecyclerView. If the item with the given
4717     * <code>id</code> is not laid out, it <em>will not</em> create a new one.
4718     *
4719     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
4720     * same id. In this case, the updated ViewHolder will be returned.
4721     *
4722     * @param id The id for the requested item
4723     * @return The ViewHolder with the given <code>id</code> or null if there is no such item
4724     */
4725    public ViewHolder findViewHolderForItemId(long id) {
4726        if (mAdapter == null || !mAdapter.hasStableIds()) {
4727            return null;
4728        }
4729        final int childCount = mChildHelper.getUnfilteredChildCount();
4730        ViewHolder hidden = null;
4731        for (int i = 0; i < childCount; i++) {
4732            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4733            if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
4734                if (mChildHelper.isHidden(holder.itemView)) {
4735                    hidden = holder;
4736                } else {
4737                    return holder;
4738                }
4739            }
4740        }
4741        return hidden;
4742    }
4743
4744    /**
4745     * Find the topmost view under the given point.
4746     *
4747     * @param x Horizontal position in pixels to search
4748     * @param y Vertical position in pixels to search
4749     * @return The child view under (x, y) or null if no matching child is found
4750     */
4751    @Nullable
4752    public View findChildViewUnder(float x, float y) {
4753        final int count = mChildHelper.getChildCount();
4754        for (int i = count - 1; i >= 0; i--) {
4755            final View child = mChildHelper.getChildAt(i);
4756            final float translationX = child.getTranslationX();
4757            final float translationY = child.getTranslationY();
4758            if (x >= child.getLeft() + translationX
4759                    && x <= child.getRight() + translationX
4760                    && y >= child.getTop() + translationY
4761                    && y <= child.getBottom() + translationY) {
4762                return child;
4763            }
4764        }
4765        return null;
4766    }
4767
4768    @Override
4769    public boolean drawChild(Canvas canvas, View child, long drawingTime) {
4770        return super.drawChild(canvas, child, drawingTime);
4771    }
4772
4773    /**
4774     * Offset the bounds of all child views by <code>dy</code> pixels.
4775     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4776     *
4777     * @param dy Vertical pixel offset to apply to the bounds of all child views
4778     */
4779    public void offsetChildrenVertical(@Px int dy) {
4780        final int childCount = mChildHelper.getChildCount();
4781        for (int i = 0; i < childCount; i++) {
4782            mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
4783        }
4784    }
4785
4786    /**
4787     * Called when an item view is attached to this RecyclerView.
4788     *
4789     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4790     * of child views as they become attached. This will be called before a
4791     * {@link LayoutManager} measures or lays out the view and is a good time to perform these
4792     * changes.</p>
4793     *
4794     * @param child Child view that is now attached to this RecyclerView and its associated window
4795     */
4796    public void onChildAttachedToWindow(@NonNull View child) {
4797    }
4798
4799    /**
4800     * Called when an item view is detached from this RecyclerView.
4801     *
4802     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4803     * of child views as they become detached. This will be called as a
4804     * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
4805     *
4806     * @param child Child view that is now detached from this RecyclerView and its associated window
4807     */
4808    public void onChildDetachedFromWindow(@NonNull View child) {
4809    }
4810
4811    /**
4812     * Offset the bounds of all child views by <code>dx</code> pixels.
4813     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4814     *
4815     * @param dx Horizontal pixel offset to apply to the bounds of all child views
4816     */
4817    public void offsetChildrenHorizontal(@Px int dx) {
4818        final int childCount = mChildHelper.getChildCount();
4819        for (int i = 0; i < childCount; i++) {
4820            mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
4821        }
4822    }
4823
4824    /**
4825     * Returns the bounds of the view including its decoration and margins.
4826     *
4827     * @param view The view element to check
4828     * @param outBounds A rect that will receive the bounds of the element including its
4829     *                  decoration and margins.
4830     */
4831    public void getDecoratedBoundsWithMargins(@NonNull View view, @NonNull Rect outBounds) {
4832        getDecoratedBoundsWithMarginsInt(view, outBounds);
4833    }
4834
4835    static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
4836        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
4837        final Rect insets = lp.mDecorInsets;
4838        outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
4839                view.getTop() - insets.top - lp.topMargin,
4840                view.getRight() + insets.right + lp.rightMargin,
4841                view.getBottom() + insets.bottom + lp.bottomMargin);
4842    }
4843
4844    Rect getItemDecorInsetsForChild(View child) {
4845        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
4846        if (!lp.mInsetsDirty) {
4847            return lp.mDecorInsets;
4848        }
4849
4850        if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
4851            // changed/invalid items should not be updated until they are rebound.
4852            return lp.mDecorInsets;
4853        }
4854        final Rect insets = lp.mDecorInsets;
4855        insets.set(0, 0, 0, 0);
4856        final int decorCount = mItemDecorations.size();
4857        for (int i = 0; i < decorCount; i++) {
4858            mTempRect.set(0, 0, 0, 0);
4859            mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
4860            insets.left += mTempRect.left;
4861            insets.top += mTempRect.top;
4862            insets.right += mTempRect.right;
4863            insets.bottom += mTempRect.bottom;
4864        }
4865        lp.mInsetsDirty = false;
4866        return insets;
4867    }
4868
4869    /**
4870     * Called when the scroll position of this RecyclerView changes. Subclasses should use
4871     * this method to respond to scrolling within the adapter's data set instead of an explicit
4872     * listener.
4873     *
4874     * <p>This method will always be invoked before listeners. If a subclass needs to perform
4875     * any additional upkeep or bookkeeping after scrolling but before listeners run,
4876     * this is a good place to do so.</p>
4877     *
4878     * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
4879     * the distance scrolled in either direction within the adapter's data set instead of absolute
4880     * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
4881     * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
4882     * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
4883     * do not correspond to the data set scroll position. However, some subclasses may choose
4884     * to use these fields as special offsets.</p>
4885     *
4886     * @param dx horizontal distance scrolled in pixels
4887     * @param dy vertical distance scrolled in pixels
4888     */
4889    public void onScrolled(@Px int dx, @Px int dy) {
4890        // Do nothing
4891    }
4892
4893    void dispatchOnScrolled(int hresult, int vresult) {
4894        mDispatchScrollCounter++;
4895        // Pass the current scrollX/scrollY values; no actual change in these properties occurred
4896        // but some general-purpose code may choose to respond to changes this way.
4897        final int scrollX = getScrollX();
4898        final int scrollY = getScrollY();
4899        onScrollChanged(scrollX, scrollY, scrollX, scrollY);
4900
4901        // Pass the real deltas to onScrolled, the RecyclerView-specific method.
4902        onScrolled(hresult, vresult);
4903
4904        // Invoke listeners last. Subclassed view methods always handle the event first.
4905        // All internal state is consistent by the time listeners are invoked.
4906        if (mScrollListener != null) {
4907            mScrollListener.onScrolled(this, hresult, vresult);
4908        }
4909        if (mScrollListeners != null) {
4910            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4911                mScrollListeners.get(i).onScrolled(this, hresult, vresult);
4912            }
4913        }
4914        mDispatchScrollCounter--;
4915    }
4916
4917    /**
4918     * Called when the scroll state of this RecyclerView changes. Subclasses should use this
4919     * method to respond to state changes instead of an explicit listener.
4920     *
4921     * <p>This method will always be invoked before listeners, but after the LayoutManager
4922     * responds to the scroll state change.</p>
4923     *
4924     * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
4925     *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
4926     */
4927    public void onScrollStateChanged(int state) {
4928        // Do nothing
4929    }
4930
4931    void dispatchOnScrollStateChanged(int state) {
4932        // Let the LayoutManager go first; this allows it to bring any properties into
4933        // a consistent state before the RecyclerView subclass responds.
4934        if (mLayout != null) {
4935            mLayout.onScrollStateChanged(state);
4936        }
4937
4938        // Let the RecyclerView subclass handle this event next; any LayoutManager property
4939        // changes will be reflected by this time.
4940        onScrollStateChanged(state);
4941
4942        // Listeners go last. All other internal state is consistent by this point.
4943        if (mScrollListener != null) {
4944            mScrollListener.onScrollStateChanged(this, state);
4945        }
4946        if (mScrollListeners != null) {
4947            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4948                mScrollListeners.get(i).onScrollStateChanged(this, state);
4949            }
4950        }
4951    }
4952
4953    /**
4954     * Returns whether there are pending adapter updates which are not yet applied to the layout.
4955     * <p>
4956     * If this method returns <code>true</code>, it means that what user is currently seeing may not
4957     * reflect them adapter contents (depending on what has changed).
4958     * You may use this information to defer or cancel some operations.
4959     * <p>
4960     * This method returns true if RecyclerView has not yet calculated the first layout after it is
4961     * attached to the Window or the Adapter has been replaced.
4962     *
4963     * @return True if there are some adapter updates which are not yet reflected to layout or false
4964     * if layout is up to date.
4965     */
4966    public boolean hasPendingAdapterUpdates() {
4967        return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
4968                || mAdapterHelper.hasPendingUpdates();
4969    }
4970
4971    class ViewFlinger implements Runnable {
4972        private int mLastFlingX;
4973        private int mLastFlingY;
4974        private OverScroller mScroller;
4975        Interpolator mInterpolator = sQuinticInterpolator;
4976
4977        // When set to true, postOnAnimation callbacks are delayed until the run method completes
4978        private boolean mEatRunOnAnimationRequest = false;
4979
4980        // Tracks if postAnimationCallback should be re-attached when it is done
4981        private boolean mReSchedulePostAnimationCallback = false;
4982
4983        ViewFlinger() {
4984            mScroller = new OverScroller(getContext(), sQuinticInterpolator);
4985        }
4986
4987        @Override
4988        public void run() {
4989            if (mLayout == null) {
4990                stop();
4991                return; // no layout, cannot scroll.
4992            }
4993            disableRunOnAnimationRequests();
4994            consumePendingUpdateOperations();
4995            // keep a local reference so that if it is changed during onAnimation method, it won't
4996            // cause unexpected behaviors
4997            final OverScroller scroller = mScroller;
4998            final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
4999            if (scroller.computeScrollOffset()) {
5000                final int[] scrollConsumed = mScrollConsumed;
5001                final int x = scroller.getCurrX();
5002                final int y = scroller.getCurrY();
5003                int dx = x - mLastFlingX;
5004                int dy = y - mLastFlingY;
5005                int hresult = 0;
5006                int vresult = 0;
5007                mLastFlingX = x;
5008                mLastFlingY = y;
5009                int overscrollX = 0, overscrollY = 0;
5010
5011                if (dispatchNestedPreScroll(dx, dy, scrollConsumed, null, TYPE_NON_TOUCH)) {
5012                    dx -= scrollConsumed[0];
5013                    dy -= scrollConsumed[1];
5014                }
5015
5016                if (mAdapter != null) {
5017                    scrollStep(dx, dy, mScrollStepConsumed);
5018                    hresult = mScrollStepConsumed[0];
5019                    vresult = mScrollStepConsumed[1];
5020                    overscrollX = dx - hresult;
5021                    overscrollY = dy - vresult;
5022
5023                    if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
5024                            && smoothScroller.isRunning()) {
5025                        final int adapterSize = mState.getItemCount();
5026                        if (adapterSize == 0) {
5027                            smoothScroller.stop();
5028                        } else if (smoothScroller.getTargetPosition() >= adapterSize) {
5029                            smoothScroller.setTargetPosition(adapterSize - 1);
5030                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
5031                        } else {
5032                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
5033                        }
5034                    }
5035                }
5036                if (!mItemDecorations.isEmpty()) {
5037                    invalidate();
5038                }
5039                if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
5040                    considerReleasingGlowsOnScroll(dx, dy);
5041                }
5042
5043                if (!dispatchNestedScroll(hresult, vresult, overscrollX, overscrollY, null,
5044                        TYPE_NON_TOUCH)
5045                        && (overscrollX != 0 || overscrollY != 0)) {
5046                    final int vel = (int) scroller.getCurrVelocity();
5047
5048                    int velX = 0;
5049                    if (overscrollX != x) {
5050                        velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
5051                    }
5052
5053                    int velY = 0;
5054                    if (overscrollY != y) {
5055                        velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
5056                    }
5057
5058                    if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
5059                        absorbGlows(velX, velY);
5060                    }
5061                    if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
5062                            && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
5063                        scroller.abortAnimation();
5064                    }
5065                }
5066                if (hresult != 0 || vresult != 0) {
5067                    dispatchOnScrolled(hresult, vresult);
5068                }
5069
5070                if (!awakenScrollBars()) {
5071                    invalidate();
5072                }
5073
5074                final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
5075                        && vresult == dy;
5076                final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
5077                        && hresult == dx;
5078                final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
5079                        || fullyConsumedVertical;
5080
5081                if (scroller.isFinished() || (!fullyConsumedAny
5082                        && !hasNestedScrollingParent(TYPE_NON_TOUCH))) {
5083                    // setting state to idle will stop this.
5084                    setScrollState(SCROLL_STATE_IDLE);
5085                    if (ALLOW_THREAD_GAP_WORK) {
5086                        mPrefetchRegistry.clearPrefetchPositions();
5087                    }
5088                    stopNestedScroll(TYPE_NON_TOUCH);
5089                } else {
5090                    postOnAnimation();
5091                    if (mGapWorker != null) {
5092                        mGapWorker.postFromTraversal(RecyclerView.this, dx, dy);
5093                    }
5094                }
5095            }
5096            // call this after the onAnimation is complete not to have inconsistent callbacks etc.
5097            if (smoothScroller != null) {
5098                if (smoothScroller.isPendingInitialRun()) {
5099                    smoothScroller.onAnimation(0, 0);
5100                }
5101                if (!mReSchedulePostAnimationCallback) {
5102                    smoothScroller.stop(); //stop if it does not trigger any scroll
5103                }
5104            }
5105            enableRunOnAnimationRequests();
5106        }
5107
5108        private void disableRunOnAnimationRequests() {
5109            mReSchedulePostAnimationCallback = false;
5110            mEatRunOnAnimationRequest = true;
5111        }
5112
5113        private void enableRunOnAnimationRequests() {
5114            mEatRunOnAnimationRequest = false;
5115            if (mReSchedulePostAnimationCallback) {
5116                postOnAnimation();
5117            }
5118        }
5119
5120        void postOnAnimation() {
5121            if (mEatRunOnAnimationRequest) {
5122                mReSchedulePostAnimationCallback = true;
5123            } else {
5124                removeCallbacks(this);
5125                ViewCompat.postOnAnimation(RecyclerView.this, this);
5126            }
5127        }
5128
5129        public void fling(int velocityX, int velocityY) {
5130            setScrollState(SCROLL_STATE_SETTLING);
5131            mLastFlingX = mLastFlingY = 0;
5132            mScroller.fling(0, 0, velocityX, velocityY,
5133                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
5134            postOnAnimation();
5135        }
5136
5137        public void smoothScrollBy(int dx, int dy) {
5138            smoothScrollBy(dx, dy, 0, 0);
5139        }
5140
5141        public void smoothScrollBy(int dx, int dy, int vx, int vy) {
5142            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
5143        }
5144
5145        private float distanceInfluenceForSnapDuration(float f) {
5146            f -= 0.5f; // center the values about 0.
5147            f *= 0.3f * (float) Math.PI / 2.0f;
5148            return (float) Math.sin(f);
5149        }
5150
5151        private int computeScrollDuration(int dx, int dy, int vx, int vy) {
5152            final int absDx = Math.abs(dx);
5153            final int absDy = Math.abs(dy);
5154            final boolean horizontal = absDx > absDy;
5155            final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
5156            final int delta = (int) Math.sqrt(dx * dx + dy * dy);
5157            final int containerSize = horizontal ? getWidth() : getHeight();
5158            final int halfContainerSize = containerSize / 2;
5159            final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
5160            final float distance = halfContainerSize + halfContainerSize
5161                    * distanceInfluenceForSnapDuration(distanceRatio);
5162
5163            final int duration;
5164            if (velocity > 0) {
5165                duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
5166            } else {
5167                float absDelta = (float) (horizontal ? absDx : absDy);
5168                duration = (int) (((absDelta / containerSize) + 1) * 300);
5169            }
5170            return Math.min(duration, MAX_SCROLL_DURATION);
5171        }
5172
5173        public void smoothScrollBy(int dx, int dy, int duration) {
5174            smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
5175        }
5176
5177        public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
5178            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, 0, 0),
5179                    interpolator == null ? sQuinticInterpolator : interpolator);
5180        }
5181
5182        public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
5183            if (mInterpolator != interpolator) {
5184                mInterpolator = interpolator;
5185                mScroller = new OverScroller(getContext(), interpolator);
5186            }
5187            setScrollState(SCROLL_STATE_SETTLING);
5188            mLastFlingX = mLastFlingY = 0;
5189            mScroller.startScroll(0, 0, dx, dy, duration);
5190            if (Build.VERSION.SDK_INT < 23) {
5191                // b/64931938 before API 23, startScroll() does not reset getCurX()/getCurY()
5192                // to start values, which causes fillRemainingScrollValues() put in obsolete values
5193                // for LayoutManager.onLayoutChildren().
5194                mScroller.computeScrollOffset();
5195            }
5196            postOnAnimation();
5197        }
5198
5199        public void stop() {
5200            removeCallbacks(this);
5201            mScroller.abortAnimation();
5202        }
5203
5204    }
5205
5206    void repositionShadowingViews() {
5207        // Fix up shadow views used by change animations
5208        int count = mChildHelper.getChildCount();
5209        for (int i = 0; i < count; i++) {
5210            View view = mChildHelper.getChildAt(i);
5211            ViewHolder holder = getChildViewHolder(view);
5212            if (holder != null && holder.mShadowingHolder != null) {
5213                View shadowingView = holder.mShadowingHolder.itemView;
5214                int left = view.getLeft();
5215                int top = view.getTop();
5216                if (left != shadowingView.getLeft() ||  top != shadowingView.getTop()) {
5217                    shadowingView.layout(left, top,
5218                            left + shadowingView.getWidth(),
5219                            top + shadowingView.getHeight());
5220                }
5221            }
5222        }
5223    }
5224
5225    private class RecyclerViewDataObserver extends AdapterDataObserver {
5226        RecyclerViewDataObserver() {
5227        }
5228
5229        @Override
5230        public void onChanged() {
5231            assertNotInLayoutOrScroll(null);
5232            mState.mStructureChanged = true;
5233
5234            processDataSetCompletelyChanged(true);
5235            if (!mAdapterHelper.hasPendingUpdates()) {
5236                requestLayout();
5237            }
5238        }
5239
5240        @Override
5241        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
5242            assertNotInLayoutOrScroll(null);
5243            if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
5244                triggerUpdateProcessor();
5245            }
5246        }
5247
5248        @Override
5249        public void onItemRangeInserted(int positionStart, int itemCount) {
5250            assertNotInLayoutOrScroll(null);
5251            if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
5252                triggerUpdateProcessor();
5253            }
5254        }
5255
5256        @Override
5257        public void onItemRangeRemoved(int positionStart, int itemCount) {
5258            assertNotInLayoutOrScroll(null);
5259            if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
5260                triggerUpdateProcessor();
5261            }
5262        }
5263
5264        @Override
5265        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
5266            assertNotInLayoutOrScroll(null);
5267            if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
5268                triggerUpdateProcessor();
5269            }
5270        }
5271
5272        void triggerUpdateProcessor() {
5273            if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
5274                ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
5275            } else {
5276                mAdapterUpdateDuringMeasure = true;
5277                requestLayout();
5278            }
5279        }
5280    }
5281
5282    /**
5283     * EdgeEffectFactory lets you customize the over-scroll edge effect for RecyclerViews.
5284     *
5285     * @see RecyclerView#setEdgeEffectFactory(EdgeEffectFactory)
5286     */
5287    public static class EdgeEffectFactory {
5288
5289        @Retention(RetentionPolicy.SOURCE)
5290        @IntDef({DIRECTION_LEFT, DIRECTION_TOP, DIRECTION_RIGHT, DIRECTION_BOTTOM})
5291        public @interface EdgeDirection {}
5292
5293        /**
5294         * Direction constant for the left edge
5295         */
5296        public static final int DIRECTION_LEFT = 0;
5297
5298        /**
5299         * Direction constant for the top edge
5300         */
5301        public static final int DIRECTION_TOP = 1;
5302
5303        /**
5304         * Direction constant for the right edge
5305         */
5306        public static final int DIRECTION_RIGHT = 2;
5307
5308        /**
5309         * Direction constant for the bottom edge
5310         */
5311        public static final int DIRECTION_BOTTOM = 3;
5312
5313        /**
5314         * Create a new EdgeEffect for the provided direction.
5315         */
5316        protected @NonNull EdgeEffect createEdgeEffect(@NonNull RecyclerView view,
5317                @EdgeDirection int direction) {
5318            return new EdgeEffect(view.getContext());
5319        }
5320    }
5321
5322    /**
5323     * RecycledViewPool lets you share Views between multiple RecyclerViews.
5324     * <p>
5325     * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
5326     * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
5327     * <p>
5328     * RecyclerView automatically creates a pool for itself if you don't provide one.
5329     */
5330    public static class RecycledViewPool {
5331        private static final int DEFAULT_MAX_SCRAP = 5;
5332
5333        /**
5334         * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
5335         *
5336         * Note that this tracks running averages of create/bind time across all RecyclerViews
5337         * (and, indirectly, Adapters) that use this pool.
5338         *
5339         * 1) This enables us to track average create and bind times across multiple adapters. Even
5340         * though create (and especially bind) may behave differently for different Adapter
5341         * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
5342         *
5343         * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
5344         * false for all other views of its type for the same deadline. This prevents items
5345         * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
5346         */
5347        static class ScrapData {
5348            final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
5349            int mMaxScrap = DEFAULT_MAX_SCRAP;
5350            long mCreateRunningAverageNs = 0;
5351            long mBindRunningAverageNs = 0;
5352        }
5353        SparseArray<ScrapData> mScrap = new SparseArray<>();
5354
5355        private int mAttachCount = 0;
5356
5357        /**
5358         * Discard all ViewHolders.
5359         */
5360        public void clear() {
5361            for (int i = 0; i < mScrap.size(); i++) {
5362                ScrapData data = mScrap.valueAt(i);
5363                data.mScrapHeap.clear();
5364            }
5365        }
5366
5367        /**
5368         * Sets the maximum number of ViewHolders to hold in the pool before discarding.
5369         *
5370         * @param viewType ViewHolder Type
5371         * @param max Maximum number
5372         */
5373        public void setMaxRecycledViews(int viewType, int max) {
5374            ScrapData scrapData = getScrapDataForType(viewType);
5375            scrapData.mMaxScrap = max;
5376            final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
5377            while (scrapHeap.size() > max) {
5378                scrapHeap.remove(scrapHeap.size() - 1);
5379            }
5380        }
5381
5382        /**
5383         * Returns the current number of Views held by the RecycledViewPool of the given view type.
5384         */
5385        public int getRecycledViewCount(int viewType) {
5386            return getScrapDataForType(viewType).mScrapHeap.size();
5387        }
5388
5389        /**
5390         * Acquire a ViewHolder of the specified type from the pool, or {@code null} if none are
5391         * present.
5392         *
5393         * @param viewType ViewHolder type.
5394         * @return ViewHolder of the specified type acquired from the pool, or {@code null} if none
5395         * are present.
5396         */
5397        @Nullable
5398        public ViewHolder getRecycledView(int viewType) {
5399            final ScrapData scrapData = mScrap.get(viewType);
5400            if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
5401                final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
5402                return scrapHeap.remove(scrapHeap.size() - 1);
5403            }
5404            return null;
5405        }
5406
5407        /**
5408         * Total number of ViewHolders held by the pool.
5409         *
5410         * @return Number of ViewHolders held by the pool.
5411         */
5412        int size() {
5413            int count = 0;
5414            for (int i = 0; i < mScrap.size(); i++) {
5415                ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
5416                if (viewHolders != null) {
5417                    count += viewHolders.size();
5418                }
5419            }
5420            return count;
5421        }
5422
5423        /**
5424         * Add a scrap ViewHolder to the pool.
5425         * <p>
5426         * If the pool is already full for that ViewHolder's type, it will be immediately discarded.
5427         *
5428         * @param scrap ViewHolder to be added to the pool.
5429         */
5430        public void putRecycledView(ViewHolder scrap) {
5431            final int viewType = scrap.getItemViewType();
5432            final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
5433            if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
5434                return;
5435            }
5436            if (DEBUG && scrapHeap.contains(scrap)) {
5437                throw new IllegalArgumentException("this scrap item already exists");
5438            }
5439            scrap.resetInternal();
5440            scrapHeap.add(scrap);
5441        }
5442
5443        long runningAverage(long oldAverage, long newValue) {
5444            if (oldAverage == 0) {
5445                return newValue;
5446            }
5447            return (oldAverage / 4 * 3) + (newValue / 4);
5448        }
5449
5450        void factorInCreateTime(int viewType, long createTimeNs) {
5451            ScrapData scrapData = getScrapDataForType(viewType);
5452            scrapData.mCreateRunningAverageNs = runningAverage(
5453                    scrapData.mCreateRunningAverageNs, createTimeNs);
5454        }
5455
5456        void factorInBindTime(int viewType, long bindTimeNs) {
5457            ScrapData scrapData = getScrapDataForType(viewType);
5458            scrapData.mBindRunningAverageNs = runningAverage(
5459                    scrapData.mBindRunningAverageNs, bindTimeNs);
5460        }
5461
5462        boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5463            long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
5464            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5465        }
5466
5467        boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5468            long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
5469            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5470        }
5471
5472        void attach() {
5473            mAttachCount++;
5474        }
5475
5476        void detach() {
5477            mAttachCount--;
5478        }
5479
5480
5481        /**
5482         * Detaches the old adapter and attaches the new one.
5483         * <p>
5484         * RecycledViewPool will clear its cache if it has only one adapter attached and the new
5485         * adapter uses a different ViewHolder than the oldAdapter.
5486         *
5487         * @param oldAdapter The previous adapter instance. Will be detached.
5488         * @param newAdapter The new adapter instance. Will be attached.
5489         * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
5490         *                               ViewHolder and view types.
5491         */
5492        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
5493                boolean compatibleWithPrevious) {
5494            if (oldAdapter != null) {
5495                detach();
5496            }
5497            if (!compatibleWithPrevious && mAttachCount == 0) {
5498                clear();
5499            }
5500            if (newAdapter != null) {
5501                attach();
5502            }
5503        }
5504
5505        private ScrapData getScrapDataForType(int viewType) {
5506            ScrapData scrapData = mScrap.get(viewType);
5507            if (scrapData == null) {
5508                scrapData = new ScrapData();
5509                mScrap.put(viewType, scrapData);
5510            }
5511            return scrapData;
5512        }
5513    }
5514
5515    /**
5516     * Utility method for finding an internal RecyclerView, if present
5517     */
5518    @Nullable
5519    static RecyclerView findNestedRecyclerView(@NonNull View view) {
5520        if (!(view instanceof ViewGroup)) {
5521            return null;
5522        }
5523        if (view instanceof RecyclerView) {
5524            return (RecyclerView) view;
5525        }
5526        final ViewGroup parent = (ViewGroup) view;
5527        final int count = parent.getChildCount();
5528        for (int i = 0; i < count; i++) {
5529            final View child = parent.getChildAt(i);
5530            final RecyclerView descendant = findNestedRecyclerView(child);
5531            if (descendant != null) {
5532                return descendant;
5533            }
5534        }
5535        return null;
5536    }
5537
5538    /**
5539     * Utility method for clearing holder's internal RecyclerView, if present
5540     */
5541    static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
5542        if (holder.mNestedRecyclerView != null) {
5543            View item = holder.mNestedRecyclerView.get();
5544            while (item != null) {
5545                if (item == holder.itemView) {
5546                    return; // match found, don't need to clear
5547                }
5548
5549                ViewParent parent = item.getParent();
5550                if (parent instanceof View) {
5551                    item = (View) parent;
5552                } else {
5553                    item = null;
5554                }
5555            }
5556            holder.mNestedRecyclerView = null; // not nested
5557        }
5558    }
5559
5560    /**
5561     * Time base for deadline-aware work scheduling. Overridable for testing.
5562     *
5563     * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
5564     * isn't relevant.
5565     */
5566    long getNanoTime() {
5567        if (ALLOW_THREAD_GAP_WORK) {
5568            return System.nanoTime();
5569        } else {
5570            return 0;
5571        }
5572    }
5573
5574    /**
5575     * A Recycler is responsible for managing scrapped or detached item views for reuse.
5576     *
5577     * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
5578     * that has been marked for removal or reuse.</p>
5579     *
5580     * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
5581     * an adapter's data set representing the data at a given position or item ID.
5582     * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
5583     * If not, the view can be quickly reused by the LayoutManager with no further work.
5584     * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
5585     * may be repositioned by a LayoutManager without remeasurement.</p>
5586     */
5587    public final class Recycler {
5588        final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
5589        ArrayList<ViewHolder> mChangedScrap = null;
5590
5591        final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
5592
5593        private final List<ViewHolder>
5594                mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
5595
5596        private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
5597        int mViewCacheMax = DEFAULT_CACHE_SIZE;
5598
5599        RecycledViewPool mRecyclerPool;
5600
5601        private ViewCacheExtension mViewCacheExtension;
5602
5603        static final int DEFAULT_CACHE_SIZE = 2;
5604
5605        /**
5606         * Clear scrap views out of this recycler. Detached views contained within a
5607         * recycled view pool will remain.
5608         */
5609        public void clear() {
5610            mAttachedScrap.clear();
5611            recycleAndClearCachedViews();
5612        }
5613
5614        /**
5615         * Set the maximum number of detached, valid views we should retain for later use.
5616         *
5617         * @param viewCount Number of views to keep before sending views to the shared pool
5618         */
5619        public void setViewCacheSize(int viewCount) {
5620            mRequestedCacheMax = viewCount;
5621            updateViewCacheSize();
5622        }
5623
5624        void updateViewCacheSize() {
5625            int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
5626            mViewCacheMax = mRequestedCacheMax + extraCache;
5627
5628            // first, try the views that can be recycled
5629            for (int i = mCachedViews.size() - 1;
5630                    i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
5631                recycleCachedViewAt(i);
5632            }
5633        }
5634
5635        /**
5636         * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
5637         *
5638         * @return List of ViewHolders in the scrap list.
5639         */
5640        @NonNull
5641        public List<ViewHolder> getScrapList() {
5642            return mUnmodifiableAttachedScrap;
5643        }
5644
5645        /**
5646         * Helper method for getViewForPosition.
5647         * <p>
5648         * Checks whether a given view holder can be used for the provided position.
5649         *
5650         * @param holder ViewHolder
5651         * @return true if ViewHolder matches the provided position, false otherwise
5652         */
5653        boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
5654            // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
5655            // if it is not removed, verify the type and id.
5656            if (holder.isRemoved()) {
5657                if (DEBUG && !mState.isPreLayout()) {
5658                    throw new IllegalStateException("should not receive a removed view unless it"
5659                            + " is pre layout" + exceptionLabel());
5660                }
5661                return mState.isPreLayout();
5662            }
5663            if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
5664                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
5665                        + "adapter position" + holder + exceptionLabel());
5666            }
5667            if (!mState.isPreLayout()) {
5668                // don't check type if it is pre-layout.
5669                final int type = mAdapter.getItemViewType(holder.mPosition);
5670                if (type != holder.getItemViewType()) {
5671                    return false;
5672                }
5673            }
5674            if (mAdapter.hasStableIds()) {
5675                return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
5676            }
5677            return true;
5678        }
5679
5680        /**
5681         * Attempts to bind view, and account for relevant timing information. If
5682         * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
5683         *
5684         * @param holder Holder to be bound.
5685         * @param offsetPosition Position of item to be bound.
5686         * @param position Pre-layout position of item to be bound.
5687         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5688         *                   complete. If FOREVER_NS is passed, this method will not fail to
5689         *                   bind the holder.
5690         * @return
5691         */
5692        private boolean tryBindViewHolderByDeadline(@NonNull ViewHolder holder, int offsetPosition,
5693                int position, long deadlineNs) {
5694            holder.mOwnerRecyclerView = RecyclerView.this;
5695            final int viewType = holder.getItemViewType();
5696            long startBindNs = getNanoTime();
5697            if (deadlineNs != FOREVER_NS
5698                    && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
5699                // abort - we have a deadline we can't meet
5700                return false;
5701            }
5702            mAdapter.bindViewHolder(holder, offsetPosition);
5703            long endBindNs = getNanoTime();
5704            mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
5705            attachAccessibilityDelegateOnBind(holder);
5706            if (mState.isPreLayout()) {
5707                holder.mPreLayoutPosition = position;
5708            }
5709            return true;
5710        }
5711
5712        /**
5713         * Binds the given View to the position. The View can be a View previously retrieved via
5714         * {@link #getViewForPosition(int)} or created by
5715         * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
5716         * <p>
5717         * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
5718         * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
5719         * wants to handle its own recycling logic.
5720         * <p>
5721         * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
5722         * you don't need to call this method unless you want to bind this View to another position.
5723         *
5724         * @param view The view to update.
5725         * @param position The position of the item to bind to this View.
5726         */
5727        public void bindViewToPosition(@NonNull View view, int position) {
5728            ViewHolder holder = getChildViewHolderInt(view);
5729            if (holder == null) {
5730                throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
5731                        + " pass arbitrary views to this method, they should be created by the "
5732                        + "Adapter" + exceptionLabel());
5733            }
5734            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5735            if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5736                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5737                        + "position " + position + "(offset:" + offsetPosition + ")."
5738                        + "state:" + mState.getItemCount() + exceptionLabel());
5739            }
5740            tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
5741
5742            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5743            final LayoutParams rvLayoutParams;
5744            if (lp == null) {
5745                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5746                holder.itemView.setLayoutParams(rvLayoutParams);
5747            } else if (!checkLayoutParams(lp)) {
5748                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5749                holder.itemView.setLayoutParams(rvLayoutParams);
5750            } else {
5751                rvLayoutParams = (LayoutParams) lp;
5752            }
5753
5754            rvLayoutParams.mInsetsDirty = true;
5755            rvLayoutParams.mViewHolder = holder;
5756            rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
5757        }
5758
5759        /**
5760         * RecyclerView provides artificial position range (item count) in pre-layout state and
5761         * automatically maps these positions to {@link Adapter} positions when
5762         * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
5763         * <p>
5764         * Usually, LayoutManager does not need to worry about this. However, in some cases, your
5765         * LayoutManager may need to call some custom component with item positions in which
5766         * case you need the actual adapter position instead of the pre layout position. You
5767         * can use this method to convert a pre-layout position to adapter (post layout) position.
5768         * <p>
5769         * Note that if the provided position belongs to a deleted ViewHolder, this method will
5770         * return -1.
5771         * <p>
5772         * Calling this method in post-layout state returns the same value back.
5773         *
5774         * @param position The pre-layout position to convert. Must be greater or equal to 0 and
5775         *                 less than {@link State#getItemCount()}.
5776         */
5777        public int convertPreLayoutPositionToPostLayout(int position) {
5778            if (position < 0 || position >= mState.getItemCount()) {
5779                throw new IndexOutOfBoundsException("invalid position " + position + ". State "
5780                        + "item count is " + mState.getItemCount() + exceptionLabel());
5781            }
5782            if (!mState.isPreLayout()) {
5783                return position;
5784            }
5785            return mAdapterHelper.findPositionOffset(position);
5786        }
5787
5788        /**
5789         * Obtain a view initialized for the given position.
5790         *
5791         * This method should be used by {@link LayoutManager} implementations to obtain
5792         * views to represent data from an {@link Adapter}.
5793         * <p>
5794         * The Recycler may reuse a scrap or detached view from a shared pool if one is
5795         * available for the correct view type. If the adapter has not indicated that the
5796         * data at the given position has changed, the Recycler will attempt to hand back
5797         * a scrap view that was previously initialized for that data without rebinding.
5798         *
5799         * @param position Position to obtain a view for
5800         * @return A view representing the data at <code>position</code> from <code>adapter</code>
5801         */
5802        @NonNull
5803        public View getViewForPosition(int position) {
5804            return getViewForPosition(position, false);
5805        }
5806
5807        View getViewForPosition(int position, boolean dryRun) {
5808            return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
5809        }
5810
5811        /**
5812         * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
5813         * cache, the RecycledViewPool, or creating it directly.
5814         * <p>
5815         * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
5816         * rather than constructing or binding a ViewHolder if it doesn't think it has time.
5817         * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
5818         * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
5819         * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
5820         *
5821         * @param position Position of ViewHolder to be returned.
5822         * @param dryRun True if the ViewHolder should not be removed from scrap/cache/
5823         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5824         *                   complete. If FOREVER_NS is passed, this method will not fail to
5825         *                   create/bind the holder if needed.
5826         *
5827         * @return ViewHolder for requested position
5828         */
5829        @Nullable
5830        ViewHolder tryGetViewHolderForPositionByDeadline(int position,
5831                boolean dryRun, long deadlineNs) {
5832            if (position < 0 || position >= mState.getItemCount()) {
5833                throw new IndexOutOfBoundsException("Invalid item position " + position
5834                        + "(" + position + "). Item count:" + mState.getItemCount()
5835                        + exceptionLabel());
5836            }
5837            boolean fromScrapOrHiddenOrCache = false;
5838            ViewHolder holder = null;
5839            // 0) If there is a changed scrap, try to find from there
5840            if (mState.isPreLayout()) {
5841                holder = getChangedScrapViewForPosition(position);
5842                fromScrapOrHiddenOrCache = holder != null;
5843            }
5844            // 1) Find by position from scrap/hidden list/cache
5845            if (holder == null) {
5846                holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
5847                if (holder != null) {
5848                    if (!validateViewHolderForOffsetPosition(holder)) {
5849                        // recycle holder (and unscrap if relevant) since it can't be used
5850                        if (!dryRun) {
5851                            // we would like to recycle this but need to make sure it is not used by
5852                            // animation logic etc.
5853                            holder.addFlags(ViewHolder.FLAG_INVALID);
5854                            if (holder.isScrap()) {
5855                                removeDetachedView(holder.itemView, false);
5856                                holder.unScrap();
5857                            } else if (holder.wasReturnedFromScrap()) {
5858                                holder.clearReturnedFromScrapFlag();
5859                            }
5860                            recycleViewHolderInternal(holder);
5861                        }
5862                        holder = null;
5863                    } else {
5864                        fromScrapOrHiddenOrCache = true;
5865                    }
5866                }
5867            }
5868            if (holder == null) {
5869                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5870                if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5871                    throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5872                            + "position " + position + "(offset:" + offsetPosition + ")."
5873                            + "state:" + mState.getItemCount() + exceptionLabel());
5874                }
5875
5876                final int type = mAdapter.getItemViewType(offsetPosition);
5877                // 2) Find from scrap/cache via stable ids, if exists
5878                if (mAdapter.hasStableIds()) {
5879                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
5880                            type, dryRun);
5881                    if (holder != null) {
5882                        // update position
5883                        holder.mPosition = offsetPosition;
5884                        fromScrapOrHiddenOrCache = true;
5885                    }
5886                }
5887                if (holder == null && mViewCacheExtension != null) {
5888                    // We are NOT sending the offsetPosition because LayoutManager does not
5889                    // know it.
5890                    final View view = mViewCacheExtension
5891                            .getViewForPositionAndType(this, position, type);
5892                    if (view != null) {
5893                        holder = getChildViewHolder(view);
5894                        if (holder == null) {
5895                            throw new IllegalArgumentException("getViewForPositionAndType returned"
5896                                    + " a view which does not have a ViewHolder"
5897                                    + exceptionLabel());
5898                        } else if (holder.shouldIgnore()) {
5899                            throw new IllegalArgumentException("getViewForPositionAndType returned"
5900                                    + " a view that is ignored. You must call stopIgnoring before"
5901                                    + " returning this view." + exceptionLabel());
5902                        }
5903                    }
5904                }
5905                if (holder == null) { // fallback to pool
5906                    if (DEBUG) {
5907                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
5908                                + position + ") fetching from shared pool");
5909                    }
5910                    holder = getRecycledViewPool().getRecycledView(type);
5911                    if (holder != null) {
5912                        holder.resetInternal();
5913                        if (FORCE_INVALIDATE_DISPLAY_LIST) {
5914                            invalidateDisplayListInt(holder);
5915                        }
5916                    }
5917                }
5918                if (holder == null) {
5919                    long start = getNanoTime();
5920                    if (deadlineNs != FOREVER_NS
5921                            && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
5922                        // abort - we have a deadline we can't meet
5923                        return null;
5924                    }
5925                    holder = mAdapter.createViewHolder(RecyclerView.this, type);
5926                    if (ALLOW_THREAD_GAP_WORK) {
5927                        // only bother finding nested RV if prefetching
5928                        RecyclerView innerView = findNestedRecyclerView(holder.itemView);
5929                        if (innerView != null) {
5930                            holder.mNestedRecyclerView = new WeakReference<>(innerView);
5931                        }
5932                    }
5933
5934                    long end = getNanoTime();
5935                    mRecyclerPool.factorInCreateTime(type, end - start);
5936                    if (DEBUG) {
5937                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
5938                    }
5939                }
5940            }
5941
5942            // This is very ugly but the only place we can grab this information
5943            // before the View is rebound and returned to the LayoutManager for post layout ops.
5944            // We don't need this in pre-layout since the VH is not updated by the LM.
5945            if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
5946                    .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
5947                holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5948                if (mState.mRunSimpleAnimations) {
5949                    int changeFlags = ItemAnimator
5950                            .buildAdapterChangeFlagsForAnimations(holder);
5951                    changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
5952                    final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
5953                            holder, changeFlags, holder.getUnmodifiedPayloads());
5954                    recordAnimationInfoIfBouncedHiddenView(holder, info);
5955                }
5956            }
5957
5958            boolean bound = false;
5959            if (mState.isPreLayout() && holder.isBound()) {
5960                // do not update unless we absolutely have to.
5961                holder.mPreLayoutPosition = position;
5962            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
5963                if (DEBUG && holder.isRemoved()) {
5964                    throw new IllegalStateException("Removed holder should be bound and it should"
5965                            + " come here only in pre-layout. Holder: " + holder
5966                            + exceptionLabel());
5967                }
5968                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5969                bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
5970            }
5971
5972            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5973            final LayoutParams rvLayoutParams;
5974            if (lp == null) {
5975                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5976                holder.itemView.setLayoutParams(rvLayoutParams);
5977            } else if (!checkLayoutParams(lp)) {
5978                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5979                holder.itemView.setLayoutParams(rvLayoutParams);
5980            } else {
5981                rvLayoutParams = (LayoutParams) lp;
5982            }
5983            rvLayoutParams.mViewHolder = holder;
5984            rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
5985            return holder;
5986        }
5987
5988        private void attachAccessibilityDelegateOnBind(ViewHolder holder) {
5989            if (isAccessibilityEnabled()) {
5990                final View itemView = holder.itemView;
5991                if (ViewCompat.getImportantForAccessibility(itemView)
5992                        == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
5993                    ViewCompat.setImportantForAccessibility(itemView,
5994                            ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
5995                }
5996                if (!ViewCompat.hasAccessibilityDelegate(itemView)) {
5997                    holder.addFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
5998                    ViewCompat.setAccessibilityDelegate(itemView,
5999                            mAccessibilityDelegate.getItemDelegate());
6000                }
6001            }
6002        }
6003
6004        private void invalidateDisplayListInt(ViewHolder holder) {
6005            if (holder.itemView instanceof ViewGroup) {
6006                invalidateDisplayListInt((ViewGroup) holder.itemView, false);
6007            }
6008        }
6009
6010        private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
6011            for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
6012                final View view = viewGroup.getChildAt(i);
6013                if (view instanceof ViewGroup) {
6014                    invalidateDisplayListInt((ViewGroup) view, true);
6015                }
6016            }
6017            if (!invalidateThis) {
6018                return;
6019            }
6020            // we need to force it to become invisible
6021            if (viewGroup.getVisibility() == View.INVISIBLE) {
6022                viewGroup.setVisibility(View.VISIBLE);
6023                viewGroup.setVisibility(View.INVISIBLE);
6024            } else {
6025                final int visibility = viewGroup.getVisibility();
6026                viewGroup.setVisibility(View.INVISIBLE);
6027                viewGroup.setVisibility(visibility);
6028            }
6029        }
6030
6031        /**
6032         * Recycle a detached view. The specified view will be added to a pool of views
6033         * for later rebinding and reuse.
6034         *
6035         * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
6036         * View is scrapped, it will be removed from scrap list.</p>
6037         *
6038         * @param view Removed view for recycling
6039         * @see LayoutManager#removeAndRecycleView(View, Recycler)
6040         */
6041        public void recycleView(@NonNull View view) {
6042            // This public recycle method tries to make view recycle-able since layout manager
6043            // intended to recycle this view (e.g. even if it is in scrap or change cache)
6044            ViewHolder holder = getChildViewHolderInt(view);
6045            if (holder.isTmpDetached()) {
6046                removeDetachedView(view, false);
6047            }
6048            if (holder.isScrap()) {
6049                holder.unScrap();
6050            } else if (holder.wasReturnedFromScrap()) {
6051                holder.clearReturnedFromScrapFlag();
6052            }
6053            recycleViewHolderInternal(holder);
6054        }
6055
6056        /**
6057         * Internally, use this method instead of {@link #recycleView(android.view.View)} to
6058         * catch potential bugs.
6059         * @param view
6060         */
6061        void recycleViewInternal(View view) {
6062            recycleViewHolderInternal(getChildViewHolderInt(view));
6063        }
6064
6065        void recycleAndClearCachedViews() {
6066            final int count = mCachedViews.size();
6067            for (int i = count - 1; i >= 0; i--) {
6068                recycleCachedViewAt(i);
6069            }
6070            mCachedViews.clear();
6071            if (ALLOW_THREAD_GAP_WORK) {
6072                mPrefetchRegistry.clearPrefetchPositions();
6073            }
6074        }
6075
6076        /**
6077         * Recycles a cached view and removes the view from the list. Views are added to cache
6078         * if and only if they are recyclable, so this method does not check it again.
6079         * <p>
6080         * A small exception to this rule is when the view does not have an animator reference
6081         * but transient state is true (due to animations created outside ItemAnimator). In that
6082         * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
6083         * still recyclable since Adapter wants to do so.
6084         *
6085         * @param cachedViewIndex The index of the view in cached views list
6086         */
6087        void recycleCachedViewAt(int cachedViewIndex) {
6088            if (DEBUG) {
6089                Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
6090            }
6091            ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
6092            if (DEBUG) {
6093                Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
6094            }
6095            addViewHolderToRecycledViewPool(viewHolder, true);
6096            mCachedViews.remove(cachedViewIndex);
6097        }
6098
6099        /**
6100         * internal implementation checks if view is scrapped or attached and throws an exception
6101         * if so.
6102         * Public version un-scraps before calling recycle.
6103         */
6104        void recycleViewHolderInternal(ViewHolder holder) {
6105            if (holder.isScrap() || holder.itemView.getParent() != null) {
6106                throw new IllegalArgumentException(
6107                        "Scrapped or attached views may not be recycled. isScrap:"
6108                                + holder.isScrap() + " isAttached:"
6109                                + (holder.itemView.getParent() != null) + exceptionLabel());
6110            }
6111
6112            if (holder.isTmpDetached()) {
6113                throw new IllegalArgumentException("Tmp detached view should be removed "
6114                        + "from RecyclerView before it can be recycled: " + holder
6115                        + exceptionLabel());
6116            }
6117
6118            if (holder.shouldIgnore()) {
6119                throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
6120                        + " should first call stopIgnoringView(view) before calling recycle."
6121                        + exceptionLabel());
6122            }
6123            //noinspection unchecked
6124            final boolean transientStatePreventsRecycling = holder
6125                    .doesTransientStatePreventRecycling();
6126            final boolean forceRecycle = mAdapter != null
6127                    && transientStatePreventsRecycling
6128                    && mAdapter.onFailedToRecycleView(holder);
6129            boolean cached = false;
6130            boolean recycled = false;
6131            if (DEBUG && mCachedViews.contains(holder)) {
6132                throw new IllegalArgumentException("cached view received recycle internal? "
6133                        + holder + exceptionLabel());
6134            }
6135            if (forceRecycle || holder.isRecyclable()) {
6136                if (mViewCacheMax > 0
6137                        && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
6138                        | ViewHolder.FLAG_REMOVED
6139                        | ViewHolder.FLAG_UPDATE
6140                        | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
6141                    // Retire oldest cached view
6142                    int cachedViewSize = mCachedViews.size();
6143                    if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
6144                        recycleCachedViewAt(0);
6145                        cachedViewSize--;
6146                    }
6147
6148                    int targetCacheIndex = cachedViewSize;
6149                    if (ALLOW_THREAD_GAP_WORK
6150                            && cachedViewSize > 0
6151                            && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
6152                        // when adding the view, skip past most recently prefetched views
6153                        int cacheIndex = cachedViewSize - 1;
6154                        while (cacheIndex >= 0) {
6155                            int cachedPos = mCachedViews.get(cacheIndex).mPosition;
6156                            if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
6157                                break;
6158                            }
6159                            cacheIndex--;
6160                        }
6161                        targetCacheIndex = cacheIndex + 1;
6162                    }
6163                    mCachedViews.add(targetCacheIndex, holder);
6164                    cached = true;
6165                }
6166                if (!cached) {
6167                    addViewHolderToRecycledViewPool(holder, true);
6168                    recycled = true;
6169                }
6170            } else {
6171                // NOTE: A view can fail to be recycled when it is scrolled off while an animation
6172                // runs. In this case, the item is eventually recycled by
6173                // ItemAnimatorRestoreListener#onAnimationFinished.
6174
6175                // TODO: consider cancelling an animation when an item is removed scrollBy,
6176                // to return it to the pool faster
6177                if (DEBUG) {
6178                    Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
6179                            + "re-visit here. We are still removing it from animation lists"
6180                            + exceptionLabel());
6181                }
6182            }
6183            // even if the holder is not removed, we still call this method so that it is removed
6184            // from view holder lists.
6185            mViewInfoStore.removeViewHolder(holder);
6186            if (!cached && !recycled && transientStatePreventsRecycling) {
6187                holder.mOwnerRecyclerView = null;
6188            }
6189        }
6190
6191        /**
6192         * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
6193         *
6194         * Pass false to dispatchRecycled for views that have not been bound.
6195         *
6196         * @param holder Holder to be added to the pool.
6197         * @param dispatchRecycled True to dispatch View recycled callbacks.
6198         */
6199        void addViewHolderToRecycledViewPool(@NonNull ViewHolder holder, boolean dispatchRecycled) {
6200            clearNestedRecyclerViewIfNotNested(holder);
6201            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE)) {
6202                holder.setFlags(0, ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
6203                ViewCompat.setAccessibilityDelegate(holder.itemView, null);
6204            }
6205            if (dispatchRecycled) {
6206                dispatchViewRecycled(holder);
6207            }
6208            holder.mOwnerRecyclerView = null;
6209            getRecycledViewPool().putRecycledView(holder);
6210        }
6211
6212        /**
6213         * Used as a fast path for unscrapping and recycling a view during a bulk operation.
6214         * The caller must call {@link #clearScrap()} when it's done to update the recycler's
6215         * internal bookkeeping.
6216         */
6217        void quickRecycleScrapView(View view) {
6218            final ViewHolder holder = getChildViewHolderInt(view);
6219            holder.mScrapContainer = null;
6220            holder.mInChangeScrap = false;
6221            holder.clearReturnedFromScrapFlag();
6222            recycleViewHolderInternal(holder);
6223        }
6224
6225        /**
6226         * Mark an attached view as scrap.
6227         *
6228         * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
6229         * for rebinding and reuse. Requests for a view for a given position may return a
6230         * reused or rebound scrap view instance.</p>
6231         *
6232         * @param view View to scrap
6233         */
6234        void scrapView(View view) {
6235            final ViewHolder holder = getChildViewHolderInt(view);
6236            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
6237                    || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
6238                if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
6239                    throw new IllegalArgumentException("Called scrap view with an invalid view."
6240                            + " Invalid views cannot be reused from scrap, they should rebound from"
6241                            + " recycler pool." + exceptionLabel());
6242                }
6243                holder.setScrapContainer(this, false);
6244                mAttachedScrap.add(holder);
6245            } else {
6246                if (mChangedScrap == null) {
6247                    mChangedScrap = new ArrayList<ViewHolder>();
6248                }
6249                holder.setScrapContainer(this, true);
6250                mChangedScrap.add(holder);
6251            }
6252        }
6253
6254        /**
6255         * Remove a previously scrapped view from the pool of eligible scrap.
6256         *
6257         * <p>This view will no longer be eligible for reuse until re-scrapped or
6258         * until it is explicitly removed and recycled.</p>
6259         */
6260        void unscrapView(ViewHolder holder) {
6261            if (holder.mInChangeScrap) {
6262                mChangedScrap.remove(holder);
6263            } else {
6264                mAttachedScrap.remove(holder);
6265            }
6266            holder.mScrapContainer = null;
6267            holder.mInChangeScrap = false;
6268            holder.clearReturnedFromScrapFlag();
6269        }
6270
6271        int getScrapCount() {
6272            return mAttachedScrap.size();
6273        }
6274
6275        View getScrapViewAt(int index) {
6276            return mAttachedScrap.get(index).itemView;
6277        }
6278
6279        void clearScrap() {
6280            mAttachedScrap.clear();
6281            if (mChangedScrap != null) {
6282                mChangedScrap.clear();
6283            }
6284        }
6285
6286        ViewHolder getChangedScrapViewForPosition(int position) {
6287            // If pre-layout, check the changed scrap for an exact match.
6288            final int changedScrapSize;
6289            if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
6290                return null;
6291            }
6292            // find by position
6293            for (int i = 0; i < changedScrapSize; i++) {
6294                final ViewHolder holder = mChangedScrap.get(i);
6295                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
6296                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6297                    return holder;
6298                }
6299            }
6300            // find by id
6301            if (mAdapter.hasStableIds()) {
6302                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
6303                if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
6304                    final long id = mAdapter.getItemId(offsetPosition);
6305                    for (int i = 0; i < changedScrapSize; i++) {
6306                        final ViewHolder holder = mChangedScrap.get(i);
6307                        if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
6308                            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6309                            return holder;
6310                        }
6311                    }
6312                }
6313            }
6314            return null;
6315        }
6316
6317        /**
6318         * Returns a view for the position either from attach scrap, hidden children, or cache.
6319         *
6320         * @param position Item position
6321         * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
6322         * @return a ViewHolder that can be re-used for this position.
6323         */
6324        ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
6325            final int scrapCount = mAttachedScrap.size();
6326
6327            // Try first for an exact, non-invalid match from scrap.
6328            for (int i = 0; i < scrapCount; i++) {
6329                final ViewHolder holder = mAttachedScrap.get(i);
6330                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
6331                        && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
6332                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6333                    return holder;
6334                }
6335            }
6336
6337            if (!dryRun) {
6338                View view = mChildHelper.findHiddenNonRemovedView(position);
6339                if (view != null) {
6340                    // This View is good to be used. We just need to unhide, detach and move to the
6341                    // scrap list.
6342                    final ViewHolder vh = getChildViewHolderInt(view);
6343                    mChildHelper.unhide(view);
6344                    int layoutIndex = mChildHelper.indexOfChild(view);
6345                    if (layoutIndex == RecyclerView.NO_POSITION) {
6346                        throw new IllegalStateException("layout index should not be -1 after "
6347                                + "unhiding a view:" + vh + exceptionLabel());
6348                    }
6349                    mChildHelper.detachViewFromParent(layoutIndex);
6350                    scrapView(view);
6351                    vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
6352                            | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
6353                    return vh;
6354                }
6355            }
6356
6357            // Search in our first-level recycled view cache.
6358            final int cacheSize = mCachedViews.size();
6359            for (int i = 0; i < cacheSize; i++) {
6360                final ViewHolder holder = mCachedViews.get(i);
6361                // invalid view holders may be in cache if adapter has stable ids as they can be
6362                // retrieved via getScrapOrCachedViewForId
6363                if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
6364                    if (!dryRun) {
6365                        mCachedViews.remove(i);
6366                    }
6367                    if (DEBUG) {
6368                        Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
6369                                + ") found match in cache: " + holder);
6370                    }
6371                    return holder;
6372                }
6373            }
6374            return null;
6375        }
6376
6377        ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
6378            // Look in our attached views first
6379            final int count = mAttachedScrap.size();
6380            for (int i = count - 1; i >= 0; i--) {
6381                final ViewHolder holder = mAttachedScrap.get(i);
6382                if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
6383                    if (type == holder.getItemViewType()) {
6384                        holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6385                        if (holder.isRemoved()) {
6386                            // this might be valid in two cases:
6387                            // > item is removed but we are in pre-layout pass
6388                            // >> do nothing. return as is. make sure we don't rebind
6389                            // > item is removed then added to another position and we are in
6390                            // post layout.
6391                            // >> remove removed and invalid flags, add update flag to rebind
6392                            // because item was invisible to us and we don't know what happened in
6393                            // between.
6394                            if (!mState.isPreLayout()) {
6395                                holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
6396                                        | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
6397                            }
6398                        }
6399                        return holder;
6400                    } else if (!dryRun) {
6401                        // if we are running animations, it is actually better to keep it in scrap
6402                        // but this would force layout manager to lay it out which would be bad.
6403                        // Recycle this scrap. Type mismatch.
6404                        mAttachedScrap.remove(i);
6405                        removeDetachedView(holder.itemView, false);
6406                        quickRecycleScrapView(holder.itemView);
6407                    }
6408                }
6409            }
6410
6411            // Search the first-level cache
6412            final int cacheSize = mCachedViews.size();
6413            for (int i = cacheSize - 1; i >= 0; i--) {
6414                final ViewHolder holder = mCachedViews.get(i);
6415                if (holder.getItemId() == id) {
6416                    if (type == holder.getItemViewType()) {
6417                        if (!dryRun) {
6418                            mCachedViews.remove(i);
6419                        }
6420                        return holder;
6421                    } else if (!dryRun) {
6422                        recycleCachedViewAt(i);
6423                        return null;
6424                    }
6425                }
6426            }
6427            return null;
6428        }
6429
6430        void dispatchViewRecycled(@NonNull ViewHolder holder) {
6431            if (mRecyclerListener != null) {
6432                mRecyclerListener.onViewRecycled(holder);
6433            }
6434            if (mAdapter != null) {
6435                mAdapter.onViewRecycled(holder);
6436            }
6437            if (mState != null) {
6438                mViewInfoStore.removeViewHolder(holder);
6439            }
6440            if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
6441        }
6442
6443        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
6444                boolean compatibleWithPrevious) {
6445            clear();
6446            getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
6447        }
6448
6449        void offsetPositionRecordsForMove(int from, int to) {
6450            final int start, end, inBetweenOffset;
6451            if (from < to) {
6452                start = from;
6453                end = to;
6454                inBetweenOffset = -1;
6455            } else {
6456                start = to;
6457                end = from;
6458                inBetweenOffset = 1;
6459            }
6460            final int cachedCount = mCachedViews.size();
6461            for (int i = 0; i < cachedCount; i++) {
6462                final ViewHolder holder = mCachedViews.get(i);
6463                if (holder == null || holder.mPosition < start || holder.mPosition > end) {
6464                    continue;
6465                }
6466                if (holder.mPosition == from) {
6467                    holder.offsetPosition(to - from, false);
6468                } else {
6469                    holder.offsetPosition(inBetweenOffset, false);
6470                }
6471                if (DEBUG) {
6472                    Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
6473                            + holder);
6474                }
6475            }
6476        }
6477
6478        void offsetPositionRecordsForInsert(int insertedAt, int count) {
6479            final int cachedCount = mCachedViews.size();
6480            for (int i = 0; i < cachedCount; i++) {
6481                final ViewHolder holder = mCachedViews.get(i);
6482                if (holder != null && holder.mPosition >= insertedAt) {
6483                    if (DEBUG) {
6484                        Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder "
6485                                + holder + " now at position " + (holder.mPosition + count));
6486                    }
6487                    holder.offsetPosition(count, true);
6488                }
6489            }
6490        }
6491
6492        /**
6493         * @param removedFrom Remove start index
6494         * @param count Remove count
6495         * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
6496         *                         false, they'll be applied before the second layout pass
6497         */
6498        void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
6499            final int removedEnd = removedFrom + count;
6500            final int cachedCount = mCachedViews.size();
6501            for (int i = cachedCount - 1; i >= 0; i--) {
6502                final ViewHolder holder = mCachedViews.get(i);
6503                if (holder != null) {
6504                    if (holder.mPosition >= removedEnd) {
6505                        if (DEBUG) {
6506                            Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
6507                                    + " holder " + holder + " now at position "
6508                                    + (holder.mPosition - count));
6509                        }
6510                        holder.offsetPosition(-count, applyToPreLayout);
6511                    } else if (holder.mPosition >= removedFrom) {
6512                        // Item for this view was removed. Dump it from the cache.
6513                        holder.addFlags(ViewHolder.FLAG_REMOVED);
6514                        recycleCachedViewAt(i);
6515                    }
6516                }
6517            }
6518        }
6519
6520        void setViewCacheExtension(ViewCacheExtension extension) {
6521            mViewCacheExtension = extension;
6522        }
6523
6524        void setRecycledViewPool(RecycledViewPool pool) {
6525            if (mRecyclerPool != null) {
6526                mRecyclerPool.detach();
6527            }
6528            mRecyclerPool = pool;
6529            if (mRecyclerPool != null && getAdapter() != null) {
6530                mRecyclerPool.attach();
6531            }
6532        }
6533
6534        RecycledViewPool getRecycledViewPool() {
6535            if (mRecyclerPool == null) {
6536                mRecyclerPool = new RecycledViewPool();
6537            }
6538            return mRecyclerPool;
6539        }
6540
6541        void viewRangeUpdate(int positionStart, int itemCount) {
6542            final int positionEnd = positionStart + itemCount;
6543            final int cachedCount = mCachedViews.size();
6544            for (int i = cachedCount - 1; i >= 0; i--) {
6545                final ViewHolder holder = mCachedViews.get(i);
6546                if (holder == null) {
6547                    continue;
6548                }
6549
6550                final int pos = holder.mPosition;
6551                if (pos >= positionStart && pos < positionEnd) {
6552                    holder.addFlags(ViewHolder.FLAG_UPDATE);
6553                    recycleCachedViewAt(i);
6554                    // cached views should not be flagged as changed because this will cause them
6555                    // to animate when they are returned from cache.
6556                }
6557            }
6558        }
6559
6560        void markKnownViewsInvalid() {
6561            final int cachedCount = mCachedViews.size();
6562            for (int i = 0; i < cachedCount; i++) {
6563                final ViewHolder holder = mCachedViews.get(i);
6564                if (holder != null) {
6565                    holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
6566                    holder.addChangePayload(null);
6567                }
6568            }
6569
6570            if (mAdapter == null || !mAdapter.hasStableIds()) {
6571                // we cannot re-use cached views in this case. Recycle them all
6572                recycleAndClearCachedViews();
6573            }
6574        }
6575
6576        void clearOldPositions() {
6577            final int cachedCount = mCachedViews.size();
6578            for (int i = 0; i < cachedCount; i++) {
6579                final ViewHolder holder = mCachedViews.get(i);
6580                holder.clearOldPosition();
6581            }
6582            final int scrapCount = mAttachedScrap.size();
6583            for (int i = 0; i < scrapCount; i++) {
6584                mAttachedScrap.get(i).clearOldPosition();
6585            }
6586            if (mChangedScrap != null) {
6587                final int changedScrapCount = mChangedScrap.size();
6588                for (int i = 0; i < changedScrapCount; i++) {
6589                    mChangedScrap.get(i).clearOldPosition();
6590                }
6591            }
6592        }
6593
6594        void markItemDecorInsetsDirty() {
6595            final int cachedCount = mCachedViews.size();
6596            for (int i = 0; i < cachedCount; i++) {
6597                final ViewHolder holder = mCachedViews.get(i);
6598                LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
6599                if (layoutParams != null) {
6600                    layoutParams.mInsetsDirty = true;
6601                }
6602            }
6603        }
6604    }
6605
6606    /**
6607     * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
6608     * be controlled by the developer.
6609     * <p>
6610     * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
6611     * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
6612     * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
6613     * {@link RecycledViewPool}.
6614     * <p>
6615     * Note that, Recycler never sends Views to this method to be cached. It is developers
6616     * responsibility to decide whether they want to keep their Views in this custom cache or let
6617     * the default recycling policy handle it.
6618     */
6619    public abstract static class ViewCacheExtension {
6620
6621        /**
6622         * Returns a View that can be binded to the given Adapter position.
6623         * <p>
6624         * This method should <b>not</b> create a new View. Instead, it is expected to return
6625         * an already created View that can be re-used for the given type and position.
6626         * If the View is marked as ignored, it should first call
6627         * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
6628         * <p>
6629         * RecyclerView will re-bind the returned View to the position if necessary.
6630         *
6631         * @param recycler The Recycler that can be used to bind the View
6632         * @param position The adapter position
6633         * @param type     The type of the View, defined by adapter
6634         * @return A View that is bound to the given position or NULL if there is no View to re-use
6635         * @see LayoutManager#ignoreView(View)
6636         */
6637        @Nullable
6638        public abstract View getViewForPositionAndType(@NonNull Recycler recycler, int position,
6639                int type);
6640    }
6641
6642    /**
6643     * Base class for an Adapter
6644     *
6645     * <p>Adapters provide a binding from an app-specific data set to views that are displayed
6646     * within a {@link RecyclerView}.</p>
6647     *
6648     * @param  A class that extends ViewHolder that will be used by the adapter.
6649     */
6650    public abstract static class Adapter<VH extends ViewHolder> {
6651        private final AdapterDataObservable mObservable = new AdapterDataObservable();
6652        private boolean mHasStableIds = false;
6653
6654        /**
6655         * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
6656         * an item.
6657         * <p>
6658         * This new ViewHolder should be constructed with a new View that can represent the items
6659         * of the given type. You can either create a new View manually or inflate it from an XML
6660         * layout file.
6661         * <p>
6662         * The new ViewHolder will be used to display items of the adapter using
6663         * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
6664         * different items in the data set, it is a good idea to cache references to sub views of
6665         * the View to avoid unnecessary {@link View#findViewById(int)} calls.
6666         *
6667         * @param parent The ViewGroup into which the new View will be added after it is bound to
6668         *               an adapter position.
6669         * @param viewType The view type of the new View.
6670         *
6671         * @return A new ViewHolder that holds a View of the given view type.
6672         * @see #getItemViewType(int)
6673         * @see #onBindViewHolder(ViewHolder, int)
6674         */
6675        @NonNull
6676        public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
6677
6678        /**
6679         * Called by RecyclerView to display the data at the specified position. This method should
6680         * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
6681         * position.
6682         * <p>
6683         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6684         * again if the position of the item changes in the data set unless the item itself is
6685         * invalidated or the new position cannot be determined. For this reason, you should only
6686         * use the <code>position</code> parameter while acquiring the related data item inside
6687         * this method and should not keep a copy of it. If you need the position of an item later
6688         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6689         * have the updated adapter position.
6690         *
6691         * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
6692         * handle efficient partial bind.
6693         *
6694         * @param holder The ViewHolder which should be updated to represent the contents of the
6695         *        item at the given position in the data set.
6696         * @param position The position of the item within the adapter's data set.
6697         */
6698        public abstract void onBindViewHolder(@NonNull VH holder, int position);
6699
6700        /**
6701         * Called by RecyclerView to display the data at the specified position. This method
6702         * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
6703         * the given position.
6704         * <p>
6705         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6706         * again if the position of the item changes in the data set unless the item itself is
6707         * invalidated or the new position cannot be determined. For this reason, you should only
6708         * use the <code>position</code> parameter while acquiring the related data item inside
6709         * this method and should not keep a copy of it. If you need the position of an item later
6710         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6711         * have the updated adapter position.
6712         * <p>
6713         * Partial bind vs full bind:
6714         * <p>
6715         * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
6716         * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
6717         * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
6718         * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
6719         * Adapter should not assume that the payload passed in notify methods will be received by
6720         * onBindViewHolder().  For example when the view is not attached to the screen, the
6721         * payload in notifyItemChange() will be simply dropped.
6722         *
6723         * @param holder The ViewHolder which should be updated to represent the contents of the
6724         *               item at the given position in the data set.
6725         * @param position The position of the item within the adapter's data set.
6726         * @param payloads A non-null list of merged payloads. Can be empty list if requires full
6727         *                 update.
6728         */
6729        public void onBindViewHolder(@NonNull VH holder, int position,
6730                @NonNull List<Object> payloads) {
6731            onBindViewHolder(holder, position);
6732        }
6733
6734        /**
6735         * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
6736         * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
6737         *
6738         * @see #onCreateViewHolder(ViewGroup, int)
6739         */
6740        @NonNull
6741        public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
6742            try {
6743                TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
6744                final VH holder = onCreateViewHolder(parent, viewType);
6745                if (holder.itemView.getParent() != null) {
6746                    throw new IllegalStateException("ViewHolder views must not be attached when"
6747                            + " created. Ensure that you are not passing 'true' to the attachToRoot"
6748                            + " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
6749                }
6750                holder.mItemViewType = viewType;
6751                return holder;
6752            } finally {
6753                TraceCompat.endSection();
6754            }
6755        }
6756
6757        /**
6758         * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
6759         * {@link ViewHolder} contents with the item at the given position and also sets up some
6760         * private fields to be used by RecyclerView.
6761         *
6762         * @see #onBindViewHolder(ViewHolder, int)
6763         */
6764        public final void bindViewHolder(@NonNull VH holder, int position) {
6765            holder.mPosition = position;
6766            if (hasStableIds()) {
6767                holder.mItemId = getItemId(position);
6768            }
6769            holder.setFlags(ViewHolder.FLAG_BOUND,
6770                    ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
6771                            | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
6772            TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
6773            onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
6774            holder.clearPayload();
6775            final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
6776            if (layoutParams instanceof RecyclerView.LayoutParams) {
6777                ((LayoutParams) layoutParams).mInsetsDirty = true;
6778            }
6779            TraceCompat.endSection();
6780        }
6781
6782        /**
6783         * Return the view type of the item at <code>position</code> for the purposes
6784         * of view recycling.
6785         *
6786         * <p>The default implementation of this method returns 0, making the assumption of
6787         * a single view type for the adapter. Unlike ListView adapters, types need not
6788         * be contiguous. Consider using id resources to uniquely identify item view types.
6789         *
6790         * @param position position to query
6791         * @return integer value identifying the type of the view needed to represent the item at
6792         *                 <code>position</code>. Type codes need not be contiguous.
6793         */
6794        public int getItemViewType(int position) {
6795            return 0;
6796        }
6797
6798        /**
6799         * Indicates whether each item in the data set can be represented with a unique identifier
6800         * of type {@link java.lang.Long}.
6801         *
6802         * @param hasStableIds Whether items in data set have unique identifiers or not.
6803         * @see #hasStableIds()
6804         * @see #getItemId(int)
6805         */
6806        public void setHasStableIds(boolean hasStableIds) {
6807            if (hasObservers()) {
6808                throw new IllegalStateException("Cannot change whether this adapter has "
6809                        + "stable IDs while the adapter has registered observers.");
6810            }
6811            mHasStableIds = hasStableIds;
6812        }
6813
6814        /**
6815         * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
6816         * would return false this method should return {@link #NO_ID}. The default implementation
6817         * of this method returns {@link #NO_ID}.
6818         *
6819         * @param position Adapter position to query
6820         * @return the stable ID of the item at position
6821         */
6822        public long getItemId(int position) {
6823            return NO_ID;
6824        }
6825
6826        /**
6827         * Returns the total number of items in the data set held by the adapter.
6828         *
6829         * @return The total number of items in this adapter.
6830         */
6831        public abstract int getItemCount();
6832
6833        /**
6834         * Returns true if this adapter publishes a unique <code>long</code> value that can
6835         * act as a key for the item at a given position in the data set. If that item is relocated
6836         * in the data set, the ID returned for that item should be the same.
6837         *
6838         * @return true if this adapter's items have stable IDs
6839         */
6840        public final boolean hasStableIds() {
6841            return mHasStableIds;
6842        }
6843
6844        /**
6845         * Called when a view created by this adapter has been recycled.
6846         *
6847         * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
6848         * needs to be attached to its parent {@link RecyclerView}. This can be because it has
6849         * fallen out of visibility or a set of cached views represented by views still
6850         * attached to the parent RecyclerView. If an item view has large or expensive data
6851         * bound to it such as large bitmaps, this may be a good place to release those
6852         * resources.</p>
6853         * <p>
6854         * RecyclerView calls this method right before clearing ViewHolder's internal data and
6855         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
6856         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
6857         * its adapter position.
6858         *
6859         * @param holder The ViewHolder for the view being recycled
6860         */
6861        public void onViewRecycled(@NonNull VH holder) {
6862        }
6863
6864        /**
6865         * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
6866         * due to its transient state. Upon receiving this callback, Adapter can clear the
6867         * animation(s) that effect the View's transient state and return <code>true</code> so that
6868         * the View can be recycled. Keep in mind that the View in question is already removed from
6869         * the RecyclerView.
6870         * <p>
6871         * In some cases, it is acceptable to recycle a View although it has transient state. Most
6872         * of the time, this is a case where the transient state will be cleared in
6873         * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
6874         * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
6875         * value of this method to decide whether the View should be recycled or not.
6876         * <p>
6877         * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
6878         * should never receive this callback because RecyclerView keeps those Views as children
6879         * until their animations are complete. This callback is useful when children of the item
6880         * views create animations which may not be easy to implement using an {@link ItemAnimator}.
6881         * <p>
6882         * You should <em>never</em> fix this issue by calling
6883         * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
6884         * <code>holder.itemView.setHasTransientState(true);</code>. Each
6885         * <code>View.setHasTransientState(true)</code> call must be matched by a
6886         * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
6887         * may become inconsistent. You should always prefer to end or cancel animations that are
6888         * triggering the transient state instead of handling it manually.
6889         *
6890         * @param holder The ViewHolder containing the View that could not be recycled due to its
6891         *               transient state.
6892         * @return True if the View should be recycled, false otherwise. Note that if this method
6893         * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
6894         * the View and recycle it regardless. If this method returns <code>false</code>,
6895         * RecyclerView will check the View's transient state again before giving a final decision.
6896         * Default implementation returns false.
6897         */
6898        public boolean onFailedToRecycleView(@NonNull VH holder) {
6899            return false;
6900        }
6901
6902        /**
6903         * Called when a view created by this adapter has been attached to a window.
6904         *
6905         * <p>This can be used as a reasonable signal that the view is about to be seen
6906         * by the user. If the adapter previously freed any resources in
6907         * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
6908         * those resources should be restored here.</p>
6909         *
6910         * @param holder Holder of the view being attached
6911         */
6912        public void onViewAttachedToWindow(@NonNull VH holder) {
6913        }
6914
6915        /**
6916         * Called when a view created by this adapter has been detached from its window.
6917         *
6918         * <p>Becoming detached from the window is not necessarily a permanent condition;
6919         * the consumer of an Adapter's views may choose to cache views offscreen while they
6920         * are not visible, attaching and detaching them as appropriate.</p>
6921         *
6922         * @param holder Holder of the view being detached
6923         */
6924        public void onViewDetachedFromWindow(@NonNull VH holder) {
6925        }
6926
6927        /**
6928         * Returns true if one or more observers are attached to this adapter.
6929         *
6930         * @return true if this adapter has observers
6931         */
6932        public final boolean hasObservers() {
6933            return mObservable.hasObservers();
6934        }
6935
6936        /**
6937         * Register a new observer to listen for data changes.
6938         *
6939         * <p>The adapter may publish a variety of events describing specific changes.
6940         * Not all adapters may support all change types and some may fall back to a generic
6941         * {@link RecyclerView.AdapterDataObserver#onChanged()
6942         * "something changed"} event if more specific data is not available.</p>
6943         *
6944         * <p>Components registering observers with an adapter are responsible for
6945         * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6946         * unregistering} those observers when finished.</p>
6947         *
6948         * @param observer Observer to register
6949         *
6950         * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6951         */
6952        public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
6953            mObservable.registerObserver(observer);
6954        }
6955
6956        /**
6957         * Unregister an observer currently listening for data changes.
6958         *
6959         * <p>The unregistered observer will no longer receive events about changes
6960         * to the adapter.</p>
6961         *
6962         * @param observer Observer to unregister
6963         *
6964         * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
6965         */
6966        public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {
6967            mObservable.unregisterObserver(observer);
6968        }
6969
6970        /**
6971         * Called by RecyclerView when it starts observing this Adapter.
6972         * <p>
6973         * Keep in mind that same adapter may be observed by multiple RecyclerViews.
6974         *
6975         * @param recyclerView The RecyclerView instance which started observing this adapter.
6976         * @see #onDetachedFromRecyclerView(RecyclerView)
6977         */
6978        public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
6979        }
6980
6981        /**
6982         * Called by RecyclerView when it stops observing this Adapter.
6983         *
6984         * @param recyclerView The RecyclerView instance which stopped observing this adapter.
6985         * @see #onAttachedToRecyclerView(RecyclerView)
6986         */
6987        public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
6988        }
6989
6990        /**
6991         * Notify any registered observers that the data set has changed.
6992         *
6993         * <p>There are two different classes of data change events, item changes and structural
6994         * changes. Item changes are when a single item has its data updated but no positional
6995         * changes have occurred. Structural changes are when items are inserted, removed or moved
6996         * within the data set.</p>
6997         *
6998         * <p>This event does not specify what about the data set has changed, forcing
6999         * any observers to assume that all existing items and structure may no longer be valid.
7000         * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
7001         *
7002         * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
7003         * for adapters that report that they have {@link #hasStableIds() stable IDs} when
7004         * this method is used. This can help for the purposes of animation and visual
7005         * object persistence but individual item views will still need to be rebound
7006         * and relaid out.</p>
7007         *
7008         * <p>If you are writing an adapter it will always be more efficient to use the more
7009         * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
7010         * as a last resort.</p>
7011         *
7012         * @see #notifyItemChanged(int)
7013         * @see #notifyItemInserted(int)
7014         * @see #notifyItemRemoved(int)
7015         * @see #notifyItemRangeChanged(int, int)
7016         * @see #notifyItemRangeInserted(int, int)
7017         * @see #notifyItemRangeRemoved(int, int)
7018         */
7019        public final void notifyDataSetChanged() {
7020            mObservable.notifyChanged();
7021        }
7022
7023        /**
7024         * Notify any registered observers that the item at <code>position</code> has changed.
7025         * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
7026         *
7027         * <p>This is an item change event, not a structural change event. It indicates that any
7028         * reflection of the data at <code>position</code> is out of date and should be updated.
7029         * The item at <code>position</code> retains the same identity.</p>
7030         *
7031         * @param position Position of the item that has changed
7032         *
7033         * @see #notifyItemRangeChanged(int, int)
7034         */
7035        public final void notifyItemChanged(int position) {
7036            mObservable.notifyItemRangeChanged(position, 1);
7037        }
7038
7039        /**
7040         * Notify any registered observers that the item at <code>position</code> has changed with
7041         * an optional payload object.
7042         *
7043         * <p>This is an item change event, not a structural change event. It indicates that any
7044         * reflection of the data at <code>position</code> is out of date and should be updated.
7045         * The item at <code>position</code> retains the same identity.
7046         * </p>
7047         *
7048         * <p>
7049         * Client can optionally pass a payload for partial change. These payloads will be merged
7050         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
7051         * item is already represented by a ViewHolder and it will be rebound to the same
7052         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
7053         * payloads on that item and prevent future payload until
7054         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
7055         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
7056         * attached, the payload will be simply dropped.
7057         *
7058         * @param position Position of the item that has changed
7059         * @param payload Optional parameter, use null to identify a "full" update
7060         *
7061         * @see #notifyItemRangeChanged(int, int)
7062         */
7063        public final void notifyItemChanged(int position, @Nullable Object payload) {
7064            mObservable.notifyItemRangeChanged(position, 1, payload);
7065        }
7066
7067        /**
7068         * Notify any registered observers that the <code>itemCount</code> items starting at
7069         * position <code>positionStart</code> have changed.
7070         * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
7071         *
7072         * <p>This is an item change event, not a structural change event. It indicates that
7073         * any reflection of the data in the given position range is out of date and should
7074         * be updated. The items in the given range retain the same identity.</p>
7075         *
7076         * @param positionStart Position of the first item that has changed
7077         * @param itemCount Number of items that have changed
7078         *
7079         * @see #notifyItemChanged(int)
7080         */
7081        public final void notifyItemRangeChanged(int positionStart, int itemCount) {
7082            mObservable.notifyItemRangeChanged(positionStart, itemCount);
7083        }
7084
7085        /**
7086         * Notify any registered observers that the <code>itemCount</code> items starting at
7087         * position <code>positionStart</code> have changed. An optional payload can be
7088         * passed to each changed item.
7089         *
7090         * <p>This is an item change event, not a structural change event. It indicates that any
7091         * reflection of the data in the given position range is out of date and should be updated.
7092         * The items in the given range retain the same identity.
7093         * </p>
7094         *
7095         * <p>
7096         * Client can optionally pass a payload for partial change. These payloads will be merged
7097         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
7098         * item is already represented by a ViewHolder and it will be rebound to the same
7099         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
7100         * payloads on that item and prevent future payload until
7101         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
7102         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
7103         * attached, the payload will be simply dropped.
7104         *
7105         * @param positionStart Position of the first item that has changed
7106         * @param itemCount Number of items that have changed
7107         * @param payload  Optional parameter, use null to identify a "full" update
7108         *
7109         * @see #notifyItemChanged(int)
7110         */
7111        public final void notifyItemRangeChanged(int positionStart, int itemCount,
7112                @Nullable Object payload) {
7113            mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
7114        }
7115
7116        /**
7117         * Notify any registered observers that the item reflected at <code>position</code>
7118         * has been newly inserted. The item previously at <code>position</code> is now at
7119         * position <code>position + 1</code>.
7120         *
7121         * <p>This is a structural change event. Representations of other existing items in the
7122         * data set are still considered up to date and will not be rebound, though their
7123         * positions may be altered.</p>
7124         *
7125         * @param position Position of the newly inserted item in the data set
7126         *
7127         * @see #notifyItemRangeInserted(int, int)
7128         */
7129        public final void notifyItemInserted(int position) {
7130            mObservable.notifyItemRangeInserted(position, 1);
7131        }
7132
7133        /**
7134         * Notify any registered observers that the item reflected at <code>fromPosition</code>
7135         * has been moved to <code>toPosition</code>.
7136         *
7137         * <p>This is a structural change event. Representations of other existing items in the
7138         * data set are still considered up to date and will not be rebound, though their
7139         * positions may be altered.</p>
7140         *
7141         * @param fromPosition Previous position of the item.
7142         * @param toPosition New position of the item.
7143         */
7144        public final void notifyItemMoved(int fromPosition, int toPosition) {
7145            mObservable.notifyItemMoved(fromPosition, toPosition);
7146        }
7147
7148        /**
7149         * Notify any registered observers that the currently reflected <code>itemCount</code>
7150         * items starting at <code>positionStart</code> have been newly inserted. The items
7151         * previously located at <code>positionStart</code> and beyond can now be found starting
7152         * at position <code>positionStart + itemCount</code>.
7153         *
7154         * <p>This is a structural change event. Representations of other existing items in the
7155         * data set are still considered up to date and will not be rebound, though their positions
7156         * may be altered.</p>
7157         *
7158         * @param positionStart Position of the first item that was inserted
7159         * @param itemCount Number of items inserted
7160         *
7161         * @see #notifyItemInserted(int)
7162         */
7163        public final void notifyItemRangeInserted(int positionStart, int itemCount) {
7164            mObservable.notifyItemRangeInserted(positionStart, itemCount);
7165        }
7166
7167        /**
7168         * Notify any registered observers that the item previously located at <code>position</code>
7169         * has been removed from the data set. The items previously located at and after
7170         * <code>position</code> may now be found at <code>oldPosition - 1</code>.
7171         *
7172         * <p>This is a structural change event. Representations of other existing items in the
7173         * data set are still considered up to date and will not be rebound, though their positions
7174         * may be altered.</p>
7175         *
7176         * @param position Position of the item that has now been removed
7177         *
7178         * @see #notifyItemRangeRemoved(int, int)
7179         */
7180        public final void notifyItemRemoved(int position) {
7181            mObservable.notifyItemRangeRemoved(position, 1);
7182        }
7183
7184        /**
7185         * Notify any registered observers that the <code>itemCount</code> items previously
7186         * located at <code>positionStart</code> have been removed from the data set. The items
7187         * previously located at and after <code>positionStart + itemCount</code> may now be found
7188         * at <code>oldPosition - itemCount</code>.
7189         *
7190         * <p>This is a structural change event. Representations of other existing items in the data
7191         * set are still considered up to date and will not be rebound, though their positions
7192         * may be altered.</p>
7193         *
7194         * @param positionStart Previous position of the first item that was removed
7195         * @param itemCount Number of items removed from the data set
7196         */
7197        public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
7198            mObservable.notifyItemRangeRemoved(positionStart, itemCount);
7199        }
7200    }
7201
7202    void dispatchChildDetached(View child) {
7203        final ViewHolder viewHolder = getChildViewHolderInt(child);
7204        onChildDetachedFromWindow(child);
7205        if (mAdapter != null && viewHolder != null) {
7206            mAdapter.onViewDetachedFromWindow(viewHolder);
7207        }
7208        if (mOnChildAttachStateListeners != null) {
7209            final int cnt = mOnChildAttachStateListeners.size();
7210            for (int i = cnt - 1; i >= 0; i--) {
7211                mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
7212            }
7213        }
7214    }
7215
7216    void dispatchChildAttached(View child) {
7217        final ViewHolder viewHolder = getChildViewHolderInt(child);
7218        onChildAttachedToWindow(child);
7219        if (mAdapter != null && viewHolder != null) {
7220            mAdapter.onViewAttachedToWindow(viewHolder);
7221        }
7222        if (mOnChildAttachStateListeners != null) {
7223            final int cnt = mOnChildAttachStateListeners.size();
7224            for (int i = cnt - 1; i >= 0; i--) {
7225                mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
7226            }
7227        }
7228    }
7229
7230    /**
7231     * A <code>LayoutManager</code> is responsible for measuring and positioning item views
7232     * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
7233     * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
7234     * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
7235     * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
7236     * layout managers are provided for general use.
7237     * <p/>
7238     * If the LayoutManager specifies a default constructor or one with the signature
7239     * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
7240     * instantiate and set the LayoutManager when being inflated. Most used properties can
7241     * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
7242     * a LayoutManager specifies both constructors, the non-default constructor will take
7243     * precedence.
7244     *
7245     */
7246    public abstract static class LayoutManager {
7247        ChildHelper mChildHelper;
7248        RecyclerView mRecyclerView;
7249
7250        /**
7251         * The callback used for retrieving information about a RecyclerView and its children in the
7252         * horizontal direction.
7253         */
7254        private final ViewBoundsCheck.Callback mHorizontalBoundCheckCallback =
7255                new ViewBoundsCheck.Callback() {
7256                    @Override
7257                    public int getChildCount() {
7258                        return LayoutManager.this.getChildCount();
7259                    }
7260
7261                    @Override
7262                    public View getParent() {
7263                        return mRecyclerView;
7264                    }
7265
7266                    @Override
7267                    public View getChildAt(int index) {
7268                        return LayoutManager.this.getChildAt(index);
7269                    }
7270
7271                    @Override
7272                    public int getParentStart() {
7273                        return LayoutManager.this.getPaddingLeft();
7274                    }
7275
7276                    @Override
7277                    public int getParentEnd() {
7278                        return LayoutManager.this.getWidth() - LayoutManager.this.getPaddingRight();
7279                    }
7280
7281                    @Override
7282                    public int getChildStart(View view) {
7283                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7284                                view.getLayoutParams();
7285                        return LayoutManager.this.getDecoratedLeft(view) - params.leftMargin;
7286                    }
7287
7288                    @Override
7289                    public int getChildEnd(View view) {
7290                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7291                                view.getLayoutParams();
7292                        return LayoutManager.this.getDecoratedRight(view) + params.rightMargin;
7293                    }
7294                };
7295
7296        /**
7297         * The callback used for retrieving information about a RecyclerView and its children in the
7298         * vertical direction.
7299         */
7300        private final ViewBoundsCheck.Callback mVerticalBoundCheckCallback =
7301                new ViewBoundsCheck.Callback() {
7302                    @Override
7303                    public int getChildCount() {
7304                        return LayoutManager.this.getChildCount();
7305                    }
7306
7307                    @Override
7308                    public View getParent() {
7309                        return mRecyclerView;
7310                    }
7311
7312                    @Override
7313                    public View getChildAt(int index) {
7314                        return LayoutManager.this.getChildAt(index);
7315                    }
7316
7317                    @Override
7318                    public int getParentStart() {
7319                        return LayoutManager.this.getPaddingTop();
7320                    }
7321
7322                    @Override
7323                    public int getParentEnd() {
7324                        return LayoutManager.this.getHeight()
7325                                - LayoutManager.this.getPaddingBottom();
7326                    }
7327
7328                    @Override
7329                    public int getChildStart(View view) {
7330                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7331                                view.getLayoutParams();
7332                        return LayoutManager.this.getDecoratedTop(view) - params.topMargin;
7333                    }
7334
7335                    @Override
7336                    public int getChildEnd(View view) {
7337                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7338                                view.getLayoutParams();
7339                        return LayoutManager.this.getDecoratedBottom(view) + params.bottomMargin;
7340                    }
7341                };
7342
7343        /**
7344         * Utility objects used to check the boundaries of children against their parent
7345         * RecyclerView.
7346         * @see #isViewPartiallyVisible(View, boolean, boolean),
7347         * {@link LinearLayoutManager#findOneVisibleChild(int, int, boolean, boolean)},
7348         * and {@link LinearLayoutManager#findOnePartiallyOrCompletelyInvisibleChild(int, int)}.
7349         */
7350        ViewBoundsCheck mHorizontalBoundCheck = new ViewBoundsCheck(mHorizontalBoundCheckCallback);
7351        ViewBoundsCheck mVerticalBoundCheck = new ViewBoundsCheck(mVerticalBoundCheckCallback);
7352
7353        @Nullable
7354        SmoothScroller mSmoothScroller;
7355
7356        boolean mRequestedSimpleAnimations = false;
7357
7358        boolean mIsAttachedToWindow = false;
7359
7360        /**
7361         * This field is only set via the deprecated {@link #setAutoMeasureEnabled(boolean)} and is
7362         * only accessed via {@link #isAutoMeasureEnabled()} for backwards compatability reasons.
7363         */
7364        boolean mAutoMeasure = false;
7365
7366        /**
7367         * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
7368         * if the space that will be given to it is already larger than what it has measured before.
7369         */
7370        private boolean mMeasurementCacheEnabled = true;
7371
7372        private boolean mItemPrefetchEnabled = true;
7373
7374        /**
7375         * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
7376         * requested by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)} or
7377         * {@link #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)} call.
7378         *
7379         * If expanded by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)},
7380         * will be reset upon layout to prevent initial prefetches (often large, since they're
7381         * proportional to expected child count) from expanding cache permanently.
7382         */
7383        int mPrefetchMaxCountObserved;
7384
7385        /**
7386         * If true, mPrefetchMaxCountObserved is only valid until next layout, and should be reset.
7387         */
7388        boolean mPrefetchMaxObservedInInitialPrefetch;
7389
7390        /**
7391         * These measure specs might be the measure specs that were passed into RecyclerView's
7392         * onMeasure method OR fake measure specs created by the RecyclerView.
7393         * For example, when a layout is run, RecyclerView always sets these specs to be
7394         * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
7395         * <p>
7396         * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
7397         * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
7398         * corrupt values. Older platforms have no responsibility to provide a size if they set
7399         * mode to unspecified.
7400         */
7401        private int mWidthMode, mHeightMode;
7402        private int mWidth, mHeight;
7403
7404
7405        /**
7406         * Interface for LayoutManagers to request items to be prefetched, based on position, with
7407         * specified distance from viewport, which indicates priority.
7408         *
7409         * @see LayoutManager#collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
7410         * @see LayoutManager#collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
7411         */
7412        public interface LayoutPrefetchRegistry {
7413            /**
7414             * Requests an an item to be prefetched, based on position, with a specified distance,
7415             * indicating priority.
7416             *
7417             * @param layoutPosition Position of the item to prefetch.
7418             * @param pixelDistance Distance from the current viewport to the bounds of the item,
7419             *                      must be non-negative.
7420             */
7421            void addPosition(int layoutPosition, int pixelDistance);
7422        }
7423
7424        void setRecyclerView(RecyclerView recyclerView) {
7425            if (recyclerView == null) {
7426                mRecyclerView = null;
7427                mChildHelper = null;
7428                mWidth = 0;
7429                mHeight = 0;
7430            } else {
7431                mRecyclerView = recyclerView;
7432                mChildHelper = recyclerView.mChildHelper;
7433                mWidth = recyclerView.getWidth();
7434                mHeight = recyclerView.getHeight();
7435            }
7436            mWidthMode = MeasureSpec.EXACTLY;
7437            mHeightMode = MeasureSpec.EXACTLY;
7438        }
7439
7440        void setMeasureSpecs(int wSpec, int hSpec) {
7441            mWidth = MeasureSpec.getSize(wSpec);
7442            mWidthMode = MeasureSpec.getMode(wSpec);
7443            if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
7444                mWidth = 0;
7445            }
7446
7447            mHeight = MeasureSpec.getSize(hSpec);
7448            mHeightMode = MeasureSpec.getMode(hSpec);
7449            if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
7450                mHeight = 0;
7451            }
7452        }
7453
7454        /**
7455         * Called after a layout is calculated during a measure pass when using auto-measure.
7456         * <p>
7457         * It simply traverses all children to calculate a bounding box then calls
7458         * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
7459         * if they need to handle the bounding box differently.
7460         * <p>
7461         * For example, GridLayoutManager override that method to ensure that even if a column is
7462         * empty, the GridLayoutManager still measures wide enough to include it.
7463         *
7464         * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
7465         * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
7466         */
7467        void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
7468            final int count = getChildCount();
7469            if (count == 0) {
7470                mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
7471                return;
7472            }
7473            int minX = Integer.MAX_VALUE;
7474            int minY = Integer.MAX_VALUE;
7475            int maxX = Integer.MIN_VALUE;
7476            int maxY = Integer.MIN_VALUE;
7477
7478            for (int i = 0; i < count; i++) {
7479                View child = getChildAt(i);
7480                final Rect bounds = mRecyclerView.mTempRect;
7481                getDecoratedBoundsWithMargins(child, bounds);
7482                if (bounds.left < minX) {
7483                    minX = bounds.left;
7484                }
7485                if (bounds.right > maxX) {
7486                    maxX = bounds.right;
7487                }
7488                if (bounds.top < minY) {
7489                    minY = bounds.top;
7490                }
7491                if (bounds.bottom > maxY) {
7492                    maxY = bounds.bottom;
7493                }
7494            }
7495            mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
7496            setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
7497        }
7498
7499        /**
7500         * Sets the measured dimensions from the given bounding box of the children and the
7501         * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
7502         * only called if a LayoutManager returns <code>true</code> from
7503         * {@link #isAutoMeasureEnabled()} and it is called after the RecyclerView calls
7504         * {@link LayoutManager#onLayoutChildren(Recycler, State)} in the execution of
7505         * {@link RecyclerView#onMeasure(int, int)}.
7506         * <p>
7507         * This method must call {@link #setMeasuredDimension(int, int)}.
7508         * <p>
7509         * The default implementation adds the RecyclerView's padding to the given bounding box
7510         * then caps the value to be within the given measurement specs.
7511         *
7512         * @param childrenBounds The bounding box of all children
7513         * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
7514         * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
7515         *
7516         * @see #isAutoMeasureEnabled()
7517         * @see #setMeasuredDimension(int, int)
7518         */
7519        public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
7520            int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
7521            int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
7522            int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
7523            int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
7524            setMeasuredDimension(width, height);
7525        }
7526
7527        /**
7528         * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
7529         */
7530        public void requestLayout() {
7531            if (mRecyclerView != null) {
7532                mRecyclerView.requestLayout();
7533            }
7534        }
7535
7536        /**
7537         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7538         * {@link IllegalStateException} if it <b>is not</b>.
7539         *
7540         * @param message The message for the exception. Can be null.
7541         * @see #assertNotInLayoutOrScroll(String)
7542         */
7543        public void assertInLayoutOrScroll(String message) {
7544            if (mRecyclerView != null) {
7545                mRecyclerView.assertInLayoutOrScroll(message);
7546            }
7547        }
7548
7549        /**
7550         * Chooses a size from the given specs and parameters that is closest to the desired size
7551         * and also complies with the spec.
7552         *
7553         * @param spec The measureSpec
7554         * @param desired The preferred measurement
7555         * @param min The minimum value
7556         *
7557         * @return A size that fits to the given specs
7558         */
7559        public static int chooseSize(int spec, int desired, int min) {
7560            final int mode = View.MeasureSpec.getMode(spec);
7561            final int size = View.MeasureSpec.getSize(spec);
7562            switch (mode) {
7563                case View.MeasureSpec.EXACTLY:
7564                    return size;
7565                case View.MeasureSpec.AT_MOST:
7566                    return Math.min(size, Math.max(desired, min));
7567                case View.MeasureSpec.UNSPECIFIED:
7568                default:
7569                    return Math.max(desired, min);
7570            }
7571        }
7572
7573        /**
7574         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7575         * {@link IllegalStateException} if it <b>is</b>.
7576         *
7577         * @param message The message for the exception. Can be null.
7578         * @see #assertInLayoutOrScroll(String)
7579         */
7580        public void assertNotInLayoutOrScroll(String message) {
7581            if (mRecyclerView != null) {
7582                mRecyclerView.assertNotInLayoutOrScroll(message);
7583            }
7584        }
7585
7586        /**
7587         * Defines whether the measuring pass of layout should use the AutoMeasure mechanism of
7588         * {@link RecyclerView} or if it should be done by the LayoutManager's implementation of
7589         * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
7590         *
7591         * @param enabled <code>True</code> if layout measurement should be done by the
7592         *                RecyclerView, <code>false</code> if it should be done by this
7593         *                LayoutManager.
7594         *
7595         * @see #isAutoMeasureEnabled()
7596         *
7597         * @deprecated Implementors of LayoutManager should define whether or not it uses
7598         *             AutoMeasure by overriding {@link #isAutoMeasureEnabled()}.
7599         */
7600        @Deprecated
7601        public void setAutoMeasureEnabled(boolean enabled) {
7602            mAutoMeasure = enabled;
7603        }
7604
7605        /**
7606         * Returns whether the measuring pass of layout should use the AutoMeasure mechanism of
7607         * {@link RecyclerView} or if it should be done by the LayoutManager's implementation of
7608         * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
7609         * <p>
7610         * This method returns false by default (it actually returns the value passed to the
7611         * deprecated {@link #setAutoMeasureEnabled(boolean)}) and should be overridden to return
7612         * true if a LayoutManager wants to be auto measured by the RecyclerView.
7613         * <p>
7614         * If this method is overridden to return true,
7615         * {@link LayoutManager#onMeasure(Recycler, State, int, int)} should not be overridden.
7616         * <p>
7617         * AutoMeasure is a RecyclerView mechanism that handles the measuring pass of layout in a
7618         * simple and contract satisfying way, including the wrapping of children laid out by
7619         * LayoutManager. Simply put, it handles wrapping children by calling
7620         * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a call to
7621         * {@link RecyclerView#onMeasure(int, int)}, and then calculating desired dimensions based
7622         * on children's dimensions and positions. It does this while supporting all existing
7623         * animation capabilities of the RecyclerView.
7624         * <p>
7625         * More specifically:
7626         * <ol>
7627         * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided measure
7628         * specs both have a mode of {@link View.MeasureSpec#EXACTLY}, RecyclerView will set its
7629         * measured dimensions accordingly and return, allowing layout to continue as normal
7630         * (Actually, RecyclerView will call
7631         * {@link LayoutManager#onMeasure(Recycler, State, int, int)} for backwards compatibility
7632         * reasons but it should not be overridden if AutoMeasure is being used).</li>
7633         * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
7634         * layout process. It will first process all pending Adapter updates and
7635         * then decide whether to run a predictive layout. If it decides to do so, it will first
7636         * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
7637         * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
7638         * return the width and height of the RecyclerView as of the last layout calculation.
7639         * <p>
7640         * After handling the predictive case, RecyclerView will call
7641         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7642         * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
7643         * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
7644         * {@link #getWidth()} and {@link #getWidthMode()}.</li>
7645         * <li>After the layout calculation, RecyclerView sets the measured width & height by
7646         * calculating the bounding box for the children (+ RecyclerView's padding). The
7647         * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
7648         * different values. For instance, GridLayoutManager overrides this value to handle the case
7649         * where if it is vertical and has 3 columns but only 2 items, it should still measure its
7650         * width to fit 3 items, not 2.</li>
7651         * <li>Any following calls to {@link RecyclerView#onMeasure(int, int)} will run
7652         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7653         * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
7654         * take care of which views are actually added / removed / moved / changed for animations so
7655         * that the LayoutManager should not worry about them and handle each
7656         * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.</li>
7657         * <li>When measure is complete and RecyclerView's
7658         * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
7659         * whether it already did layout calculations during the measure pass and if so, it re-uses
7660         * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
7661         * if the last measure spec was different from the final dimensions or adapter contents
7662         * have changed between the measure call and the layout call.</li>
7663         * <li>Finally, animations are calculated and run as usual.</li>
7664         * </ol>
7665         *
7666         * @return <code>True</code> if the measuring pass of layout should use the AutoMeasure
7667         * mechanism of {@link RecyclerView} or <code>False</code> if it should be done by the
7668         * LayoutManager's implementation of
7669         * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
7670         *
7671         * @see #setMeasuredDimension(Rect, int, int)
7672         * @see #onMeasure(Recycler, State, int, int)
7673         */
7674        public boolean isAutoMeasureEnabled() {
7675            return mAutoMeasure;
7676        }
7677
7678        /**
7679         * Returns whether this LayoutManager supports "predictive item animations".
7680         * <p>
7681         * "Predictive item animations" are automatically created animations that show
7682         * where items came from, and where they are going to, as items are added, removed,
7683         * or moved within a layout.
7684         * <p>
7685         * A LayoutManager wishing to support predictive item animations must override this
7686         * method to return true (the default implementation returns false) and must obey certain
7687         * behavioral contracts outlined in {@link #onLayoutChildren(Recycler, State)}.
7688         * <p>
7689         * Whether item animations actually occur in a RecyclerView is actually determined by both
7690         * the return value from this method and the
7691         * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
7692         * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
7693         * method returns false, then only "simple item animations" will be enabled in the
7694         * RecyclerView, in which views whose position are changing are simply faded in/out. If the
7695         * RecyclerView has a non-null ItemAnimator and this method returns true, then predictive
7696         * item animations will be enabled in the RecyclerView.
7697         *
7698         * @return true if this LayoutManager supports predictive item animations, false otherwise.
7699         */
7700        public boolean supportsPredictiveItemAnimations() {
7701            return false;
7702        }
7703
7704        /**
7705         * Sets whether the LayoutManager should be queried for views outside of
7706         * its viewport while the UI thread is idle between frames.
7707         *
7708         * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
7709         * view system traversals on devices running API 21 or greater. Default value is true.</p>
7710         *
7711         * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
7712         * to RenderThread and the starting up its next frame at the next VSync pulse. By
7713         * prefetching out of window views in this time period, delays from inflation and view
7714         * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
7715         *
7716         * <p>While prefetch is enabled, it will have the side effect of expanding the effective
7717         * size of the View cache to hold prefetched views.</p>
7718         *
7719         * @param enabled <code>True</code> if items should be prefetched in between traversals.
7720         *
7721         * @see #isItemPrefetchEnabled()
7722         */
7723        public final void setItemPrefetchEnabled(boolean enabled) {
7724            if (enabled != mItemPrefetchEnabled) {
7725                mItemPrefetchEnabled = enabled;
7726                mPrefetchMaxCountObserved = 0;
7727                if (mRecyclerView != null) {
7728                    mRecyclerView.mRecycler.updateViewCacheSize();
7729                }
7730            }
7731        }
7732
7733        /**
7734         * Sets whether the LayoutManager should be queried for views outside of
7735         * its viewport while the UI thread is idle between frames.
7736         *
7737         * @see #setItemPrefetchEnabled(boolean)
7738         *
7739         * @return true if item prefetch is enabled, false otherwise
7740         */
7741        public final boolean isItemPrefetchEnabled() {
7742            return mItemPrefetchEnabled;
7743        }
7744
7745        /**
7746         * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
7747         *
7748         * <p>If item prefetch is enabled, this method is called in between traversals to gather
7749         * which positions the LayoutManager will soon need, given upcoming movement in subsequent
7750         * traversals.</p>
7751         *
7752         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7753         * each item to be prepared, and these positions will have their ViewHolders created and
7754         * bound, if there is sufficient time available, in advance of being needed by a
7755         * scroll or layout.</p>
7756         *
7757         * @param dx X movement component.
7758         * @param dy Y movement component.
7759         * @param state State of RecyclerView
7760         * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7761         *
7762         * @see #isItemPrefetchEnabled()
7763         * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
7764         */
7765        public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
7766                LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7767
7768        /**
7769         * Gather all positions from the LayoutManager to be prefetched in preperation for its
7770         * RecyclerView to come on screen, due to the movement of another, containing RecyclerView.
7771         *
7772         * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
7773         *
7774         * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
7775         * LayoutManager, this method is called in between draw traversals to gather
7776         * which positions this LayoutManager will first need, once it appears on the screen.</p>
7777         *
7778         * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
7779         * vertically scrolling LayoutManager, this method would be called when the horizontal list
7780         * is about to come onscreen.</p>
7781         *
7782         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7783         * each item to be prepared, and these positions will have their ViewHolders created and
7784         * bound, if there is sufficient time available, in advance of being needed by a
7785         * scroll or layout.</p>
7786         *
7787         * @param adapterItemCount number of items in the associated adapter.
7788         * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7789         *
7790         * @see #isItemPrefetchEnabled()
7791         * @see #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
7792         */
7793        public void collectInitialPrefetchPositions(int adapterItemCount,
7794                LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7795
7796        void dispatchAttachedToWindow(RecyclerView view) {
7797            mIsAttachedToWindow = true;
7798            onAttachedToWindow(view);
7799        }
7800
7801        void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
7802            mIsAttachedToWindow = false;
7803            onDetachedFromWindow(view, recycler);
7804        }
7805
7806        /**
7807         * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
7808         * to a window.
7809         *
7810         * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
7811         * is attached to window.
7812         */
7813        public boolean isAttachedToWindow() {
7814            return mIsAttachedToWindow;
7815        }
7816
7817        /**
7818         * Causes the Runnable to execute on the next animation time step.
7819         * The runnable will be run on the user interface thread.
7820         * <p>
7821         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7822         *
7823         * @param action The Runnable that will be executed.
7824         *
7825         * @see #removeCallbacks
7826         */
7827        public void postOnAnimation(Runnable action) {
7828            if (mRecyclerView != null) {
7829                ViewCompat.postOnAnimation(mRecyclerView, action);
7830            }
7831        }
7832
7833        /**
7834         * Removes the specified Runnable from the message queue.
7835         * <p>
7836         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7837         *
7838         * @param action The Runnable to remove from the message handling queue
7839         *
7840         * @return true if RecyclerView could ask the Handler to remove the Runnable,
7841         *         false otherwise. When the returned value is true, the Runnable
7842         *         may or may not have been actually removed from the message queue
7843         *         (for instance, if the Runnable was not in the queue already.)
7844         *
7845         * @see #postOnAnimation
7846         */
7847        public boolean removeCallbacks(Runnable action) {
7848            if (mRecyclerView != null) {
7849                return mRecyclerView.removeCallbacks(action);
7850            }
7851            return false;
7852        }
7853        /**
7854         * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
7855         * is attached to a window.
7856         * <p>
7857         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7858         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7859         * not requested on the RecyclerView while it was detached.
7860         * <p>
7861         * Subclass implementations should always call through to the superclass implementation.
7862         *
7863         * @param view The RecyclerView this LayoutManager is bound to
7864         *
7865         * @see #onDetachedFromWindow(RecyclerView, Recycler)
7866         */
7867        @CallSuper
7868        public void onAttachedToWindow(RecyclerView view) {
7869        }
7870
7871        /**
7872         * @deprecated
7873         * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
7874         */
7875        @Deprecated
7876        public void onDetachedFromWindow(RecyclerView view) {
7877
7878        }
7879
7880        /**
7881         * Called when this LayoutManager is detached from its parent RecyclerView or when
7882         * its parent RecyclerView is detached from its window.
7883         * <p>
7884         * LayoutManager should clear all of its View references as another LayoutManager might be
7885         * assigned to the RecyclerView.
7886         * <p>
7887         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7888         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7889         * not requested on the RecyclerView while it was detached.
7890         * <p>
7891         * If your LayoutManager has View references that it cleans in on-detach, it should also
7892         * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
7893         * RecyclerView is re-attached.
7894         * <p>
7895         * Subclass implementations should always call through to the superclass implementation.
7896         *
7897         * @param view The RecyclerView this LayoutManager is bound to
7898         * @param recycler The recycler to use if you prefer to recycle your children instead of
7899         *                 keeping them around.
7900         *
7901         * @see #onAttachedToWindow(RecyclerView)
7902         */
7903        @CallSuper
7904        public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
7905            onDetachedFromWindow(view);
7906        }
7907
7908        /**
7909         * Check if the RecyclerView is configured to clip child views to its padding.
7910         *
7911         * @return true if this RecyclerView clips children to its padding, false otherwise
7912         */
7913        public boolean getClipToPadding() {
7914            return mRecyclerView != null && mRecyclerView.mClipToPadding;
7915        }
7916
7917        /**
7918         * Lay out all relevant child views from the given adapter.
7919         *
7920         * The LayoutManager is in charge of the behavior of item animations. By default,
7921         * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
7922         * item animations are enabled. This means that add/remove operations on the
7923         * adapter will result in animations to add new or appearing items, removed or
7924         * disappearing items, and moved items. If a LayoutManager returns false from
7925         * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
7926         * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
7927         * RecyclerView will have enough information to run those animations in a simple
7928         * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
7929         * simply fade views in and out, whether they are actually added/removed or whether
7930         * they are moved on or off the screen due to other add/remove operations.
7931         *
7932         * <p>A LayoutManager wanting a better item animation experience, where items can be
7933         * animated onto and off of the screen according to where the items exist when they
7934         * are not on screen, then the LayoutManager should return true from
7935         * {@link #supportsPredictiveItemAnimations()} and add additional logic to
7936         * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
7937         * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
7938         * once as a "pre" layout step to determine where items would have been prior to
7939         * a real layout, and again to do the "real" layout. In the pre-layout phase,
7940         * items will remember their pre-layout positions to allow them to be laid out
7941         * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
7942         * be returned from the scrap to help determine correct placement of other items.
7943         * These removed items should not be added to the child list, but should be used
7944         * to help calculate correct positioning of other views, including views that
7945         * were not previously onscreen (referred to as APPEARING views), but whose
7946         * pre-layout offscreen position can be determined given the extra
7947         * information about the pre-layout removed views.</p>
7948         *
7949         * <p>The second layout pass is the real layout in which only non-removed views
7950         * will be used. The only additional requirement during this pass is, if
7951         * {@link #supportsPredictiveItemAnimations()} returns true, to note which
7952         * views exist in the child list prior to layout and which are not there after
7953         * layout (referred to as DISAPPEARING views), and to position/layout those views
7954         * appropriately, without regard to the actual bounds of the RecyclerView. This allows
7955         * the animation system to know the location to which to animate these disappearing
7956         * views.</p>
7957         *
7958         * <p>The default LayoutManager implementations for RecyclerView handle all of these
7959         * requirements for animations already. Clients of RecyclerView can either use one
7960         * of these layout managers directly or look at their implementations of
7961         * onLayoutChildren() to see how they account for the APPEARING and
7962         * DISAPPEARING views.</p>
7963         *
7964         * @param recycler         Recycler to use for fetching potentially cached views for a
7965         *                         position
7966         * @param state            Transient state of RecyclerView
7967         */
7968        public void onLayoutChildren(Recycler recycler, State state) {
7969            Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
7970        }
7971
7972        /**
7973         * Called after a full layout calculation is finished. The layout calculation may include
7974         * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
7975         * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
7976         * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
7977         * <p>
7978         * This is a good place for the LayoutManager to do some cleanup like pending scroll
7979         * position, saved state etc.
7980         *
7981         * @param state Transient state of RecyclerView
7982         */
7983        public void onLayoutCompleted(State state) {
7984        }
7985
7986        /**
7987         * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
7988         *
7989         * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
7990         * to store extra information specific to the layout. Client code should subclass
7991         * {@link RecyclerView.LayoutParams} for this purpose.</p>
7992         *
7993         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7994         * you must also override
7995         * {@link #checkLayoutParams(LayoutParams)},
7996         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7997         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7998         *
7999         * @return A new LayoutParams for a child view
8000         */
8001        public abstract LayoutParams generateDefaultLayoutParams();
8002
8003        /**
8004         * Determines the validity of the supplied LayoutParams object.
8005         *
8006         * <p>This should check to make sure that the object is of the correct type
8007         * and all values are within acceptable ranges. The default implementation
8008         * returns <code>true</code> for non-null params.</p>
8009         *
8010         * @param lp LayoutParams object to check
8011         * @return true if this LayoutParams object is valid, false otherwise
8012         */
8013        public boolean checkLayoutParams(LayoutParams lp) {
8014            return lp != null;
8015        }
8016
8017        /**
8018         * Create a LayoutParams object suitable for this LayoutManager, copying relevant
8019         * values from the supplied LayoutParams object if possible.
8020         *
8021         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
8022         * you must also override
8023         * {@link #checkLayoutParams(LayoutParams)},
8024         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
8025         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
8026         *
8027         * @param lp Source LayoutParams object to copy values from
8028         * @return a new LayoutParams object
8029         */
8030        public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
8031            if (lp instanceof LayoutParams) {
8032                return new LayoutParams((LayoutParams) lp);
8033            } else if (lp instanceof MarginLayoutParams) {
8034                return new LayoutParams((MarginLayoutParams) lp);
8035            } else {
8036                return new LayoutParams(lp);
8037            }
8038        }
8039
8040        /**
8041         * Create a LayoutParams object suitable for this LayoutManager from
8042         * an inflated layout resource.
8043         *
8044         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
8045         * you must also override
8046         * {@link #checkLayoutParams(LayoutParams)},
8047         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
8048         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
8049         *
8050         * @param c Context for obtaining styled attributes
8051         * @param attrs AttributeSet describing the supplied arguments
8052         * @return a new LayoutParams object
8053         */
8054        public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
8055            return new LayoutParams(c, attrs);
8056        }
8057
8058        /**
8059         * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
8060         * The default implementation does nothing and returns 0.
8061         *
8062         * @param dx            distance to scroll by in pixels. X increases as scroll position
8063         *                      approaches the right.
8064         * @param recycler      Recycler to use for fetching potentially cached views for a
8065         *                      position
8066         * @param state         Transient state of RecyclerView
8067         * @return The actual distance scrolled. The return value will be negative if dx was
8068         * negative and scrolling proceeeded in that direction.
8069         * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
8070         */
8071        public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
8072            return 0;
8073        }
8074
8075        /**
8076         * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
8077         * The default implementation does nothing and returns 0.
8078         *
8079         * @param dy            distance to scroll in pixels. Y increases as scroll position
8080         *                      approaches the bottom.
8081         * @param recycler      Recycler to use for fetching potentially cached views for a
8082         *                      position
8083         * @param state         Transient state of RecyclerView
8084         * @return The actual distance scrolled. The return value will be negative if dy was
8085         * negative and scrolling proceeeded in that direction.
8086         * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
8087         */
8088        public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
8089            return 0;
8090        }
8091
8092        /**
8093         * Query if horizontal scrolling is currently supported. The default implementation
8094         * returns false.
8095         *
8096         * @return True if this LayoutManager can scroll the current contents horizontally
8097         */
8098        public boolean canScrollHorizontally() {
8099            return false;
8100        }
8101
8102        /**
8103         * Query if vertical scrolling is currently supported. The default implementation
8104         * returns false.
8105         *
8106         * @return True if this LayoutManager can scroll the current contents vertically
8107         */
8108        public boolean canScrollVertically() {
8109            return false;
8110        }
8111
8112        /**
8113         * Scroll to the specified adapter position.
8114         *
8115         * Actual position of the item on the screen depends on the LayoutManager implementation.
8116         * @param position Scroll to this adapter position.
8117         */
8118        public void scrollToPosition(int position) {
8119            if (DEBUG) {
8120                Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
8121            }
8122        }
8123
8124        /**
8125         * <p>Smooth scroll to the specified adapter position.</p>
8126         * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
8127         * instance and call {@link #startSmoothScroll(SmoothScroller)}.
8128         * </p>
8129         * @param recyclerView The RecyclerView to which this layout manager is attached
8130         * @param state    Current State of RecyclerView
8131         * @param position Scroll to this adapter position.
8132         */
8133        public void smoothScrollToPosition(RecyclerView recyclerView, State state,
8134                int position) {
8135            Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
8136        }
8137
8138        /**
8139         * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
8140         * <p>Calling this method will cancel any previous smooth scroll request.</p>
8141         * @param smoothScroller Instance which defines how smooth scroll should be animated
8142         */
8143        public void startSmoothScroll(SmoothScroller smoothScroller) {
8144            if (mSmoothScroller != null && smoothScroller != mSmoothScroller
8145                    && mSmoothScroller.isRunning()) {
8146                mSmoothScroller.stop();
8147            }
8148            mSmoothScroller = smoothScroller;
8149            mSmoothScroller.start(mRecyclerView, this);
8150        }
8151
8152        /**
8153         * @return true if RecyclerView is currently in the state of smooth scrolling.
8154         */
8155        public boolean isSmoothScrolling() {
8156            return mSmoothScroller != null && mSmoothScroller.isRunning();
8157        }
8158
8159
8160        /**
8161         * Returns the resolved layout direction for this RecyclerView.
8162         *
8163         * @return {@link androidx.core.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout
8164         * direction is RTL or returns
8165         * {@link androidx.core.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction
8166         * is not RTL.
8167         */
8168        public int getLayoutDirection() {
8169            return ViewCompat.getLayoutDirection(mRecyclerView);
8170        }
8171
8172        /**
8173         * Ends all animations on the view created by the {@link ItemAnimator}.
8174         *
8175         * @param view The View for which the animations should be ended.
8176         * @see RecyclerView.ItemAnimator#endAnimations()
8177         */
8178        public void endAnimation(View view) {
8179            if (mRecyclerView.mItemAnimator != null) {
8180                mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
8181            }
8182        }
8183
8184        /**
8185         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
8186         * to the layout that is known to be going away, either because it has been
8187         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
8188         * visible portion of the container but is being laid out in order to inform RecyclerView
8189         * in how to animate the item out of view.
8190         * <p>
8191         * Views added via this method are going to be invisible to LayoutManager after the
8192         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
8193         * or won't be included in {@link #getChildCount()} method.
8194         *
8195         * @param child View to add and then remove with animation.
8196         */
8197        public void addDisappearingView(View child) {
8198            addDisappearingView(child, -1);
8199        }
8200
8201        /**
8202         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
8203         * to the layout that is known to be going away, either because it has been
8204         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
8205         * visible portion of the container but is being laid out in order to inform RecyclerView
8206         * in how to animate the item out of view.
8207         * <p>
8208         * Views added via this method are going to be invisible to LayoutManager after the
8209         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
8210         * or won't be included in {@link #getChildCount()} method.
8211         *
8212         * @param child View to add and then remove with animation.
8213         * @param index Index of the view.
8214         */
8215        public void addDisappearingView(View child, int index) {
8216            addViewInt(child, index, true);
8217        }
8218
8219        /**
8220         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
8221         * use this method to add views obtained from a {@link Recycler} using
8222         * {@link Recycler#getViewForPosition(int)}.
8223         *
8224         * @param child View to add
8225         */
8226        public void addView(View child) {
8227            addView(child, -1);
8228        }
8229
8230        /**
8231         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
8232         * use this method to add views obtained from a {@link Recycler} using
8233         * {@link Recycler#getViewForPosition(int)}.
8234         *
8235         * @param child View to add
8236         * @param index Index to add child at
8237         */
8238        public void addView(View child, int index) {
8239            addViewInt(child, index, false);
8240        }
8241
8242        private void addViewInt(View child, int index, boolean disappearing) {
8243            final ViewHolder holder = getChildViewHolderInt(child);
8244            if (disappearing || holder.isRemoved()) {
8245                // these views will be hidden at the end of the layout pass.
8246                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
8247            } else {
8248                // This may look like unnecessary but may happen if layout manager supports
8249                // predictive layouts and adapter removed then re-added the same item.
8250                // In this case, added version will be visible in the post layout (because add is
8251                // deferred) but RV will still bind it to the same View.
8252                // So if a View re-appears in post layout pass, remove it from disappearing list.
8253                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
8254            }
8255            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8256            if (holder.wasReturnedFromScrap() || holder.isScrap()) {
8257                if (holder.isScrap()) {
8258                    holder.unScrap();
8259                } else {
8260                    holder.clearReturnedFromScrapFlag();
8261                }
8262                mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
8263                if (DISPATCH_TEMP_DETACH) {
8264                    ViewCompat.dispatchFinishTemporaryDetach(child);
8265                }
8266            } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
8267                // ensure in correct position
8268                int currentIndex = mChildHelper.indexOfChild(child);
8269                if (index == -1) {
8270                    index = mChildHelper.getChildCount();
8271                }
8272                if (currentIndex == -1) {
8273                    throw new IllegalStateException("Added View has RecyclerView as parent but"
8274                            + " view is not a real child. Unfiltered index:"
8275                            + mRecyclerView.indexOfChild(child) + mRecyclerView.exceptionLabel());
8276                }
8277                if (currentIndex != index) {
8278                    mRecyclerView.mLayout.moveView(currentIndex, index);
8279                }
8280            } else {
8281                mChildHelper.addView(child, index, false);
8282                lp.mInsetsDirty = true;
8283                if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
8284                    mSmoothScroller.onChildAttachedToWindow(child);
8285                }
8286            }
8287            if (lp.mPendingInvalidate) {
8288                if (DEBUG) {
8289                    Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
8290                }
8291                holder.itemView.invalidate();
8292                lp.mPendingInvalidate = false;
8293            }
8294        }
8295
8296        /**
8297         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
8298         * use this method to completely remove a child view that is no longer needed.
8299         * LayoutManagers should strongly consider recycling removed views using
8300         * {@link Recycler#recycleView(android.view.View)}.
8301         *
8302         * @param child View to remove
8303         */
8304        public void removeView(View child) {
8305            mChildHelper.removeView(child);
8306        }
8307
8308        /**
8309         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
8310         * use this method to completely remove a child view that is no longer needed.
8311         * LayoutManagers should strongly consider recycling removed views using
8312         * {@link Recycler#recycleView(android.view.View)}.
8313         *
8314         * @param index Index of the child view to remove
8315         */
8316        public void removeViewAt(int index) {
8317            final View child = getChildAt(index);
8318            if (child != null) {
8319                mChildHelper.removeViewAt(index);
8320            }
8321        }
8322
8323        /**
8324         * Remove all views from the currently attached RecyclerView. This will not recycle
8325         * any of the affected views; the LayoutManager is responsible for doing so if desired.
8326         */
8327        public void removeAllViews() {
8328            // Only remove non-animating views
8329            final int childCount = getChildCount();
8330            for (int i = childCount - 1; i >= 0; i--) {
8331                mChildHelper.removeViewAt(i);
8332            }
8333        }
8334
8335        /**
8336         * Returns offset of the RecyclerView's text baseline from the its top boundary.
8337         *
8338         * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
8339         * there is no baseline.
8340         */
8341        public int getBaseline() {
8342            return -1;
8343        }
8344
8345        /**
8346         * Returns the adapter position of the item represented by the given View. This does not
8347         * contain any adapter changes that might have happened after the last layout.
8348         *
8349         * @param view The view to query
8350         * @return The adapter position of the item which is rendered by this View.
8351         */
8352        public int getPosition(@NonNull View view) {
8353            return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
8354        }
8355
8356        /**
8357         * Returns the View type defined by the adapter.
8358         *
8359         * @param view The view to query
8360         * @return The type of the view assigned by the adapter.
8361         */
8362        public int getItemViewType(@NonNull View view) {
8363            return getChildViewHolderInt(view).getItemViewType();
8364        }
8365
8366        /**
8367         * Traverses the ancestors of the given view and returns the item view that contains it
8368         * and also a direct child of the LayoutManager.
8369         * <p>
8370         * Note that this method may return null if the view is a child of the RecyclerView but
8371         * not a child of the LayoutManager (e.g. running a disappear animation).
8372         *
8373         * @param view The view that is a descendant of the LayoutManager.
8374         *
8375         * @return The direct child of the LayoutManager which contains the given view or null if
8376         * the provided view is not a descendant of this LayoutManager.
8377         *
8378         * @see RecyclerView#getChildViewHolder(View)
8379         * @see RecyclerView#findContainingViewHolder(View)
8380         */
8381        @Nullable
8382        public View findContainingItemView(@NonNull View view) {
8383            if (mRecyclerView == null) {
8384                return null;
8385            }
8386            View found = mRecyclerView.findContainingItemView(view);
8387            if (found == null) {
8388                return null;
8389            }
8390            if (mChildHelper.isHidden(found)) {
8391                return null;
8392            }
8393            return found;
8394        }
8395
8396        /**
8397         * Finds the view which represents the given adapter position.
8398         * <p>
8399         * This method traverses each child since it has no information about child order.
8400         * Override this method to improve performance if your LayoutManager keeps data about
8401         * child views.
8402         * <p>
8403         * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
8404         *
8405         * @param position Position of the item in adapter
8406         * @return The child view that represents the given position or null if the position is not
8407         * laid out
8408         */
8409        @Nullable
8410        public View findViewByPosition(int position) {
8411            final int childCount = getChildCount();
8412            for (int i = 0; i < childCount; i++) {
8413                View child = getChildAt(i);
8414                ViewHolder vh = getChildViewHolderInt(child);
8415                if (vh == null) {
8416                    continue;
8417                }
8418                if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
8419                        && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
8420                    return child;
8421                }
8422            }
8423            return null;
8424        }
8425
8426        /**
8427         * Temporarily detach a child view.
8428         *
8429         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
8430         * views currently attached to the RecyclerView. Generally LayoutManager implementations
8431         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
8432         * so that the detached view may be rebound and reused.</p>
8433         *
8434         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
8435         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
8436         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
8437         * before the LayoutManager entry point method called by RecyclerView returns.</p>
8438         *
8439         * @param child Child to detach
8440         */
8441        public void detachView(@NonNull View child) {
8442            final int ind = mChildHelper.indexOfChild(child);
8443            if (ind >= 0) {
8444                detachViewInternal(ind, child);
8445            }
8446        }
8447
8448        /**
8449         * Temporarily detach a child view.
8450         *
8451         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
8452         * views currently attached to the RecyclerView. Generally LayoutManager implementations
8453         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
8454         * so that the detached view may be rebound and reused.</p>
8455         *
8456         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
8457         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
8458         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
8459         * before the LayoutManager entry point method called by RecyclerView returns.</p>
8460         *
8461         * @param index Index of the child to detach
8462         */
8463        public void detachViewAt(int index) {
8464            detachViewInternal(index, getChildAt(index));
8465        }
8466
8467        private void detachViewInternal(int index, @NonNull View view) {
8468            if (DISPATCH_TEMP_DETACH) {
8469                ViewCompat.dispatchStartTemporaryDetach(view);
8470            }
8471            mChildHelper.detachViewFromParent(index);
8472        }
8473
8474        /**
8475         * Reattach a previously {@link #detachView(android.view.View) detached} view.
8476         * This method should not be used to reattach views that were previously
8477         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8478         *
8479         * @param child Child to reattach
8480         * @param index Intended child index for child
8481         * @param lp LayoutParams for child
8482         */
8483        public void attachView(@NonNull View child, int index, LayoutParams lp) {
8484            ViewHolder vh = getChildViewHolderInt(child);
8485            if (vh.isRemoved()) {
8486                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
8487            } else {
8488                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
8489            }
8490            mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
8491            if (DISPATCH_TEMP_DETACH)  {
8492                ViewCompat.dispatchFinishTemporaryDetach(child);
8493            }
8494        }
8495
8496        /**
8497         * Reattach a previously {@link #detachView(android.view.View) detached} view.
8498         * This method should not be used to reattach views that were previously
8499         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8500         *
8501         * @param child Child to reattach
8502         * @param index Intended child index for child
8503         */
8504        public void attachView(@NonNull View child, int index) {
8505            attachView(child, index, (LayoutParams) child.getLayoutParams());
8506        }
8507
8508        /**
8509         * Reattach a previously {@link #detachView(android.view.View) detached} view.
8510         * This method should not be used to reattach views that were previously
8511         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8512         *
8513         * @param child Child to reattach
8514         */
8515        public void attachView(@NonNull View child) {
8516            attachView(child, -1);
8517        }
8518
8519        /**
8520         * Finish removing a view that was previously temporarily
8521         * {@link #detachView(android.view.View) detached}.
8522         *
8523         * @param child Detached child to remove
8524         */
8525        public void removeDetachedView(@NonNull View child) {
8526            mRecyclerView.removeDetachedView(child, false);
8527        }
8528
8529        /**
8530         * Moves a View from one position to another.
8531         *
8532         * @param fromIndex The View's initial index
8533         * @param toIndex The View's target index
8534         */
8535        public void moveView(int fromIndex, int toIndex) {
8536            View view = getChildAt(fromIndex);
8537            if (view == null) {
8538                throw new IllegalArgumentException("Cannot move a child from non-existing index:"
8539                        + fromIndex + mRecyclerView.toString());
8540            }
8541            detachViewAt(fromIndex);
8542            attachView(view, toIndex);
8543        }
8544
8545        /**
8546         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8547         *
8548         * <p>Scrapping a view allows it to be rebound and reused to show updated or
8549         * different data.</p>
8550         *
8551         * @param child Child to detach and scrap
8552         * @param recycler Recycler to deposit the new scrap view into
8553         */
8554        public void detachAndScrapView(@NonNull View child, @NonNull Recycler recycler) {
8555            int index = mChildHelper.indexOfChild(child);
8556            scrapOrRecycleView(recycler, index, child);
8557        }
8558
8559        /**
8560         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8561         *
8562         * <p>Scrapping a view allows it to be rebound and reused to show updated or
8563         * different data.</p>
8564         *
8565         * @param index Index of child to detach and scrap
8566         * @param recycler Recycler to deposit the new scrap view into
8567         */
8568        public void detachAndScrapViewAt(int index, @NonNull Recycler recycler) {
8569            final View child = getChildAt(index);
8570            scrapOrRecycleView(recycler, index, child);
8571        }
8572
8573        /**
8574         * Remove a child view and recycle it using the given Recycler.
8575         *
8576         * @param child Child to remove and recycle
8577         * @param recycler Recycler to use to recycle child
8578         */
8579        public void removeAndRecycleView(@NonNull View child, @NonNull Recycler recycler) {
8580            removeView(child);
8581            recycler.recycleView(child);
8582        }
8583
8584        /**
8585         * Remove a child view and recycle it using the given Recycler.
8586         *
8587         * @param index Index of child to remove and recycle
8588         * @param recycler Recycler to use to recycle child
8589         */
8590        public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
8591            final View view = getChildAt(index);
8592            removeViewAt(index);
8593            recycler.recycleView(view);
8594        }
8595
8596        /**
8597         * Return the current number of child views attached to the parent RecyclerView.
8598         * This does not include child views that were temporarily detached and/or scrapped.
8599         *
8600         * @return Number of attached children
8601         */
8602        public int getChildCount() {
8603            return mChildHelper != null ? mChildHelper.getChildCount() : 0;
8604        }
8605
8606        /**
8607         * Return the child view at the given index
8608         * @param index Index of child to return
8609         * @return Child view at index
8610         */
8611        @Nullable
8612        public View getChildAt(int index) {
8613            return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
8614        }
8615
8616        /**
8617         * Return the width measurement spec mode that is currently relevant to the LayoutManager.
8618         *
8619         * <p>This value is set only if the LayoutManager opts into the AutoMeasure api via
8620         * {@link #setAutoMeasureEnabled(boolean)}.
8621         *
8622         * <p>When RecyclerView is running a layout, this value is always set to
8623         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8624         *
8625         * @return Width measure spec mode
8626         *
8627         * @see View.MeasureSpec#getMode(int)
8628         */
8629        public int getWidthMode() {
8630            return mWidthMode;
8631        }
8632
8633        /**
8634         * Return the height measurement spec mode that is currently relevant to the LayoutManager.
8635         *
8636         * <p>This value is set only if the LayoutManager opts into the AutoMeasure api via
8637         * {@link #setAutoMeasureEnabled(boolean)}.
8638         *
8639         * <p>When RecyclerView is running a layout, this value is always set to
8640         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8641         *
8642         * @return Height measure spec mode
8643         *
8644         * @see View.MeasureSpec#getMode(int)
8645         */
8646        public int getHeightMode() {
8647            return mHeightMode;
8648        }
8649
8650        /**
8651         * Returns the width that is currently relevant to the LayoutManager.
8652         *
8653         * <p>This value is usually equal to the laid out width of the {@link RecyclerView} but may
8654         * reflect the current {@link android.view.View.MeasureSpec} width if the
8655         * {@link LayoutManager} is using AutoMeasure and the RecyclerView is in the process of
8656         * measuring. The LayoutManager must always use this method to retrieve the width relevant
8657         * to it at any given time.
8658         *
8659         * @return Width in pixels
8660         */
8661        @Px
8662        public int getWidth() {
8663            return mWidth;
8664        }
8665
8666        /**
8667         * Returns the height that is currently relevant to the LayoutManager.
8668         *
8669         * <p>This value is usually equal to the laid out height of the {@link RecyclerView} but may
8670         * reflect the current {@link android.view.View.MeasureSpec} height if the
8671         * {@link LayoutManager} is using AutoMeasure and the RecyclerView is in the process of
8672         * measuring. The LayoutManager must always use this method to retrieve the height relevant
8673         * to it at any given time.
8674         *
8675         * @return Height in pixels
8676         */
8677        @Px
8678        public int getHeight() {
8679            return mHeight;
8680        }
8681
8682        /**
8683         * Return the left padding of the parent RecyclerView
8684         *
8685         * @return Padding in pixels
8686         */
8687        @Px
8688        public int getPaddingLeft() {
8689            return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
8690        }
8691
8692        /**
8693         * Return the top padding of the parent RecyclerView
8694         *
8695         * @return Padding in pixels
8696         */
8697        @Px
8698        public int getPaddingTop() {
8699            return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
8700        }
8701
8702        /**
8703         * Return the right padding of the parent RecyclerView
8704         *
8705         * @return Padding in pixels
8706         */
8707        @Px
8708        public int getPaddingRight() {
8709            return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
8710        }
8711
8712        /**
8713         * Return the bottom padding of the parent RecyclerView
8714         *
8715         * @return Padding in pixels
8716         */
8717        @Px
8718        public int getPaddingBottom() {
8719            return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
8720        }
8721
8722        /**
8723         * Return the start padding of the parent RecyclerView
8724         *
8725         * @return Padding in pixels
8726         */
8727        @Px
8728        public int getPaddingStart() {
8729            return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;
8730        }
8731
8732        /**
8733         * Return the end padding of the parent RecyclerView
8734         *
8735         * @return Padding in pixels
8736         */
8737        @Px
8738        public int getPaddingEnd() {
8739            return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;
8740        }
8741
8742        /**
8743         * Returns true if the RecyclerView this LayoutManager is bound to has focus.
8744         *
8745         * @return True if the RecyclerView has focus, false otherwise.
8746         * @see View#isFocused()
8747         */
8748        public boolean isFocused() {
8749            return mRecyclerView != null && mRecyclerView.isFocused();
8750        }
8751
8752        /**
8753         * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
8754         *
8755         * @return true if the RecyclerView has or contains focus
8756         * @see View#hasFocus()
8757         */
8758        public boolean hasFocus() {
8759            return mRecyclerView != null && mRecyclerView.hasFocus();
8760        }
8761
8762        /**
8763         * Returns the item View which has or contains focus.
8764         *
8765         * @return A direct child of RecyclerView which has focus or contains the focused child.
8766         */
8767        @Nullable
8768        public View getFocusedChild() {
8769            if (mRecyclerView == null) {
8770                return null;
8771            }
8772            final View focused = mRecyclerView.getFocusedChild();
8773            if (focused == null || mChildHelper.isHidden(focused)) {
8774                return null;
8775            }
8776            return focused;
8777        }
8778
8779        /**
8780         * Returns the number of items in the adapter bound to the parent RecyclerView.
8781         * <p>
8782         * Note that this number is not necessarily equal to
8783         * {@link State#getItemCount() State#getItemCount()}. In methods where {@link State} is
8784         * available, you should use {@link State#getItemCount() State#getItemCount()} instead.
8785         * For more details, check the documentation for
8786         * {@link State#getItemCount() State#getItemCount()}.
8787         *
8788         * @return The number of items in the bound adapter
8789         * @see State#getItemCount()
8790         */
8791        public int getItemCount() {
8792            final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
8793            return a != null ? a.getItemCount() : 0;
8794        }
8795
8796        /**
8797         * Offset all child views attached to the parent RecyclerView by dx pixels along
8798         * the horizontal axis.
8799         *
8800         * @param dx Pixels to offset by
8801         */
8802        public void offsetChildrenHorizontal(@Px int dx) {
8803            if (mRecyclerView != null) {
8804                mRecyclerView.offsetChildrenHorizontal(dx);
8805            }
8806        }
8807
8808        /**
8809         * Offset all child views attached to the parent RecyclerView by dy pixels along
8810         * the vertical axis.
8811         *
8812         * @param dy Pixels to offset by
8813         */
8814        public void offsetChildrenVertical(@Px int dy) {
8815            if (mRecyclerView != null) {
8816                mRecyclerView.offsetChildrenVertical(dy);
8817            }
8818        }
8819
8820        /**
8821         * Flags a view so that it will not be scrapped or recycled.
8822         * <p>
8823         * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
8824         * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
8825         * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
8826         * ignore the child.
8827         * <p>
8828         * Before this child can be recycled again, you have to call
8829         * {@link #stopIgnoringView(View)}.
8830         * <p>
8831         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8832         *
8833         * @param view View to ignore.
8834         * @see #stopIgnoringView(View)
8835         */
8836        public void ignoreView(@NonNull View view) {
8837            if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
8838                // checking this because calling this method on a recycled or detached view may
8839                // cause loss of state.
8840                throw new IllegalArgumentException("View should be fully attached to be ignored"
8841                        + mRecyclerView.exceptionLabel());
8842            }
8843            final ViewHolder vh = getChildViewHolderInt(view);
8844            vh.addFlags(ViewHolder.FLAG_IGNORE);
8845            mRecyclerView.mViewInfoStore.removeViewHolder(vh);
8846        }
8847
8848        /**
8849         * View can be scrapped and recycled again.
8850         * <p>
8851         * Note that calling this method removes all information in the view holder.
8852         * <p>
8853         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8854         *
8855         * @param view View to ignore.
8856         */
8857        public void stopIgnoringView(@NonNull View view) {
8858            final ViewHolder vh = getChildViewHolderInt(view);
8859            vh.stopIgnoring();
8860            vh.resetInternal();
8861            vh.addFlags(ViewHolder.FLAG_INVALID);
8862        }
8863
8864        /**
8865         * Temporarily detach and scrap all currently attached child views. Views will be scrapped
8866         * into the given Recycler. The Recycler may prefer to reuse scrap views before
8867         * other views that were previously recycled.
8868         *
8869         * @param recycler Recycler to scrap views into
8870         */
8871        public void detachAndScrapAttachedViews(@NonNull Recycler recycler) {
8872            final int childCount = getChildCount();
8873            for (int i = childCount - 1; i >= 0; i--) {
8874                final View v = getChildAt(i);
8875                scrapOrRecycleView(recycler, i, v);
8876            }
8877        }
8878
8879        private void scrapOrRecycleView(Recycler recycler, int index, View view) {
8880            final ViewHolder viewHolder = getChildViewHolderInt(view);
8881            if (viewHolder.shouldIgnore()) {
8882                if (DEBUG) {
8883                    Log.d(TAG, "ignoring view " + viewHolder);
8884                }
8885                return;
8886            }
8887            if (viewHolder.isInvalid() && !viewHolder.isRemoved()
8888                    && !mRecyclerView.mAdapter.hasStableIds()) {
8889                removeViewAt(index);
8890                recycler.recycleViewHolderInternal(viewHolder);
8891            } else {
8892                detachViewAt(index);
8893                recycler.scrapView(view);
8894                mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
8895            }
8896        }
8897
8898        /**
8899         * Recycles the scrapped views.
8900         * <p>
8901         * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
8902         * the expected behavior if scrapped views are used for animations. Otherwise, we need to
8903         * call remove and invalidate RecyclerView to ensure UI update.
8904         *
8905         * @param recycler Recycler
8906         */
8907        void removeAndRecycleScrapInt(Recycler recycler) {
8908            final int scrapCount = recycler.getScrapCount();
8909            // Loop backward, recycler might be changed by removeDetachedView()
8910            for (int i = scrapCount - 1; i >= 0; i--) {
8911                final View scrap = recycler.getScrapViewAt(i);
8912                final ViewHolder vh = getChildViewHolderInt(scrap);
8913                if (vh.shouldIgnore()) {
8914                    continue;
8915                }
8916                // If the scrap view is animating, we need to cancel them first. If we cancel it
8917                // here, ItemAnimator callback may recycle it which will cause double recycling.
8918                // To avoid this, we mark it as not recycleable before calling the item animator.
8919                // Since removeDetachedView calls a user API, a common mistake (ending animations on
8920                // the view) may recycle it too, so we guard it before we call user APIs.
8921                vh.setIsRecyclable(false);
8922                if (vh.isTmpDetached()) {
8923                    mRecyclerView.removeDetachedView(scrap, false);
8924                }
8925                if (mRecyclerView.mItemAnimator != null) {
8926                    mRecyclerView.mItemAnimator.endAnimation(vh);
8927                }
8928                vh.setIsRecyclable(true);
8929                recycler.quickRecycleScrapView(scrap);
8930            }
8931            recycler.clearScrap();
8932            if (scrapCount > 0) {
8933                mRecyclerView.invalidate();
8934            }
8935        }
8936
8937
8938        /**
8939         * Measure a child view using standard measurement policy, taking the padding
8940         * of the parent RecyclerView and any added item decorations into account.
8941         *
8942         * <p>If the RecyclerView can be scrolled in either dimension the caller may
8943         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8944         *
8945         * @param child Child view to measure
8946         * @param widthUsed Width in pixels currently consumed by other views, if relevant
8947         * @param heightUsed Height in pixels currently consumed by other views, if relevant
8948         */
8949        public void measureChild(@NonNull View child, int widthUsed, int heightUsed) {
8950            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8951
8952            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8953            widthUsed += insets.left + insets.right;
8954            heightUsed += insets.top + insets.bottom;
8955            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8956                    getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
8957                    canScrollHorizontally());
8958            final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8959                    getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
8960                    canScrollVertically());
8961            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8962                child.measure(widthSpec, heightSpec);
8963            }
8964        }
8965
8966        /**
8967         * RecyclerView internally does its own View measurement caching which should help with
8968         * WRAP_CONTENT.
8969         * <p>
8970         * Use this method if the View is already measured once in this layout pass.
8971         */
8972        boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8973            return !mMeasurementCacheEnabled
8974                    || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
8975                    || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
8976        }
8977
8978        // we may consider making this public
8979        /**
8980         * RecyclerView internally does its own View measurement caching which should help with
8981         * WRAP_CONTENT.
8982         * <p>
8983         * Use this method if the View is not yet measured and you need to decide whether to
8984         * measure this View or not.
8985         */
8986        boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8987            return child.isLayoutRequested()
8988                    || !mMeasurementCacheEnabled
8989                    || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
8990                    || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
8991        }
8992
8993        /**
8994         * In addition to the View Framework's measurement cache, RecyclerView uses its own
8995         * additional measurement cache for its children to avoid re-measuring them when not
8996         * necessary. It is on by default but it can be turned off via
8997         * {@link #setMeasurementCacheEnabled(boolean)}.
8998         *
8999         * @return True if measurement cache is enabled, false otherwise.
9000         *
9001         * @see #setMeasurementCacheEnabled(boolean)
9002         */
9003        public boolean isMeasurementCacheEnabled() {
9004            return mMeasurementCacheEnabled;
9005        }
9006
9007        /**
9008         * Sets whether RecyclerView should use its own measurement cache for the children. This is
9009         * a more aggressive cache than the framework uses.
9010         *
9011         * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
9012         *
9013         * @see #isMeasurementCacheEnabled()
9014         */
9015        public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
9016            mMeasurementCacheEnabled = measurementCacheEnabled;
9017        }
9018
9019        private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
9020            final int specMode = MeasureSpec.getMode(spec);
9021            final int specSize = MeasureSpec.getSize(spec);
9022            if (dimension > 0 && childSize != dimension) {
9023                return false;
9024            }
9025            switch (specMode) {
9026                case MeasureSpec.UNSPECIFIED:
9027                    return true;
9028                case MeasureSpec.AT_MOST:
9029                    return specSize >= childSize;
9030                case MeasureSpec.EXACTLY:
9031                    return  specSize == childSize;
9032            }
9033            return false;
9034        }
9035
9036        /**
9037         * Measure a child view using standard measurement policy, taking the padding
9038         * of the parent RecyclerView, any added item decorations and the child margins
9039         * into account.
9040         *
9041         * <p>If the RecyclerView can be scrolled in either dimension the caller may
9042         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
9043         *
9044         * @param child Child view to measure
9045         * @param widthUsed Width in pixels currently consumed by other views, if relevant
9046         * @param heightUsed Height in pixels currently consumed by other views, if relevant
9047         */
9048        public void measureChildWithMargins(@NonNull View child, int widthUsed, int heightUsed) {
9049            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
9050
9051            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
9052            widthUsed += insets.left + insets.right;
9053            heightUsed += insets.top + insets.bottom;
9054
9055            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
9056                    getPaddingLeft() + getPaddingRight()
9057                            + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
9058                    canScrollHorizontally());
9059            final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
9060                    getPaddingTop() + getPaddingBottom()
9061                            + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
9062                    canScrollVertically());
9063            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
9064                child.measure(widthSpec, heightSpec);
9065            }
9066        }
9067
9068        /**
9069         * Calculate a MeasureSpec value for measuring a child view in one dimension.
9070         *
9071         * @param parentSize Size of the parent view where the child will be placed
9072         * @param padding Total space currently consumed by other elements of the parent
9073         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
9074         *                       Generally obtained from the child view's LayoutParams
9075         * @param canScroll true if the parent RecyclerView can scroll in this dimension
9076         *
9077         * @return a MeasureSpec value for the child view
9078         * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
9079         */
9080        @Deprecated
9081        public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
9082                boolean canScroll) {
9083            int size = Math.max(0, parentSize - padding);
9084            int resultSize = 0;
9085            int resultMode = 0;
9086            if (canScroll) {
9087                if (childDimension >= 0) {
9088                    resultSize = childDimension;
9089                    resultMode = MeasureSpec.EXACTLY;
9090                } else {
9091                    // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
9092                    // instead using UNSPECIFIED.
9093                    resultSize = 0;
9094                    resultMode = MeasureSpec.UNSPECIFIED;
9095                }
9096            } else {
9097                if (childDimension >= 0) {
9098                    resultSize = childDimension;
9099                    resultMode = MeasureSpec.EXACTLY;
9100                } else if (childDimension == LayoutParams.MATCH_PARENT) {
9101                    resultSize = size;
9102                    // TODO this should be my spec.
9103                    resultMode = MeasureSpec.EXACTLY;
9104                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
9105                    resultSize = size;
9106                    resultMode = MeasureSpec.AT_MOST;
9107                }
9108            }
9109            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
9110        }
9111
9112        /**
9113         * Calculate a MeasureSpec value for measuring a child view in one dimension.
9114         *
9115         * @param parentSize Size of the parent view where the child will be placed
9116         * @param parentMode The measurement spec mode of the parent
9117         * @param padding Total space currently consumed by other elements of parent
9118         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
9119         *                       Generally obtained from the child view's LayoutParams
9120         * @param canScroll true if the parent RecyclerView can scroll in this dimension
9121         *
9122         * @return a MeasureSpec value for the child view
9123         */
9124        public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
9125                int childDimension, boolean canScroll) {
9126            int size = Math.max(0, parentSize - padding);
9127            int resultSize = 0;
9128            int resultMode = 0;
9129            if (canScroll) {
9130                if (childDimension >= 0) {
9131                    resultSize = childDimension;
9132                    resultMode = MeasureSpec.EXACTLY;
9133                } else if (childDimension == LayoutParams.MATCH_PARENT) {
9134                    switch (parentMode) {
9135                        case MeasureSpec.AT_MOST:
9136                        case MeasureSpec.EXACTLY:
9137                            resultSize = size;
9138                            resultMode = parentMode;
9139                            break;
9140                        case MeasureSpec.UNSPECIFIED:
9141                            resultSize = 0;
9142                            resultMode = MeasureSpec.UNSPECIFIED;
9143                            break;
9144                    }
9145                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
9146                    resultSize = 0;
9147                    resultMode = MeasureSpec.UNSPECIFIED;
9148                }
9149            } else {
9150                if (childDimension >= 0) {
9151                    resultSize = childDimension;
9152                    resultMode = MeasureSpec.EXACTLY;
9153                } else if (childDimension == LayoutParams.MATCH_PARENT) {
9154                    resultSize = size;
9155                    resultMode = parentMode;
9156                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
9157                    resultSize = size;
9158                    if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
9159                        resultMode = MeasureSpec.AT_MOST;
9160                    } else {
9161                        resultMode = MeasureSpec.UNSPECIFIED;
9162                    }
9163
9164                }
9165            }
9166            //noinspection WrongConstant
9167            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
9168        }
9169
9170        /**
9171         * Returns the measured width of the given child, plus the additional size of
9172         * any insets applied by {@link ItemDecoration ItemDecorations}.
9173         *
9174         * @param child Child view to query
9175         * @return child's measured width plus <code>ItemDecoration</code> insets
9176         *
9177         * @see View#getMeasuredWidth()
9178         */
9179        public int getDecoratedMeasuredWidth(@NonNull View child) {
9180            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9181            return child.getMeasuredWidth() + insets.left + insets.right;
9182        }
9183
9184        /**
9185         * Returns the measured height of the given child, plus the additional size of
9186         * any insets applied by {@link ItemDecoration ItemDecorations}.
9187         *
9188         * @param child Child view to query
9189         * @return child's measured height plus <code>ItemDecoration</code> insets
9190         *
9191         * @see View#getMeasuredHeight()
9192         */
9193        public int getDecoratedMeasuredHeight(@NonNull View child) {
9194            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9195            return child.getMeasuredHeight() + insets.top + insets.bottom;
9196        }
9197
9198        /**
9199         * Lay out the given child view within the RecyclerView using coordinates that
9200         * include any current {@link ItemDecoration ItemDecorations}.
9201         *
9202         * <p>LayoutManagers should prefer working in sizes and coordinates that include
9203         * item decoration insets whenever possible. This allows the LayoutManager to effectively
9204         * ignore decoration insets within measurement and layout code. See the following
9205         * methods:</p>
9206         * <ul>
9207         *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
9208         *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
9209         *     <li>{@link #measureChild(View, int, int)}</li>
9210         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
9211         *     <li>{@link #getDecoratedLeft(View)}</li>
9212         *     <li>{@link #getDecoratedTop(View)}</li>
9213         *     <li>{@link #getDecoratedRight(View)}</li>
9214         *     <li>{@link #getDecoratedBottom(View)}</li>
9215         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
9216         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
9217         * </ul>
9218         *
9219         * @param child Child to lay out
9220         * @param left Left edge, with item decoration insets included
9221         * @param top Top edge, with item decoration insets included
9222         * @param right Right edge, with item decoration insets included
9223         * @param bottom Bottom edge, with item decoration insets included
9224         *
9225         * @see View#layout(int, int, int, int)
9226         * @see #layoutDecoratedWithMargins(View, int, int, int, int)
9227         */
9228        public void layoutDecorated(@NonNull View child, int left, int top, int right, int bottom) {
9229            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9230            child.layout(left + insets.left, top + insets.top, right - insets.right,
9231                    bottom - insets.bottom);
9232        }
9233
9234        /**
9235         * Lay out the given child view within the RecyclerView using coordinates that
9236         * include any current {@link ItemDecoration ItemDecorations} and margins.
9237         *
9238         * <p>LayoutManagers should prefer working in sizes and coordinates that include
9239         * item decoration insets whenever possible. This allows the LayoutManager to effectively
9240         * ignore decoration insets within measurement and layout code. See the following
9241         * methods:</p>
9242         * <ul>
9243         *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
9244         *     <li>{@link #measureChild(View, int, int)}</li>
9245         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
9246         *     <li>{@link #getDecoratedLeft(View)}</li>
9247         *     <li>{@link #getDecoratedTop(View)}</li>
9248         *     <li>{@link #getDecoratedRight(View)}</li>
9249         *     <li>{@link #getDecoratedBottom(View)}</li>
9250         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
9251         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
9252         * </ul>
9253         *
9254         * @param child Child to lay out
9255         * @param left Left edge, with item decoration insets and left margin included
9256         * @param top Top edge, with item decoration insets and top margin included
9257         * @param right Right edge, with item decoration insets and right margin included
9258         * @param bottom Bottom edge, with item decoration insets and bottom margin included
9259         *
9260         * @see View#layout(int, int, int, int)
9261         * @see #layoutDecorated(View, int, int, int, int)
9262         */
9263        public void layoutDecoratedWithMargins(@NonNull View child, int left, int top, int right,
9264                int bottom) {
9265            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
9266            final Rect insets = lp.mDecorInsets;
9267            child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
9268                    right - insets.right - lp.rightMargin,
9269                    bottom - insets.bottom - lp.bottomMargin);
9270        }
9271
9272        /**
9273         * Calculates the bounding box of the View while taking into account its matrix changes
9274         * (translation, scale etc) with respect to the RecyclerView.
9275         * <p>
9276         * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
9277         * the View's matrix so that the decor offsets also go through the same transformation.
9278         *
9279         * @param child The ItemView whose bounding box should be calculated.
9280         * @param includeDecorInsets True if the decor insets should be included in the bounding box
9281         * @param out The rectangle into which the output will be written.
9282         */
9283        public void getTransformedBoundingBox(@NonNull View child, boolean includeDecorInsets,
9284                @NonNull Rect out) {
9285            if (includeDecorInsets) {
9286                Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9287                out.set(-insets.left, -insets.top,
9288                        child.getWidth() + insets.right, child.getHeight() + insets.bottom);
9289            } else {
9290                out.set(0, 0, child.getWidth(), child.getHeight());
9291            }
9292
9293            if (mRecyclerView != null) {
9294                final Matrix childMatrix = child.getMatrix();
9295                if (childMatrix != null && !childMatrix.isIdentity()) {
9296                    final RectF tempRectF = mRecyclerView.mTempRectF;
9297                    tempRectF.set(out);
9298                    childMatrix.mapRect(tempRectF);
9299                    out.set(
9300                            (int) Math.floor(tempRectF.left),
9301                            (int) Math.floor(tempRectF.top),
9302                            (int) Math.ceil(tempRectF.right),
9303                            (int) Math.ceil(tempRectF.bottom)
9304                    );
9305                }
9306            }
9307            out.offset(child.getLeft(), child.getTop());
9308        }
9309
9310        /**
9311         * Returns the bounds of the view including its decoration and margins.
9312         *
9313         * @param view The view element to check
9314         * @param outBounds A rect that will receive the bounds of the element including its
9315         *                  decoration and margins.
9316         */
9317        public void getDecoratedBoundsWithMargins(@NonNull View view, @NonNull Rect outBounds) {
9318            RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
9319        }
9320
9321        /**
9322         * Returns the left edge of the given child view within its parent, offset by any applied
9323         * {@link ItemDecoration ItemDecorations}.
9324         *
9325         * @param child Child to query
9326         * @return Child left edge with offsets applied
9327         * @see #getLeftDecorationWidth(View)
9328         */
9329        public int getDecoratedLeft(@NonNull View child) {
9330            return child.getLeft() - getLeftDecorationWidth(child);
9331        }
9332
9333        /**
9334         * Returns the top edge of the given child view within its parent, offset by any applied
9335         * {@link ItemDecoration ItemDecorations}.
9336         *
9337         * @param child Child to query
9338         * @return Child top edge with offsets applied
9339         * @see #getTopDecorationHeight(View)
9340         */
9341        public int getDecoratedTop(@NonNull View child) {
9342            return child.getTop() - getTopDecorationHeight(child);
9343        }
9344
9345        /**
9346         * Returns the right edge of the given child view within its parent, offset by any applied
9347         * {@link ItemDecoration ItemDecorations}.
9348         *
9349         * @param child Child to query
9350         * @return Child right edge with offsets applied
9351         * @see #getRightDecorationWidth(View)
9352         */
9353        public int getDecoratedRight(@NonNull View child) {
9354            return child.getRight() + getRightDecorationWidth(child);
9355        }
9356
9357        /**
9358         * Returns the bottom edge of the given child view within its parent, offset by any applied
9359         * {@link ItemDecoration ItemDecorations}.
9360         *
9361         * @param child Child to query
9362         * @return Child bottom edge with offsets applied
9363         * @see #getBottomDecorationHeight(View)
9364         */
9365        public int getDecoratedBottom(@NonNull View child) {
9366            return child.getBottom() + getBottomDecorationHeight(child);
9367        }
9368
9369        /**
9370         * Calculates the item decor insets applied to the given child and updates the provided
9371         * Rect instance with the inset values.
9372         * <ul>
9373         *     <li>The Rect's left is set to the total width of left decorations.</li>
9374         *     <li>The Rect's top is set to the total height of top decorations.</li>
9375         *     <li>The Rect's right is set to the total width of right decorations.</li>
9376         *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
9377         * </ul>
9378         * <p>
9379         * Note that item decorations are automatically calculated when one of the LayoutManager's
9380         * measure child methods is called. If you need to measure the child with custom specs via
9381         * {@link View#measure(int, int)}, you can use this method to get decorations.
9382         *
9383         * @param child The child view whose decorations should be calculated
9384         * @param outRect The Rect to hold result values
9385         */
9386        public void calculateItemDecorationsForChild(@NonNull View child, @NonNull Rect outRect) {
9387            if (mRecyclerView == null) {
9388                outRect.set(0, 0, 0, 0);
9389                return;
9390            }
9391            Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
9392            outRect.set(insets);
9393        }
9394
9395        /**
9396         * Returns the total height of item decorations applied to child's top.
9397         * <p>
9398         * Note that this value is not updated until the View is measured or
9399         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9400         *
9401         * @param child Child to query
9402         * @return The total height of item decorations applied to the child's top.
9403         * @see #getDecoratedTop(View)
9404         * @see #calculateItemDecorationsForChild(View, Rect)
9405         */
9406        public int getTopDecorationHeight(@NonNull View child) {
9407            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
9408        }
9409
9410        /**
9411         * Returns the total height of item decorations applied to child's bottom.
9412         * <p>
9413         * Note that this value is not updated until the View is measured or
9414         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9415         *
9416         * @param child Child to query
9417         * @return The total height of item decorations applied to the child's bottom.
9418         * @see #getDecoratedBottom(View)
9419         * @see #calculateItemDecorationsForChild(View, Rect)
9420         */
9421        public int getBottomDecorationHeight(@NonNull View child) {
9422            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
9423        }
9424
9425        /**
9426         * Returns the total width of item decorations applied to child's left.
9427         * <p>
9428         * Note that this value is not updated until the View is measured or
9429         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9430         *
9431         * @param child Child to query
9432         * @return The total width of item decorations applied to the child's left.
9433         * @see #getDecoratedLeft(View)
9434         * @see #calculateItemDecorationsForChild(View, Rect)
9435         */
9436        public int getLeftDecorationWidth(@NonNull View child) {
9437            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
9438        }
9439
9440        /**
9441         * Returns the total width of item decorations applied to child's right.
9442         * <p>
9443         * Note that this value is not updated until the View is measured or
9444         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9445         *
9446         * @param child Child to query
9447         * @return The total width of item decorations applied to the child's right.
9448         * @see #getDecoratedRight(View)
9449         * @see #calculateItemDecorationsForChild(View, Rect)
9450         */
9451        public int getRightDecorationWidth(@NonNull View child) {
9452            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
9453        }
9454
9455        /**
9456         * Called when searching for a focusable view in the given direction has failed
9457         * for the current content of the RecyclerView.
9458         *
9459         * <p>This is the LayoutManager's opportunity to populate views in the given direction
9460         * to fulfill the request if it can. The LayoutManager should attach and return
9461         * the view to be focused, if a focusable view in the given direction is found.
9462         * Otherwise, if all the existing (or the newly populated views) are unfocusable, it returns
9463         * the next unfocusable view to become visible on the screen. This unfocusable view is
9464         * typically the first view that's either partially or fully out of RV's padded bounded
9465         * area in the given direction. The default implementation returns null.</p>
9466         *
9467         * @param focused   The currently focused view
9468         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9469         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9470         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9471         *                  or 0 for not applicable
9472         * @param recycler  The recycler to use for obtaining views for currently offscreen items
9473         * @param state     Transient state of RecyclerView
9474         * @return The chosen view to be focused if a focusable view is found, otherwise an
9475         * unfocusable view to become visible onto the screen, else null.
9476         */
9477        @Nullable
9478        public View onFocusSearchFailed(@NonNull View focused, int direction,
9479                @NonNull Recycler recycler, @NonNull State state) {
9480            return null;
9481        }
9482
9483        /**
9484         * This method gives a LayoutManager an opportunity to intercept the initial focus search
9485         * before the default behavior of {@link FocusFinder} is used. If this method returns
9486         * null FocusFinder will attempt to find a focusable child view. If it fails
9487         * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
9488         * will be called to give the LayoutManager an opportunity to add new views for items
9489         * that did not have attached views representing them. The LayoutManager should not add
9490         * or remove views from this method.
9491         *
9492         * @param focused The currently focused view
9493         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9494         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9495         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9496         * @return A descendant view to focus or null to fall back to default behavior.
9497         *         The default implementation returns null.
9498         */
9499        @Nullable
9500        public View onInterceptFocusSearch(@NonNull View focused, int direction) {
9501            return null;
9502        }
9503
9504        /**
9505         * Returns the scroll amount that brings the given rect in child's coordinate system within
9506         * the padded area of RecyclerView.
9507         * @param parent The parent RecyclerView.
9508         * @param child The direct child making the request.
9509         * @param rect The rectangle in the child's coordinates the child
9510         *             wishes to be on the screen.
9511         * @param immediate True to forbid animated or delayed scrolling,
9512         *                  false otherwise
9513         * @return The array containing the scroll amount in x and y directions that brings the
9514         * given rect into RV's padded area.
9515         */
9516        private int[] getChildRectangleOnScreenScrollAmount(RecyclerView parent, View child,
9517                Rect rect, boolean immediate) {
9518            int[] out = new int[2];
9519            final int parentLeft = getPaddingLeft();
9520            final int parentTop = getPaddingTop();
9521            final int parentRight = getWidth() - getPaddingRight();
9522            final int parentBottom = getHeight() - getPaddingBottom();
9523            final int childLeft = child.getLeft() + rect.left - child.getScrollX();
9524            final int childTop = child.getTop() + rect.top - child.getScrollY();
9525            final int childRight = childLeft + rect.width();
9526            final int childBottom = childTop + rect.height();
9527
9528            final int offScreenLeft = Math.min(0, childLeft - parentLeft);
9529            final int offScreenTop = Math.min(0, childTop - parentTop);
9530            final int offScreenRight = Math.max(0, childRight - parentRight);
9531            final int offScreenBottom = Math.max(0, childBottom - parentBottom);
9532
9533            // Favor the "start" layout direction over the end when bringing one side or the other
9534            // of a large rect into view. If we decide to bring in end because start is already
9535            // visible, limit the scroll such that start won't go out of bounds.
9536            final int dx;
9537            if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) {
9538                dx = offScreenRight != 0 ? offScreenRight
9539                        : Math.max(offScreenLeft, childRight - parentRight);
9540            } else {
9541                dx = offScreenLeft != 0 ? offScreenLeft
9542                        : Math.min(childLeft - parentLeft, offScreenRight);
9543            }
9544
9545            // Favor bringing the top into view over the bottom. If top is already visible and
9546            // we should scroll to make bottom visible, make sure top does not go out of bounds.
9547            final int dy = offScreenTop != 0 ? offScreenTop
9548                    : Math.min(childTop - parentTop, offScreenBottom);
9549            out[0] = dx;
9550            out[1] = dy;
9551            return out;
9552        }
9553        /**
9554         * Called when a child of the RecyclerView wants a particular rectangle to be positioned
9555         * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
9556         * android.graphics.Rect, boolean)} for more details.
9557         *
9558         * <p>The base implementation will attempt to perform a standard programmatic scroll
9559         * to bring the given rect into view, within the padded area of the RecyclerView.</p>
9560         *
9561         * @param child The direct child making the request.
9562         * @param rect  The rectangle in the child's coordinates the child
9563         *              wishes to be on the screen.
9564         * @param immediate True to forbid animated or delayed scrolling,
9565         *                  false otherwise
9566         * @return Whether the group scrolled to handle the operation
9567         */
9568        public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
9569                @NonNull View child, @NonNull Rect rect, boolean immediate) {
9570            return requestChildRectangleOnScreen(parent, child, rect, immediate, false);
9571        }
9572
9573        /**
9574         * Requests that the given child of the RecyclerView be positioned onto the screen. This
9575         * method can be called for both unfocusable and focusable child views. For unfocusable
9576         * child views, focusedChildVisible is typically true in which case, layout manager
9577         * makes the child view visible only if the currently focused child stays in-bounds of RV.
9578         * @param parent The parent RecyclerView.
9579         * @param child The direct child making the request.
9580         * @param rect The rectangle in the child's coordinates the child
9581         *              wishes to be on the screen.
9582         * @param immediate True to forbid animated or delayed scrolling,
9583         *                  false otherwise
9584         * @param focusedChildVisible Whether the currently focused view must stay visible.
9585         * @return Whether the group scrolled to handle the operation
9586         */
9587        public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
9588                @NonNull View child, @NonNull Rect rect, boolean immediate,
9589                boolean focusedChildVisible) {
9590            int[] scrollAmount = getChildRectangleOnScreenScrollAmount(parent, child, rect,
9591                    immediate);
9592            int dx = scrollAmount[0];
9593            int dy = scrollAmount[1];
9594            if (!focusedChildVisible || isFocusedChildVisibleAfterScrolling(parent, dx, dy)) {
9595                if (dx != 0 || dy != 0) {
9596                    if (immediate) {
9597                        parent.scrollBy(dx, dy);
9598                    } else {
9599                        parent.smoothScrollBy(dx, dy);
9600                    }
9601                    return true;
9602                }
9603            }
9604            return false;
9605        }
9606
9607        /**
9608         * Returns whether the given child view is partially or fully visible within the padded
9609         * bounded area of RecyclerView, depending on the input parameters.
9610         * A view is partially visible if it has non-zero overlap with RV's padded bounded area.
9611         * If acceptEndPointInclusion flag is set to true, it's also considered partially
9612         * visible if it's located outside RV's bounds and it's hitting either RV's start or end
9613         * bounds.
9614         *
9615         * @param child The child view to be examined.
9616         * @param completelyVisible If true, the method returns true if and only if the child is
9617         *                          completely visible. If false, the method returns true if and
9618         *                          only if the child is only partially visible (that is it will
9619         *                          return false if the child is either completely visible or out
9620         *                          of RV's bounds).
9621         * @param acceptEndPointInclusion If the view's endpoint intersection with RV's start of end
9622         *                                bounds is enough to consider it partially visible,
9623         *                                false otherwise.
9624         * @return True if the given child is partially or fully visible, false otherwise.
9625         */
9626        public boolean isViewPartiallyVisible(@NonNull View child, boolean completelyVisible,
9627                boolean acceptEndPointInclusion) {
9628            int boundsFlag = (ViewBoundsCheck.FLAG_CVS_GT_PVS | ViewBoundsCheck.FLAG_CVS_EQ_PVS
9629                    | ViewBoundsCheck.FLAG_CVE_LT_PVE | ViewBoundsCheck.FLAG_CVE_EQ_PVE);
9630            boolean isViewFullyVisible = mHorizontalBoundCheck.isViewWithinBoundFlags(child,
9631                    boundsFlag)
9632                    && mVerticalBoundCheck.isViewWithinBoundFlags(child, boundsFlag);
9633            if (completelyVisible) {
9634                return isViewFullyVisible;
9635            } else {
9636                return !isViewFullyVisible;
9637            }
9638        }
9639
9640        /**
9641         * Returns whether the currently focused child stays within RV's bounds with the given
9642         * amount of scrolling.
9643         * @param parent The parent RecyclerView.
9644         * @param dx The scrolling in x-axis direction to be performed.
9645         * @param dy The scrolling in y-axis direction to be performed.
9646         * @return {@code false} if the focused child is not at least partially visible after
9647         *         scrolling or no focused child exists, {@code true} otherwise.
9648         */
9649        private boolean isFocusedChildVisibleAfterScrolling(RecyclerView parent, int dx, int dy) {
9650            final View focusedChild = parent.getFocusedChild();
9651            if (focusedChild == null) {
9652                return false;
9653            }
9654            final int parentLeft = getPaddingLeft();
9655            final int parentTop = getPaddingTop();
9656            final int parentRight = getWidth() - getPaddingRight();
9657            final int parentBottom = getHeight() - getPaddingBottom();
9658            final Rect bounds = mRecyclerView.mTempRect;
9659            getDecoratedBoundsWithMargins(focusedChild, bounds);
9660
9661            if (bounds.left - dx >= parentRight || bounds.right - dx <= parentLeft
9662                    || bounds.top - dy >= parentBottom || bounds.bottom - dy <= parentTop) {
9663                return false;
9664            }
9665            return true;
9666        }
9667
9668        /**
9669         * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
9670         */
9671        @Deprecated
9672        public boolean onRequestChildFocus(@NonNull RecyclerView parent, @NonNull View child,
9673                @Nullable View focused) {
9674            // eat the request if we are in the middle of a scroll or layout
9675            return isSmoothScrolling() || parent.isComputingLayout();
9676        }
9677
9678        /**
9679         * Called when a descendant view of the RecyclerView requests focus.
9680         *
9681         * <p>A LayoutManager wishing to keep focused views aligned in a specific
9682         * portion of the view may implement that behavior in an override of this method.</p>
9683         *
9684         * <p>If the LayoutManager executes different behavior that should override the default
9685         * behavior of scrolling the focused child on screen instead of running alongside it,
9686         * this method should return true.</p>
9687         *
9688         * @param parent  The RecyclerView hosting this LayoutManager
9689         * @param state   Current state of RecyclerView
9690         * @param child   Direct child of the RecyclerView containing the newly focused view
9691         * @param focused The newly focused view. This may be the same view as child or it may be
9692         *                null
9693         * @return true if the default scroll behavior should be suppressed
9694         */
9695        public boolean onRequestChildFocus(@NonNull RecyclerView parent, @NonNull State state,
9696                @NonNull View child, @Nullable View focused) {
9697            return onRequestChildFocus(parent, child, focused);
9698        }
9699
9700        /**
9701         * Called if the RecyclerView this LayoutManager is bound to has a different adapter set via
9702         * {@link RecyclerView#setAdapter(Adapter)} or
9703         * {@link RecyclerView#swapAdapter(Adapter, boolean)}. The LayoutManager may use this
9704         * opportunity to clear caches and configure state such that it can relayout appropriately
9705         * with the new data and potentially new view types.
9706         *
9707         * <p>The default implementation removes all currently attached views.</p>
9708         *
9709         * @param oldAdapter The previous adapter instance. Will be null if there was previously no
9710         *                   adapter.
9711         * @param newAdapter The new adapter instance. Might be null if
9712         *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
9713         */
9714        public void onAdapterChanged(@Nullable Adapter oldAdapter, @Nullable Adapter newAdapter) {
9715        }
9716
9717        /**
9718         * Called to populate focusable views within the RecyclerView.
9719         *
9720         * <p>The LayoutManager implementation should return <code>true</code> if the default
9721         * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
9722         * suppressed.</p>
9723         *
9724         * <p>The default implementation returns <code>false</code> to trigger RecyclerView
9725         * to fall back to the default ViewGroup behavior.</p>
9726         *
9727         * @param recyclerView The RecyclerView hosting this LayoutManager
9728         * @param views List of output views. This method should add valid focusable views
9729         *              to this list.
9730         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9731         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9732         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9733         * @param focusableMode The type of focusables to be added.
9734         *
9735         * @return true to suppress the default behavior, false to add default focusables after
9736         *         this method returns.
9737         *
9738         * @see #FOCUSABLES_ALL
9739         * @see #FOCUSABLES_TOUCH_MODE
9740         */
9741        public boolean onAddFocusables(@NonNull RecyclerView recyclerView,
9742                @NonNull ArrayList<View> views, int direction, int focusableMode) {
9743            return false;
9744        }
9745
9746        /**
9747         * Called in response to a call to {@link Adapter#notifyDataSetChanged()} or
9748         * {@link RecyclerView#swapAdapter(Adapter, boolean)} ()} and signals that the the entire
9749         * data set has changed.
9750         *
9751         * @param recyclerView
9752         */
9753        public void onItemsChanged(@NonNull RecyclerView recyclerView) {
9754        }
9755
9756        /**
9757         * Called when items have been added to the adapter. The LayoutManager may choose to
9758         * requestLayout if the inserted items would require refreshing the currently visible set
9759         * of child views. (e.g. currently empty space would be filled by appended items, etc.)
9760         *
9761         * @param recyclerView
9762         * @param positionStart
9763         * @param itemCount
9764         */
9765        public void onItemsAdded(@NonNull RecyclerView recyclerView, int positionStart,
9766                int itemCount) {
9767        }
9768
9769        /**
9770         * Called when items have been removed from the adapter.
9771         *
9772         * @param recyclerView
9773         * @param positionStart
9774         * @param itemCount
9775         */
9776        public void onItemsRemoved(@NonNull RecyclerView recyclerView, int positionStart,
9777                int itemCount) {
9778        }
9779
9780        /**
9781         * Called when items have been changed in the adapter.
9782         * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
9783         * instead, then this callback will not be invoked.
9784         *
9785         * @param recyclerView
9786         * @param positionStart
9787         * @param itemCount
9788         */
9789        public void onItemsUpdated(@NonNull RecyclerView recyclerView, int positionStart,
9790                int itemCount) {
9791        }
9792
9793        /**
9794         * Called when items have been changed in the adapter and with optional payload.
9795         * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
9796         *
9797         * @param recyclerView
9798         * @param positionStart
9799         * @param itemCount
9800         * @param payload
9801         */
9802        public void onItemsUpdated(@NonNull RecyclerView recyclerView, int positionStart,
9803                int itemCount, @Nullable Object payload) {
9804            onItemsUpdated(recyclerView, positionStart, itemCount);
9805        }
9806
9807        /**
9808         * Called when an item is moved withing the adapter.
9809         * <p>
9810         * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
9811         * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
9812         * is called.
9813         *
9814         * @param recyclerView
9815         * @param from
9816         * @param to
9817         * @param itemCount
9818         */
9819        public void onItemsMoved(@NonNull RecyclerView recyclerView, int from, int to,
9820                int itemCount) {
9821
9822        }
9823
9824
9825        /**
9826         * <p>Override this method if you want to support scroll bars.</p>
9827         *
9828         * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
9829         *
9830         * <p>Default implementation returns 0.</p>
9831         *
9832         * @param state Current state of RecyclerView
9833         * @return The horizontal extent of the scrollbar's thumb
9834         * @see RecyclerView#computeHorizontalScrollExtent()
9835         */
9836        public int computeHorizontalScrollExtent(@NonNull State state) {
9837            return 0;
9838        }
9839
9840        /**
9841         * <p>Override this method if you want to support scroll bars.</p>
9842         *
9843         * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
9844         *
9845         * <p>Default implementation returns 0.</p>
9846         *
9847         * @param state Current State of RecyclerView where you can find total item count
9848         * @return The horizontal offset of the scrollbar's thumb
9849         * @see RecyclerView#computeHorizontalScrollOffset()
9850         */
9851        public int computeHorizontalScrollOffset(@NonNull State state) {
9852            return 0;
9853        }
9854
9855        /**
9856         * <p>Override this method if you want to support scroll bars.</p>
9857         *
9858         * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
9859         *
9860         * <p>Default implementation returns 0.</p>
9861         *
9862         * @param state Current State of RecyclerView where you can find total item count
9863         * @return The total horizontal range represented by the vertical scrollbar
9864         * @see RecyclerView#computeHorizontalScrollRange()
9865         */
9866        public int computeHorizontalScrollRange(@NonNull State state) {
9867            return 0;
9868        }
9869
9870        /**
9871         * <p>Override this method if you want to support scroll bars.</p>
9872         *
9873         * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
9874         *
9875         * <p>Default implementation returns 0.</p>
9876         *
9877         * @param state Current state of RecyclerView
9878         * @return The vertical extent of the scrollbar's thumb
9879         * @see RecyclerView#computeVerticalScrollExtent()
9880         */
9881        public int computeVerticalScrollExtent(@NonNull State state) {
9882            return 0;
9883        }
9884
9885        /**
9886         * <p>Override this method if you want to support scroll bars.</p>
9887         *
9888         * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
9889         *
9890         * <p>Default implementation returns 0.</p>
9891         *
9892         * @param state Current State of RecyclerView where you can find total item count
9893         * @return The vertical offset of the scrollbar's thumb
9894         * @see RecyclerView#computeVerticalScrollOffset()
9895         */
9896        public int computeVerticalScrollOffset(@NonNull State state) {
9897            return 0;
9898        }
9899
9900        /**
9901         * <p>Override this method if you want to support scroll bars.</p>
9902         *
9903         * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
9904         *
9905         * <p>Default implementation returns 0.</p>
9906         *
9907         * @param state Current State of RecyclerView where you can find total item count
9908         * @return The total vertical range represented by the vertical scrollbar
9909         * @see RecyclerView#computeVerticalScrollRange()
9910         */
9911        public int computeVerticalScrollRange(@NonNull State state) {
9912            return 0;
9913        }
9914
9915        /**
9916         * Measure the attached RecyclerView. Implementations must call
9917         * {@link #setMeasuredDimension(int, int)} before returning.
9918         * <p>
9919         * It is strongly advised to use the AutoMeasure mechanism by overriding
9920         * {@link #isAutoMeasureEnabled()} to return true as AutoMeasure handles all the standard
9921         * measure cases including when the RecyclerView's layout_width or layout_height have been
9922         * set to wrap_content.  If {@link #isAutoMeasureEnabled()} is overridden to return true,
9923         * this method should not be overridden.
9924         * <p>
9925         * The default implementation will handle EXACTLY measurements and respect
9926         * the minimum width and height properties of the host RecyclerView if measured
9927         * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
9928         * will consume all available space.
9929         *
9930         * @param recycler Recycler
9931         * @param state Transient state of RecyclerView
9932         * @param widthSpec Width {@link android.view.View.MeasureSpec}
9933         * @param heightSpec Height {@link android.view.View.MeasureSpec}
9934         *
9935         * @see #isAutoMeasureEnabled()
9936         * @see #setMeasuredDimension(int, int)
9937         */
9938        public void onMeasure(@NonNull Recycler recycler, @NonNull State state, int widthSpec,
9939                int heightSpec) {
9940            mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
9941        }
9942
9943        /**
9944         * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
9945         * host RecyclerView.
9946         *
9947         * @param widthSize Measured width
9948         * @param heightSize Measured height
9949         */
9950        public void setMeasuredDimension(int widthSize, int heightSize) {
9951            mRecyclerView.setMeasuredDimension(widthSize, heightSize);
9952        }
9953
9954        /**
9955         * @return The host RecyclerView's {@link View#getMinimumWidth()}
9956         */
9957        @Px
9958        public int getMinimumWidth() {
9959            return ViewCompat.getMinimumWidth(mRecyclerView);
9960        }
9961
9962        /**
9963         * @return The host RecyclerView's {@link View#getMinimumHeight()}
9964         */
9965        @Px
9966        public int getMinimumHeight() {
9967            return ViewCompat.getMinimumHeight(mRecyclerView);
9968        }
9969        /**
9970         * <p>Called when the LayoutManager should save its state. This is a good time to save your
9971         * scroll position, configuration and anything else that may be required to restore the same
9972         * layout state if the LayoutManager is recreated.</p>
9973         * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
9974         * restore. This will let you share information between your LayoutManagers but it is also
9975         * your responsibility to make sure they use the same parcelable class.</p>
9976         *
9977         * @return Necessary information for LayoutManager to be able to restore its state
9978         */
9979        @Nullable
9980        public Parcelable onSaveInstanceState() {
9981            return null;
9982        }
9983
9984
9985        public void onRestoreInstanceState(Parcelable state) {
9986
9987        }
9988
9989        void stopSmoothScroller() {
9990            if (mSmoothScroller != null) {
9991                mSmoothScroller.stop();
9992            }
9993        }
9994
9995        private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
9996            if (mSmoothScroller == smoothScroller) {
9997                mSmoothScroller = null;
9998            }
9999        }
10000
10001        /**
10002         * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
10003         *
10004         * @param state The new scroll state for RecyclerView
10005         */
10006        public void onScrollStateChanged(int state) {
10007        }
10008
10009        /**
10010         * Removes all views and recycles them using the given recycler.
10011         * <p>
10012         * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
10013         * <p>
10014         * If a View is marked as "ignored", it is not removed nor recycled.
10015         *
10016         * @param recycler Recycler to use to recycle children
10017         * @see #removeAndRecycleView(View, Recycler)
10018         * @see #removeAndRecycleViewAt(int, Recycler)
10019         * @see #ignoreView(View)
10020         */
10021        public void removeAndRecycleAllViews(@NonNull Recycler recycler) {
10022            for (int i = getChildCount() - 1; i >= 0; i--) {
10023                final View view = getChildAt(i);
10024                if (!getChildViewHolderInt(view).shouldIgnore()) {
10025                    removeAndRecycleViewAt(i, recycler);
10026                }
10027            }
10028        }
10029
10030        // called by accessibility delegate
10031        void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
10032            onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
10033        }
10034
10035        /**
10036         * Called by the AccessibilityDelegate when the information about the current layout should
10037         * be populated.
10038         * <p>
10039         * Default implementation adds a {@link
10040         * androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}.
10041         * <p>
10042         * You should override
10043         * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
10044         * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
10045         * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
10046         * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
10047         * more accurate accessibility information.
10048         *
10049         * @param recycler The Recycler that can be used to convert view positions into adapter
10050         *                 positions
10051         * @param state    The current state of RecyclerView
10052         * @param info     The info that should be filled by the LayoutManager
10053         * @see View#onInitializeAccessibilityNodeInfo(
10054         *android.view.accessibility.AccessibilityNodeInfo)
10055         * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
10056         * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
10057         * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
10058         * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
10059         */
10060        public void onInitializeAccessibilityNodeInfo(@NonNull Recycler recycler,
10061                @NonNull State state, @NonNull AccessibilityNodeInfoCompat info) {
10062            if (mRecyclerView.canScrollVertically(-1) || mRecyclerView.canScrollHorizontally(-1)) {
10063                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
10064                info.setScrollable(true);
10065            }
10066            if (mRecyclerView.canScrollVertically(1) || mRecyclerView.canScrollHorizontally(1)) {
10067                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
10068                info.setScrollable(true);
10069            }
10070            final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo =
10071                    AccessibilityNodeInfoCompat.CollectionInfoCompat
10072                            .obtain(getRowCountForAccessibility(recycler, state),
10073                                    getColumnCountForAccessibility(recycler, state),
10074                                    isLayoutHierarchical(recycler, state),
10075                                    getSelectionModeForAccessibility(recycler, state));
10076            info.setCollectionInfo(collectionInfo);
10077        }
10078
10079        // called by accessibility delegate
10080        public void onInitializeAccessibilityEvent(@NonNull AccessibilityEvent event) {
10081            onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
10082        }
10083
10084        /**
10085         * Called by the accessibility delegate to initialize an accessibility event.
10086         * <p>
10087         * Default implementation adds item count and scroll information to the event.
10088         *
10089         * @param recycler The Recycler that can be used to convert view positions into adapter
10090         *                 positions
10091         * @param state    The current state of RecyclerView
10092         * @param event    The event instance to initialize
10093         * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
10094         */
10095        public void onInitializeAccessibilityEvent(@NonNull Recycler recycler, @NonNull State state,
10096                @NonNull AccessibilityEvent event) {
10097            if (mRecyclerView == null || event == null) {
10098                return;
10099            }
10100            event.setScrollable(mRecyclerView.canScrollVertically(1)
10101                    || mRecyclerView.canScrollVertically(-1)
10102                    || mRecyclerView.canScrollHorizontally(-1)
10103                    || mRecyclerView.canScrollHorizontally(1));
10104
10105            if (mRecyclerView.mAdapter != null) {
10106                event.setItemCount(mRecyclerView.mAdapter.getItemCount());
10107            }
10108        }
10109
10110        // called by accessibility delegate
10111        void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) {
10112            final ViewHolder vh = getChildViewHolderInt(host);
10113            // avoid trying to create accessibility node info for removed children
10114            if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
10115                onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
10116                        mRecyclerView.mState, host, info);
10117            }
10118        }
10119
10120        /**
10121         * Called by the AccessibilityDelegate when the accessibility information for a specific
10122         * item should be populated.
10123         * <p>
10124         * Default implementation adds basic positioning information about the item.
10125         *
10126         * @param recycler The Recycler that can be used to convert view positions into adapter
10127         *                 positions
10128         * @param state    The current state of RecyclerView
10129         * @param host     The child for which accessibility node info should be populated
10130         * @param info     The info to fill out about the item
10131         * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
10132         * android.view.accessibility.AccessibilityNodeInfo)
10133         */
10134        public void onInitializeAccessibilityNodeInfoForItem(@NonNull Recycler recycler,
10135                @NonNull State state, @NonNull View host,
10136                @NonNull AccessibilityNodeInfoCompat info) {
10137            int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
10138            int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
10139            final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo =
10140                    AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
10141                            columnIndexGuess, 1, false, false);
10142            info.setCollectionItemInfo(itemInfo);
10143        }
10144
10145        /**
10146         * A LayoutManager can call this method to force RecyclerView to run simple animations in
10147         * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
10148         * change).
10149         * <p>
10150         * Note that, calling this method will not guarantee that RecyclerView will run animations
10151         * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
10152         * not run any animations but will still clear this flag after the layout is complete.
10153         *
10154         */
10155        public void requestSimpleAnimationsInNextLayout() {
10156            mRequestedSimpleAnimations = true;
10157        }
10158
10159        /**
10160         * Returns the selection mode for accessibility. Should be
10161         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE},
10162         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or
10163         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}.
10164         * <p>
10165         * Default implementation returns
10166         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
10167         *
10168         * @param recycler The Recycler that can be used to convert view positions into adapter
10169         *                 positions
10170         * @param state    The current state of RecyclerView
10171         * @return Selection mode for accessibility. Default implementation returns
10172         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
10173         */
10174        public int getSelectionModeForAccessibility(@NonNull Recycler recycler,
10175                @NonNull State state) {
10176            return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
10177        }
10178
10179        /**
10180         * Returns the number of rows for accessibility.
10181         * <p>
10182         * Default implementation returns the number of items in the adapter if LayoutManager
10183         * supports vertical scrolling or 1 if LayoutManager does not support vertical
10184         * scrolling.
10185         *
10186         * @param recycler The Recycler that can be used to convert view positions into adapter
10187         *                 positions
10188         * @param state    The current state of RecyclerView
10189         * @return The number of rows in LayoutManager for accessibility.
10190         */
10191        public int getRowCountForAccessibility(@NonNull Recycler recycler, @NonNull State state) {
10192            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
10193                return 1;
10194            }
10195            return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
10196        }
10197
10198        /**
10199         * Returns the number of columns for accessibility.
10200         * <p>
10201         * Default implementation returns the number of items in the adapter if LayoutManager
10202         * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
10203         * scrolling.
10204         *
10205         * @param recycler The Recycler that can be used to convert view positions into adapter
10206         *                 positions
10207         * @param state    The current state of RecyclerView
10208         * @return The number of rows in LayoutManager for accessibility.
10209         */
10210        public int getColumnCountForAccessibility(@NonNull Recycler recycler,
10211                @NonNull State state) {
10212            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
10213                return 1;
10214            }
10215            return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
10216        }
10217
10218        /**
10219         * Returns whether layout is hierarchical or not to be used for accessibility.
10220         * <p>
10221         * Default implementation returns false.
10222         *
10223         * @param recycler The Recycler that can be used to convert view positions into adapter
10224         *                 positions
10225         * @param state    The current state of RecyclerView
10226         * @return True if layout is hierarchical.
10227         */
10228        public boolean isLayoutHierarchical(@NonNull Recycler recycler, @NonNull State state) {
10229            return false;
10230        }
10231
10232        // called by accessibility delegate
10233        boolean performAccessibilityAction(int action, @Nullable Bundle args) {
10234            return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
10235                    action, args);
10236        }
10237
10238        /**
10239         * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
10240         *
10241         * @param recycler  The Recycler that can be used to convert view positions into adapter
10242         *                  positions
10243         * @param state     The current state of RecyclerView
10244         * @param action    The action to perform
10245         * @param args      Optional action arguments
10246         * @see View#performAccessibilityAction(int, android.os.Bundle)
10247         */
10248        public boolean performAccessibilityAction(@NonNull Recycler recycler, @NonNull State state,
10249                int action, @Nullable Bundle args) {
10250            if (mRecyclerView == null) {
10251                return false;
10252            }
10253            int vScroll = 0, hScroll = 0;
10254            switch (action) {
10255                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
10256                    if (mRecyclerView.canScrollVertically(-1)) {
10257                        vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
10258                    }
10259                    if (mRecyclerView.canScrollHorizontally(-1)) {
10260                        hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
10261                    }
10262                    break;
10263                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
10264                    if (mRecyclerView.canScrollVertically(1)) {
10265                        vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
10266                    }
10267                    if (mRecyclerView.canScrollHorizontally(1)) {
10268                        hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
10269                    }
10270                    break;
10271            }
10272            if (vScroll == 0 && hScroll == 0) {
10273                return false;
10274            }
10275            mRecyclerView.smoothScrollBy(hScroll, vScroll);
10276            return true;
10277        }
10278
10279        // called by accessibility delegate
10280        boolean performAccessibilityActionForItem(@NonNull View view, int action,
10281                @Nullable Bundle args) {
10282            return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
10283                    view, action, args);
10284        }
10285
10286        /**
10287         * Called by AccessibilityDelegate when an accessibility action is requested on one of the
10288         * children of LayoutManager.
10289         * <p>
10290         * Default implementation does not do anything.
10291         *
10292         * @param recycler The Recycler that can be used to convert view positions into adapter
10293         *                 positions
10294         * @param state    The current state of RecyclerView
10295         * @param view     The child view on which the action is performed
10296         * @param action   The action to perform
10297         * @param args     Optional action arguments
10298         * @return true if action is handled
10299         * @see View#performAccessibilityAction(int, android.os.Bundle)
10300         */
10301        public boolean performAccessibilityActionForItem(@NonNull Recycler recycler,
10302                @NonNull State state, @NonNull View view, int action, @Nullable Bundle args) {
10303            return false;
10304        }
10305
10306        /**
10307         * Parse the xml attributes to get the most common properties used by layout managers.
10308         *
10309         * @attr ref androidx.recyclerview.R.styleable#RecyclerView_android_orientation
10310         * @attr ref androidx.recyclerview.R.styleable#RecyclerView_spanCount
10311         * @attr ref androidx.recyclerview.R.styleable#RecyclerView_reverseLayout
10312         * @attr ref androidx.recyclerview.R.styleable#RecyclerView_stackFromEnd
10313         *
10314         * @return an object containing the properties as specified in the attrs.
10315         */
10316        public static Properties getProperties(@NonNull Context context,
10317                @Nullable AttributeSet attrs,
10318                int defStyleAttr, int defStyleRes) {
10319            Properties properties = new Properties();
10320            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
10321                    defStyleAttr, defStyleRes);
10322            properties.orientation = a.getInt(R.styleable.RecyclerView_android_orientation,
10323                    DEFAULT_ORIENTATION);
10324            properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
10325            properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
10326            properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
10327            a.recycle();
10328            return properties;
10329        }
10330
10331        void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
10332            setMeasureSpecs(
10333                    MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
10334                    MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
10335            );
10336        }
10337
10338        /**
10339         * Internal API to allow LayoutManagers to be measured twice.
10340         * <p>
10341         * This is not public because LayoutManagers should be able to handle their layouts in one
10342         * pass but it is very convenient to make existing LayoutManagers support wrapping content
10343         * when both orientations are undefined.
10344         * <p>
10345         * This API will be removed after default LayoutManagers properly implement wrap content in
10346         * non-scroll orientation.
10347         */
10348        boolean shouldMeasureTwice() {
10349            return false;
10350        }
10351
10352        boolean hasFlexibleChildInBothOrientations() {
10353            final int childCount = getChildCount();
10354            for (int i = 0; i < childCount; i++) {
10355                final View child = getChildAt(i);
10356                final ViewGroup.LayoutParams lp = child.getLayoutParams();
10357                if (lp.width < 0 && lp.height < 0) {
10358                    return true;
10359                }
10360            }
10361            return false;
10362        }
10363
10364        /**
10365         * Some general properties that a LayoutManager may want to use.
10366         */
10367        public static class Properties {
10368            /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_android_orientation */
10369            public int orientation;
10370            /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_spanCount */
10371            public int spanCount;
10372            /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_reverseLayout */
10373            public boolean reverseLayout;
10374            /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_stackFromEnd */
10375            public boolean stackFromEnd;
10376        }
10377    }
10378
10379    /**
10380     * An ItemDecoration allows the application to add a special drawing and layout offset
10381     * to specific item views from the adapter's data set. This can be useful for drawing dividers
10382     * between items, highlights, visual grouping boundaries and more.
10383     *
10384     * <p>All ItemDecorations are drawn in the order they were added, before the item
10385     * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
10386     * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
10387     * RecyclerView.State)}.</p>
10388     */
10389    public abstract static class ItemDecoration {
10390        /**
10391         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
10392         * Any content drawn by this method will be drawn before the item views are drawn,
10393         * and will thus appear underneath the views.
10394         *
10395         * @param c Canvas to draw into
10396         * @param parent RecyclerView this ItemDecoration is drawing into
10397         * @param state The current state of RecyclerView
10398         */
10399        public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull State state) {
10400            onDraw(c, parent);
10401        }
10402
10403        /**
10404         * @deprecated
10405         * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
10406         */
10407        @Deprecated
10408        public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent) {
10409        }
10410
10411        /**
10412         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
10413         * Any content drawn by this method will be drawn after the item views are drawn
10414         * and will thus appear over the views.
10415         *
10416         * @param c Canvas to draw into
10417         * @param parent RecyclerView this ItemDecoration is drawing into
10418         * @param state The current state of RecyclerView.
10419         */
10420        public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent,
10421                @NonNull State state) {
10422            onDrawOver(c, parent);
10423        }
10424
10425        /**
10426         * @deprecated
10427         * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
10428         */
10429        @Deprecated
10430        public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent) {
10431        }
10432
10433
10434        /**
10435         * @deprecated
10436         * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
10437         */
10438        @Deprecated
10439        public void getItemOffsets(@NonNull Rect outRect, int itemPosition,
10440                @NonNull RecyclerView parent) {
10441            outRect.set(0, 0, 0, 0);
10442        }
10443
10444        /**
10445         * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
10446         * the number of pixels that the item view should be inset by, similar to padding or margin.
10447         * The default implementation sets the bounds of outRect to 0 and returns.
10448         *
10449         * <p>
10450         * If this ItemDecoration does not affect the positioning of item views, it should set
10451         * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
10452         * before returning.
10453         *
10454         * <p>
10455         * If you need to access Adapter for additional data, you can call
10456         * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
10457         * View.
10458         *
10459         * @param outRect Rect to receive the output.
10460         * @param view    The child view to decorate
10461         * @param parent  RecyclerView this ItemDecoration is decorating
10462         * @param state   The current state of RecyclerView.
10463         */
10464        public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
10465                @NonNull RecyclerView parent, @NonNull State state) {
10466            getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
10467                    parent);
10468        }
10469    }
10470
10471    /**
10472     * An OnItemTouchListener allows the application to intercept touch events in progress at the
10473     * view hierarchy level of the RecyclerView before those touch events are considered for
10474     * RecyclerView's own scrolling behavior.
10475     *
10476     * <p>This can be useful for applications that wish to implement various forms of gestural
10477     * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
10478     * a touch interaction already in progress even if the RecyclerView is already handling that
10479     * gesture stream itself for the purposes of scrolling.</p>
10480     *
10481     * @see SimpleOnItemTouchListener
10482     */
10483    public interface OnItemTouchListener {
10484        /**
10485         * Silently observe and/or take over touch events sent to the RecyclerView
10486         * before they are handled by either the RecyclerView itself or its child views.
10487         *
10488         * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
10489         * in the order in which each listener was added, before any other touch processing
10490         * by the RecyclerView itself or child views occurs.</p>
10491         *
10492         * @param e MotionEvent describing the touch event. All coordinates are in
10493         *          the RecyclerView's coordinate system.
10494         * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
10495         *         to continue with the current behavior and continue observing future events in
10496         *         the gesture.
10497         */
10498        boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
10499
10500        /**
10501         * Process a touch event as part of a gesture that was claimed by returning true from
10502         * a previous call to {@link #onInterceptTouchEvent}.
10503         *
10504         * @param e MotionEvent describing the touch event. All coordinates are in
10505         *          the RecyclerView's coordinate system.
10506         */
10507        void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
10508
10509        /**
10510         * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
10511         * intercept touch events with
10512         * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
10513         *
10514         * @param disallowIntercept True if the child does not want the parent to
10515         *            intercept touch events.
10516         * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
10517         */
10518        void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
10519    }
10520
10521    /**
10522     * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies
10523     * and default return values.
10524     * <p>
10525     * You may prefer to extend this class if you don't need to override all methods. Another
10526     * benefit of using this class is future compatibility. As the interface may change, we'll
10527     * always provide a default implementation on this class so that your code won't break when
10528     * you update to a new version of the support library.
10529     */
10530    public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
10531        @Override
10532        public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
10533            return false;
10534        }
10535
10536        @Override
10537        public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
10538        }
10539
10540        @Override
10541        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
10542        }
10543    }
10544
10545
10546    /**
10547     * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
10548     * has occurred on that RecyclerView.
10549     * <p>
10550     * @see RecyclerView#addOnScrollListener(OnScrollListener)
10551     * @see RecyclerView#clearOnChildAttachStateChangeListeners()
10552     *
10553     */
10554    public abstract static class OnScrollListener {
10555        /**
10556         * Callback method to be invoked when RecyclerView's scroll state changes.
10557         *
10558         * @param recyclerView The RecyclerView whose scroll state has changed.
10559         * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
10560         *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
10561         */
10562        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState){}
10563
10564        /**
10565         * Callback method to be invoked when the RecyclerView has been scrolled. This will be
10566         * called after the scroll has completed.
10567         * <p>
10568         * This callback will also be called if visible item range changes after a layout
10569         * calculation. In that case, dx and dy will be 0.
10570         *
10571         * @param recyclerView The RecyclerView which scrolled.
10572         * @param dx The amount of horizontal scroll.
10573         * @param dy The amount of vertical scroll.
10574         */
10575        public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){}
10576    }
10577
10578    /**
10579     * A RecyclerListener can be set on a RecyclerView to receive messages whenever
10580     * a view is recycled.
10581     *
10582     * @see RecyclerView#setRecyclerListener(RecyclerListener)
10583     */
10584    public interface RecyclerListener {
10585
10586        /**
10587         * This method is called whenever the view in the ViewHolder is recycled.
10588         *
10589         * RecyclerView calls this method right before clearing ViewHolder's internal data and
10590         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
10591         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
10592         * its adapter position.
10593         *
10594         * @param holder The ViewHolder containing the view that was recycled
10595         */
10596        void onViewRecycled(@NonNull ViewHolder holder);
10597    }
10598
10599    /**
10600     * A Listener interface that can be attached to a RecylcerView to get notified
10601     * whenever a ViewHolder is attached to or detached from RecyclerView.
10602     */
10603    public interface OnChildAttachStateChangeListener {
10604
10605        /**
10606         * Called when a view is attached to the RecyclerView.
10607         *
10608         * @param view The View which is attached to the RecyclerView
10609         */
10610        void onChildViewAttachedToWindow(@NonNull View view);
10611
10612        /**
10613         * Called when a view is detached from RecyclerView.
10614         *
10615         * @param view The View which is being detached from the RecyclerView
10616         */
10617        void onChildViewDetachedFromWindow(@NonNull View view);
10618    }
10619
10620    /**
10621     * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
10622     *
10623     * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
10624     * potentially expensive {@link View#findViewById(int)} results.</p>
10625     *
10626     * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
10627     * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
10628     * their own custom ViewHolder implementations to store data that makes binding view contents
10629     * easier. Implementations should assume that individual item views will hold strong references
10630     * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
10631     * strong references to extra off-screen item views for caching purposes</p>
10632     */
10633    public abstract static class ViewHolder {
10634        @NonNull
10635        public final View itemView;
10636        WeakReference<RecyclerView> mNestedRecyclerView;
10637        int mPosition = NO_POSITION;
10638        int mOldPosition = NO_POSITION;
10639        long mItemId = NO_ID;
10640        int mItemViewType = INVALID_TYPE;
10641        int mPreLayoutPosition = NO_POSITION;
10642
10643        // The item that this holder is shadowing during an item change event/animation
10644        ViewHolder mShadowedHolder = null;
10645        // The item that is shadowing this holder during an item change event/animation
10646        ViewHolder mShadowingHolder = null;
10647
10648        /**
10649         * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
10650         * are all valid.
10651         */
10652        static final int FLAG_BOUND = 1 << 0;
10653
10654        /**
10655         * The data this ViewHolder's view reflects is stale and needs to be rebound
10656         * by the adapter. mPosition and mItemId are consistent.
10657         */
10658        static final int FLAG_UPDATE = 1 << 1;
10659
10660        /**
10661         * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
10662         * are not to be trusted and may no longer match the item view type.
10663         * This ViewHolder must be fully rebound to different data.
10664         */
10665        static final int FLAG_INVALID = 1 << 2;
10666
10667        /**
10668         * This ViewHolder points at data that represents an item previously removed from the
10669         * data set. Its view may still be used for things like outgoing animations.
10670         */
10671        static final int FLAG_REMOVED = 1 << 3;
10672
10673        /**
10674         * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
10675         * and is intended to keep views around during animations.
10676         */
10677        static final int FLAG_NOT_RECYCLABLE = 1 << 4;
10678
10679        /**
10680         * This ViewHolder is returned from scrap which means we are expecting an addView call
10681         * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
10682         * the end of the layout pass and then recycled by RecyclerView if it is not added back to
10683         * the RecyclerView.
10684         */
10685        static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
10686
10687        /**
10688         * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
10689         * it unless LayoutManager is replaced.
10690         * It is still fully visible to the LayoutManager.
10691         */
10692        static final int FLAG_IGNORE = 1 << 7;
10693
10694        /**
10695         * When the View is detached form the parent, we set this flag so that we can take correct
10696         * action when we need to remove it or add it back.
10697         */
10698        static final int FLAG_TMP_DETACHED = 1 << 8;
10699
10700        /**
10701         * Set when we can no longer determine the adapter position of this ViewHolder until it is
10702         * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
10703         * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
10704         * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
10705         * re-calculated.
10706         */
10707        static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
10708
10709        /**
10710         * Set when a addChangePayload(null) is called
10711         */
10712        static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
10713
10714        /**
10715         * Used by ItemAnimator when a ViewHolder's position changes
10716         */
10717        static final int FLAG_MOVED = 1 << 11;
10718
10719        /**
10720         * Used by ItemAnimator when a ViewHolder appears in pre-layout
10721         */
10722        static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
10723
10724        static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
10725
10726        /**
10727         * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
10728         * hidden list (as if it was scrap) without being recycled in between.
10729         *
10730         * When a ViewHolder is hidden, there are 2 paths it can be re-used:
10731         *   a) Animation ends, view is recycled and used from the recycle pool.
10732         *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
10733         *
10734         * This flag is used to represent "case b" where the ViewHolder is reused without being
10735         * recycled (thus "bounced" from the hidden list). This state requires special handling
10736         * because the ViewHolder must be added to pre layout maps for animations as if it was
10737         * already there.
10738         */
10739        static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
10740
10741        /**
10742         * Flags that RecyclerView assigned {@link RecyclerViewAccessibilityDelegate
10743         * #getItemDelegate()} in onBindView when app does not provide a delegate.
10744         */
10745        static final int FLAG_SET_A11Y_ITEM_DELEGATE = 1 << 14;
10746
10747        private int mFlags;
10748
10749        private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
10750
10751        List<Object> mPayloads = null;
10752        List<Object> mUnmodifiedPayloads = null;
10753
10754        private int mIsRecyclableCount = 0;
10755
10756        // If non-null, view is currently considered scrap and may be reused for other data by the
10757        // scrap container.
10758        private Recycler mScrapContainer = null;
10759        // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
10760        private boolean mInChangeScrap = false;
10761
10762        // Saves isImportantForAccessibility value for the view item while it's in hidden state and
10763        // marked as unimportant for accessibility.
10764        private int mWasImportantForAccessibilityBeforeHidden =
10765                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10766        // set if we defer the accessibility state change of the view holder
10767        @VisibleForTesting
10768        int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
10769
10770        /**
10771         * Is set when VH is bound from the adapter and cleaned right before it is sent to
10772         * {@link RecycledViewPool}.
10773         */
10774        RecyclerView mOwnerRecyclerView;
10775
10776        public ViewHolder(@NonNull View itemView) {
10777            if (itemView == null) {
10778                throw new IllegalArgumentException("itemView may not be null");
10779            }
10780            this.itemView = itemView;
10781        }
10782
10783        void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
10784            addFlags(ViewHolder.FLAG_REMOVED);
10785            offsetPosition(offset, applyToPreLayout);
10786            mPosition = mNewPosition;
10787        }
10788
10789        void offsetPosition(int offset, boolean applyToPreLayout) {
10790            if (mOldPosition == NO_POSITION) {
10791                mOldPosition = mPosition;
10792            }
10793            if (mPreLayoutPosition == NO_POSITION) {
10794                mPreLayoutPosition = mPosition;
10795            }
10796            if (applyToPreLayout) {
10797                mPreLayoutPosition += offset;
10798            }
10799            mPosition += offset;
10800            if (itemView.getLayoutParams() != null) {
10801                ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
10802            }
10803        }
10804
10805        void clearOldPosition() {
10806            mOldPosition = NO_POSITION;
10807            mPreLayoutPosition = NO_POSITION;
10808        }
10809
10810        void saveOldPosition() {
10811            if (mOldPosition == NO_POSITION) {
10812                mOldPosition = mPosition;
10813            }
10814        }
10815
10816        boolean shouldIgnore() {
10817            return (mFlags & FLAG_IGNORE) != 0;
10818        }
10819
10820        /**
10821         * @deprecated This method is deprecated because its meaning is ambiguous due to the async
10822         * handling of adapter updates. Please use {@link #getLayoutPosition()} or
10823         * {@link #getAdapterPosition()} depending on your use case.
10824         *
10825         * @see #getLayoutPosition()
10826         * @see #getAdapterPosition()
10827         */
10828        @Deprecated
10829        public final int getPosition() {
10830            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10831        }
10832
10833        /**
10834         * Returns the position of the ViewHolder in terms of the latest layout pass.
10835         * <p>
10836         * This position is mostly used by RecyclerView components to be consistent while
10837         * RecyclerView lazily processes adapter updates.
10838         * <p>
10839         * For performance and animation reasons, RecyclerView batches all adapter updates until the
10840         * next layout pass. This may cause mismatches between the Adapter position of the item and
10841         * the position it had in the latest layout calculations.
10842         * <p>
10843         * LayoutManagers should always call this method while doing calculations based on item
10844         * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
10845         * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
10846         * of the item.
10847         * <p>
10848         * If LayoutManager needs to call an external method that requires the adapter position of
10849         * the item, it can use {@link #getAdapterPosition()} or
10850         * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
10851         *
10852         * @return Returns the adapter position of the ViewHolder in the latest layout pass.
10853         * @see #getAdapterPosition()
10854         */
10855        public final int getLayoutPosition() {
10856            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10857        }
10858
10859        /**
10860         * Returns the Adapter position of the item represented by this ViewHolder.
10861         * <p>
10862         * Note that this might be different than the {@link #getLayoutPosition()} if there are
10863         * pending adapter updates but a new layout pass has not happened yet.
10864         * <p>
10865         * RecyclerView does not handle any adapter updates until the next layout traversal. This
10866         * may create temporary inconsistencies between what user sees on the screen and what
10867         * adapter contents have. This inconsistency is not important since it will be less than
10868         * 16ms but it might be a problem if you want to use ViewHolder position to access the
10869         * adapter. Sometimes, you may need to get the exact adapter position to do
10870         * some actions in response to user events. In that case, you should use this method which
10871         * will calculate the Adapter position of the ViewHolder.
10872         * <p>
10873         * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
10874         * next layout pass, the return value of this method will be {@link #NO_POSITION}.
10875         *
10876         * @return The adapter position of the item if it still exists in the adapter.
10877         * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
10878         * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
10879         * layout pass or the ViewHolder has already been recycled.
10880         */
10881        public final int getAdapterPosition() {
10882            if (mOwnerRecyclerView == null) {
10883                return NO_POSITION;
10884            }
10885            return mOwnerRecyclerView.getAdapterPositionFor(this);
10886        }
10887
10888        /**
10889         * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
10890         * to perform animations.
10891         * <p>
10892         * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
10893         * adapter index in the previous layout.
10894         *
10895         * @return The previous adapter index of the Item represented by this ViewHolder or
10896         * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
10897         * complete).
10898         */
10899        public final int getOldPosition() {
10900            return mOldPosition;
10901        }
10902
10903        /**
10904         * Returns The itemId represented by this ViewHolder.
10905         *
10906         * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
10907         * otherwise
10908         */
10909        public final long getItemId() {
10910            return mItemId;
10911        }
10912
10913        /**
10914         * @return The view type of this ViewHolder.
10915         */
10916        public final int getItemViewType() {
10917            return mItemViewType;
10918        }
10919
10920        boolean isScrap() {
10921            return mScrapContainer != null;
10922        }
10923
10924        void unScrap() {
10925            mScrapContainer.unscrapView(this);
10926        }
10927
10928        boolean wasReturnedFromScrap() {
10929            return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
10930        }
10931
10932        void clearReturnedFromScrapFlag() {
10933            mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
10934        }
10935
10936        void clearTmpDetachFlag() {
10937            mFlags = mFlags & ~FLAG_TMP_DETACHED;
10938        }
10939
10940        void stopIgnoring() {
10941            mFlags = mFlags & ~FLAG_IGNORE;
10942        }
10943
10944        void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
10945            mScrapContainer = recycler;
10946            mInChangeScrap = isChangeScrap;
10947        }
10948
10949        boolean isInvalid() {
10950            return (mFlags & FLAG_INVALID) != 0;
10951        }
10952
10953        boolean needsUpdate() {
10954            return (mFlags & FLAG_UPDATE) != 0;
10955        }
10956
10957        boolean isBound() {
10958            return (mFlags & FLAG_BOUND) != 0;
10959        }
10960
10961        boolean isRemoved() {
10962            return (mFlags & FLAG_REMOVED) != 0;
10963        }
10964
10965        boolean hasAnyOfTheFlags(int flags) {
10966            return (mFlags & flags) != 0;
10967        }
10968
10969        boolean isTmpDetached() {
10970            return (mFlags & FLAG_TMP_DETACHED) != 0;
10971        }
10972
10973        boolean isAdapterPositionUnknown() {
10974            return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
10975        }
10976
10977        void setFlags(int flags, int mask) {
10978            mFlags = (mFlags & ~mask) | (flags & mask);
10979        }
10980
10981        void addFlags(int flags) {
10982            mFlags |= flags;
10983        }
10984
10985        void addChangePayload(Object payload) {
10986            if (payload == null) {
10987                addFlags(FLAG_ADAPTER_FULLUPDATE);
10988            } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10989                createPayloadsIfNeeded();
10990                mPayloads.add(payload);
10991            }
10992        }
10993
10994        private void createPayloadsIfNeeded() {
10995            if (mPayloads == null) {
10996                mPayloads = new ArrayList<Object>();
10997                mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
10998            }
10999        }
11000
11001        void clearPayload() {
11002            if (mPayloads != null) {
11003                mPayloads.clear();
11004            }
11005            mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
11006        }
11007
11008        List<Object> getUnmodifiedPayloads() {
11009            if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
11010                if (mPayloads == null || mPayloads.size() == 0) {
11011                    // Initial state,  no update being called.
11012                    return FULLUPDATE_PAYLOADS;
11013                }
11014                // there are none-null payloads
11015                return mUnmodifiedPayloads;
11016            } else {
11017                // a full update has been called.
11018                return FULLUPDATE_PAYLOADS;
11019            }
11020        }
11021
11022        void resetInternal() {
11023            mFlags = 0;
11024            mPosition = NO_POSITION;
11025            mOldPosition = NO_POSITION;
11026            mItemId = NO_ID;
11027            mPreLayoutPosition = NO_POSITION;
11028            mIsRecyclableCount = 0;
11029            mShadowedHolder = null;
11030            mShadowingHolder = null;
11031            clearPayload();
11032            mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
11033            mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
11034            clearNestedRecyclerViewIfNotNested(this);
11035        }
11036
11037        /**
11038         * Called when the child view enters the hidden state
11039         */
11040        private void onEnteredHiddenState(RecyclerView parent) {
11041            // While the view item is in hidden state, make it invisible for the accessibility.
11042            if (mPendingAccessibilityState != PENDING_ACCESSIBILITY_STATE_NOT_SET) {
11043                mWasImportantForAccessibilityBeforeHidden = mPendingAccessibilityState;
11044            } else {
11045                mWasImportantForAccessibilityBeforeHidden =
11046                        ViewCompat.getImportantForAccessibility(itemView);
11047            }
11048            parent.setChildImportantForAccessibilityInternal(this,
11049                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
11050        }
11051
11052        /**
11053         * Called when the child view leaves the hidden state
11054         */
11055        private void onLeftHiddenState(RecyclerView parent) {
11056            parent.setChildImportantForAccessibilityInternal(this,
11057                    mWasImportantForAccessibilityBeforeHidden);
11058            mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
11059        }
11060
11061        @Override
11062        public String toString() {
11063            final StringBuilder sb = new StringBuilder("ViewHolder{"
11064                    + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
11065                    + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
11066            if (isScrap()) {
11067                sb.append(" scrap ")
11068                        .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
11069            }
11070            if (isInvalid()) sb.append(" invalid");
11071            if (!isBound()) sb.append(" unbound");
11072            if (needsUpdate()) sb.append(" update");
11073            if (isRemoved()) sb.append(" removed");
11074            if (shouldIgnore()) sb.append(" ignored");
11075            if (isTmpDetached()) sb.append(" tmpDetached");
11076            if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
11077            if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
11078
11079            if (itemView.getParent() == null) sb.append(" no parent");
11080            sb.append("}");
11081            return sb.toString();
11082        }
11083
11084        /**
11085         * Informs the recycler whether this item can be recycled. Views which are not
11086         * recyclable will not be reused for other items until setIsRecyclable() is
11087         * later set to true. Calls to setIsRecyclable() should always be paired (one
11088         * call to setIsRecyclabe(false) should always be matched with a later call to
11089         * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
11090         * reference-counted.
11091         *
11092         * @param recyclable Whether this item is available to be recycled. Default value
11093         * is true.
11094         *
11095         * @see #isRecyclable()
11096         */
11097        public final void setIsRecyclable(boolean recyclable) {
11098            mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
11099            if (mIsRecyclableCount < 0) {
11100                mIsRecyclableCount = 0;
11101                if (DEBUG) {
11102                    throw new RuntimeException("isRecyclable decremented below 0: "
11103                            + "unmatched pair of setIsRecyable() calls for " + this);
11104                }
11105                Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: "
11106                        + "unmatched pair of setIsRecyable() calls for " + this);
11107            } else if (!recyclable && mIsRecyclableCount == 1) {
11108                mFlags |= FLAG_NOT_RECYCLABLE;
11109            } else if (recyclable && mIsRecyclableCount == 0) {
11110                mFlags &= ~FLAG_NOT_RECYCLABLE;
11111            }
11112            if (DEBUG) {
11113                Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
11114            }
11115        }
11116
11117        /**
11118         * @return true if this item is available to be recycled, false otherwise.
11119         *
11120         * @see #setIsRecyclable(boolean)
11121         */
11122        public final boolean isRecyclable() {
11123            return (mFlags & FLAG_NOT_RECYCLABLE) == 0
11124                    && !ViewCompat.hasTransientState(itemView);
11125        }
11126
11127        /**
11128         * Returns whether we have animations referring to this view holder or not.
11129         * This is similar to isRecyclable flag but does not check transient state.
11130         */
11131        private boolean shouldBeKeptAsChild() {
11132            return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
11133        }
11134
11135        /**
11136         * @return True if ViewHolder is not referenced by RecyclerView animations but has
11137         * transient state which will prevent it from being recycled.
11138         */
11139        private boolean doesTransientStatePreventRecycling() {
11140            return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView);
11141        }
11142
11143        boolean isUpdated() {
11144            return (mFlags & FLAG_UPDATE) != 0;
11145        }
11146    }
11147
11148    /**
11149     * This method is here so that we can control the important for a11y changes and test it.
11150     */
11151    @VisibleForTesting
11152    boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
11153            int importantForAccessibility) {
11154        if (isComputingLayout()) {
11155            viewHolder.mPendingAccessibilityState = importantForAccessibility;
11156            mPendingAccessibilityImportanceChange.add(viewHolder);
11157            return false;
11158        }
11159        ViewCompat.setImportantForAccessibility(viewHolder.itemView, importantForAccessibility);
11160        return true;
11161    }
11162
11163    void dispatchPendingImportantForAccessibilityChanges() {
11164        for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
11165            ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
11166            if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
11167                continue;
11168            }
11169            int state = viewHolder.mPendingAccessibilityState;
11170            if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
11171                //noinspection WrongConstant
11172                ViewCompat.setImportantForAccessibility(viewHolder.itemView, state);
11173                viewHolder.mPendingAccessibilityState =
11174                        ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
11175            }
11176        }
11177        mPendingAccessibilityImportanceChange.clear();
11178    }
11179
11180    int getAdapterPositionFor(ViewHolder viewHolder) {
11181        if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
11182                | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
11183                || !viewHolder.isBound()) {
11184            return RecyclerView.NO_POSITION;
11185        }
11186        return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
11187    }
11188
11189    @VisibleForTesting
11190    void initFastScroller(StateListDrawable verticalThumbDrawable,
11191            Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable,
11192            Drawable horizontalTrackDrawable) {
11193        if (verticalThumbDrawable == null || verticalTrackDrawable == null
11194                || horizontalThumbDrawable == null || horizontalTrackDrawable == null) {
11195            throw new IllegalArgumentException(
11196                "Trying to set fast scroller without both required drawables." + exceptionLabel());
11197        }
11198
11199        Resources resources = getContext().getResources();
11200        new FastScroller(this, verticalThumbDrawable, verticalTrackDrawable,
11201                horizontalThumbDrawable, horizontalTrackDrawable,
11202                resources.getDimensionPixelSize(R.dimen.fastscroll_default_thickness),
11203                resources.getDimensionPixelSize(R.dimen.fastscroll_minimum_range),
11204                resources.getDimensionPixelOffset(R.dimen.fastscroll_margin));
11205    }
11206
11207    // NestedScrollingChild
11208
11209    @Override
11210    public void setNestedScrollingEnabled(boolean enabled) {
11211        getScrollingChildHelper().setNestedScrollingEnabled(enabled);
11212    }
11213
11214    @Override
11215    public boolean isNestedScrollingEnabled() {
11216        return getScrollingChildHelper().isNestedScrollingEnabled();
11217    }
11218
11219    @Override
11220    public boolean startNestedScroll(int axes) {
11221        return getScrollingChildHelper().startNestedScroll(axes);
11222    }
11223
11224    @Override
11225    public boolean startNestedScroll(int axes, int type) {
11226        return getScrollingChildHelper().startNestedScroll(axes, type);
11227    }
11228
11229    @Override
11230    public void stopNestedScroll() {
11231        getScrollingChildHelper().stopNestedScroll();
11232    }
11233
11234    @Override
11235    public void stopNestedScroll(int type) {
11236        getScrollingChildHelper().stopNestedScroll(type);
11237    }
11238
11239    @Override
11240    public boolean hasNestedScrollingParent() {
11241        return getScrollingChildHelper().hasNestedScrollingParent();
11242    }
11243
11244    @Override
11245    public boolean hasNestedScrollingParent(int type) {
11246        return getScrollingChildHelper().hasNestedScrollingParent(type);
11247    }
11248
11249    @Override
11250    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
11251            int dyUnconsumed, int[] offsetInWindow) {
11252        return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
11253                dxUnconsumed, dyUnconsumed, offsetInWindow);
11254    }
11255
11256    @Override
11257    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
11258            int dyUnconsumed, int[] offsetInWindow, int type) {
11259        return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
11260                dxUnconsumed, dyUnconsumed, offsetInWindow, type);
11261    }
11262
11263    @Override
11264    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
11265        return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
11266    }
11267
11268    @Override
11269    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
11270            int type) {
11271        return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow,
11272                type);
11273    }
11274
11275    @Override
11276    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
11277        return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
11278    }
11279
11280    @Override
11281    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
11282        return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
11283    }
11284
11285    /**
11286     * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
11287     * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
11288     * to create their own subclass of this <code>LayoutParams</code> class
11289     * to store any additional required per-child view metadata about the layout.
11290     */
11291    public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
11292        ViewHolder mViewHolder;
11293        final Rect mDecorInsets = new Rect();
11294        boolean mInsetsDirty = true;
11295        // Flag is set to true if the view is bound while it is detached from RV.
11296        // In this case, we need to manually call invalidate after view is added to guarantee that
11297        // invalidation is populated through the View hierarchy
11298        boolean mPendingInvalidate = false;
11299
11300        public LayoutParams(Context c, AttributeSet attrs) {
11301            super(c, attrs);
11302        }
11303
11304        public LayoutParams(int width, int height) {
11305            super(width, height);
11306        }
11307
11308        public LayoutParams(MarginLayoutParams source) {
11309            super(source);
11310        }
11311
11312        public LayoutParams(ViewGroup.LayoutParams source) {
11313            super(source);
11314        }
11315
11316        public LayoutParams(LayoutParams source) {
11317            super((ViewGroup.LayoutParams) source);
11318        }
11319
11320        /**
11321         * Returns true if the view this LayoutParams is attached to needs to have its content
11322         * updated from the corresponding adapter.
11323         *
11324         * @return true if the view should have its content updated
11325         */
11326        public boolean viewNeedsUpdate() {
11327            return mViewHolder.needsUpdate();
11328        }
11329
11330        /**
11331         * Returns true if the view this LayoutParams is attached to is now representing
11332         * potentially invalid data. A LayoutManager should scrap/recycle it.
11333         *
11334         * @return true if the view is invalid
11335         */
11336        public boolean isViewInvalid() {
11337            return mViewHolder.isInvalid();
11338        }
11339
11340        /**
11341         * Returns true if the adapter data item corresponding to the view this LayoutParams
11342         * is attached to has been removed from the data set. A LayoutManager may choose to
11343         * treat it differently in order to animate its outgoing or disappearing state.
11344         *
11345         * @return true if the item the view corresponds to was removed from the data set
11346         */
11347        public boolean isItemRemoved() {
11348            return mViewHolder.isRemoved();
11349        }
11350
11351        /**
11352         * Returns true if the adapter data item corresponding to the view this LayoutParams
11353         * is attached to has been changed in the data set. A LayoutManager may choose to
11354         * treat it differently in order to animate its changing state.
11355         *
11356         * @return true if the item the view corresponds to was changed in the data set
11357         */
11358        public boolean isItemChanged() {
11359            return mViewHolder.isUpdated();
11360        }
11361
11362        /**
11363         * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
11364         */
11365        @Deprecated
11366        public int getViewPosition() {
11367            return mViewHolder.getPosition();
11368        }
11369
11370        /**
11371         * Returns the adapter position that the view this LayoutParams is attached to corresponds
11372         * to as of latest layout calculation.
11373         *
11374         * @return the adapter position this view as of latest layout pass
11375         */
11376        public int getViewLayoutPosition() {
11377            return mViewHolder.getLayoutPosition();
11378        }
11379
11380        /**
11381         * Returns the up-to-date adapter position that the view this LayoutParams is attached to
11382         * corresponds to.
11383         *
11384         * @return the up-to-date adapter position this view. It may return
11385         * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
11386         * its up-to-date position cannot be calculated.
11387         */
11388        public int getViewAdapterPosition() {
11389            return mViewHolder.getAdapterPosition();
11390        }
11391    }
11392
11393    /**
11394     * Observer base class for watching changes to an {@link Adapter}.
11395     * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
11396     */
11397    public abstract static class AdapterDataObserver {
11398        public void onChanged() {
11399            // Do nothing
11400        }
11401
11402        public void onItemRangeChanged(int positionStart, int itemCount) {
11403            // do nothing
11404        }
11405
11406        public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
11407            // fallback to onItemRangeChanged(positionStart, itemCount) if app
11408            // does not override this method.
11409            onItemRangeChanged(positionStart, itemCount);
11410        }
11411
11412        public void onItemRangeInserted(int positionStart, int itemCount) {
11413            // do nothing
11414        }
11415
11416        public void onItemRangeRemoved(int positionStart, int itemCount) {
11417            // do nothing
11418        }
11419
11420        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
11421            // do nothing
11422        }
11423    }
11424
11425    /**
11426     * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
11427     * provides methods to trigger a programmatic scroll.</p>
11428     *
11429     * @see LinearSmoothScroller
11430     */
11431    public abstract static class SmoothScroller {
11432
11433        private int mTargetPosition = RecyclerView.NO_POSITION;
11434
11435        private RecyclerView mRecyclerView;
11436
11437        private LayoutManager mLayoutManager;
11438
11439        private boolean mPendingInitialRun;
11440
11441        private boolean mRunning;
11442
11443        private View mTargetView;
11444
11445        private final Action mRecyclingAction;
11446
11447        public SmoothScroller() {
11448            mRecyclingAction = new Action(0, 0);
11449        }
11450
11451        /**
11452         * Starts a smooth scroll for the given target position.
11453         * <p>In each animation step, {@link RecyclerView} will check
11454         * for the target view and call either
11455         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
11456         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
11457         * SmoothScroller is stopped.</p>
11458         *
11459         * <p>Note that if RecyclerView finds the target view, it will automatically stop the
11460         * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
11461         * stop calling SmoothScroller in each animation step.</p>
11462         */
11463        void start(RecyclerView recyclerView, LayoutManager layoutManager) {
11464            mRecyclerView = recyclerView;
11465            mLayoutManager = layoutManager;
11466            if (mTargetPosition == RecyclerView.NO_POSITION) {
11467                throw new IllegalArgumentException("Invalid target position");
11468            }
11469            mRecyclerView.mState.mTargetPosition = mTargetPosition;
11470            mRunning = true;
11471            mPendingInitialRun = true;
11472            mTargetView = findViewByPosition(getTargetPosition());
11473            onStart();
11474            mRecyclerView.mViewFlinger.postOnAnimation();
11475        }
11476
11477        public void setTargetPosition(int targetPosition) {
11478            mTargetPosition = targetPosition;
11479        }
11480
11481        /**
11482         * Compute the scroll vector for a given target position.
11483         * <p>
11484         * This method can return null if the layout manager cannot calculate a scroll vector
11485         * for the given position (e.g. it has no current scroll position).
11486         *
11487         * @param targetPosition the position to which the scroller is scrolling
11488         *
11489         * @return the scroll vector for a given target position
11490         */
11491        @Nullable
11492        public PointF computeScrollVectorForPosition(int targetPosition) {
11493            LayoutManager layoutManager = getLayoutManager();
11494            if (layoutManager instanceof ScrollVectorProvider) {
11495                return ((ScrollVectorProvider) layoutManager)
11496                        .computeScrollVectorForPosition(targetPosition);
11497            }
11498            Log.w(TAG, "You should override computeScrollVectorForPosition when the LayoutManager"
11499                    + " does not implement " + ScrollVectorProvider.class.getCanonicalName());
11500            return null;
11501        }
11502
11503        /**
11504         * @return The LayoutManager to which this SmoothScroller is attached. Will return
11505         * <code>null</code> after the SmoothScroller is stopped.
11506         */
11507        @Nullable
11508        public LayoutManager getLayoutManager() {
11509            return mLayoutManager;
11510        }
11511
11512        /**
11513         * Stops running the SmoothScroller in each animation callback. Note that this does not
11514         * cancel any existing {@link Action} updated by
11515         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
11516         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
11517         */
11518        protected final void stop() {
11519            if (!mRunning) {
11520                return;
11521            }
11522            onStop();
11523            mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
11524            mTargetView = null;
11525            mTargetPosition = RecyclerView.NO_POSITION;
11526            mPendingInitialRun = false;
11527            mRunning = false;
11528            // trigger a cleanup
11529            mLayoutManager.onSmoothScrollerStopped(this);
11530            // clear references to avoid any potential leak by a custom smooth scroller
11531            mLayoutManager = null;
11532            mRecyclerView = null;
11533        }
11534
11535        /**
11536         * Returns true if SmoothScroller has been started but has not received the first
11537         * animation
11538         * callback yet.
11539         *
11540         * @return True if this SmoothScroller is waiting to start
11541         */
11542        public boolean isPendingInitialRun() {
11543            return mPendingInitialRun;
11544        }
11545
11546
11547        /**
11548         * @return True if SmoothScroller is currently active
11549         */
11550        public boolean isRunning() {
11551            return mRunning;
11552        }
11553
11554        /**
11555         * Returns the adapter position of the target item
11556         *
11557         * @return Adapter position of the target item or
11558         * {@link RecyclerView#NO_POSITION} if no target view is set.
11559         */
11560        public int getTargetPosition() {
11561            return mTargetPosition;
11562        }
11563
11564        private void onAnimation(int dx, int dy) {
11565            // TODO(b/72745539): If mRunning is false, we call stop, which is a no op if mRunning
11566            // is false. Also, if recyclerView is null, we call stop, and stop assumes recyclerView
11567            // is not null (as does the code following this block).  This should be cleaned up.
11568            final RecyclerView recyclerView = mRecyclerView;
11569            if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
11570                stop();
11571            }
11572
11573            // The following if block exists to have the LayoutManager scroll 1 pixel in the correct
11574            // direction in order to cause the LayoutManager to draw two pages worth of views so
11575            // that the target view may be found before scrolling any further.  This is done to
11576            // prevent an initial scroll distance from scrolling past the view, which causes a
11577            // jittery looking animation. (This block also necessarily sets mPendingInitialRun to
11578            // false if it was true).
11579            if (mPendingInitialRun && mTargetView == null && mLayoutManager != null) {
11580                PointF pointF = computeScrollVectorForPosition(mTargetPosition);
11581                if (pointF != null && (pointF.x != 0 || pointF.y != 0)) {
11582                    recyclerView.scrollStep(
11583                            (int) Math.signum(pointF.x),
11584                            (int) Math.signum(pointF.y),
11585                            null);
11586                }
11587            }
11588
11589            mPendingInitialRun = false;
11590
11591            if (mTargetView != null) {
11592                // verify target position
11593                if (getChildPosition(mTargetView) == mTargetPosition) {
11594                    onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
11595                    mRecyclingAction.runIfNecessary(recyclerView);
11596                    stop();
11597                } else {
11598                    Log.e(TAG, "Passed over target position while smooth scrolling.");
11599                    mTargetView = null;
11600                }
11601            }
11602            if (mRunning) {
11603                onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
11604                boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
11605                mRecyclingAction.runIfNecessary(recyclerView);
11606                if (hadJumpTarget) {
11607                    // It is not stopped so needs to be restarted
11608                    if (mRunning) {
11609                        mPendingInitialRun = true;
11610                        recyclerView.mViewFlinger.postOnAnimation();
11611                    } else {
11612                        // TODO(b/72745539): stop() is a no-op if mRunning is false, so this can be
11613                        // removed.
11614                        stop(); // done
11615                    }
11616                }
11617            }
11618        }
11619
11620        /**
11621         * @see RecyclerView#getChildLayoutPosition(android.view.View)
11622         */
11623        public int getChildPosition(View view) {
11624            return mRecyclerView.getChildLayoutPosition(view);
11625        }
11626
11627        /**
11628         * @see RecyclerView.LayoutManager#getChildCount()
11629         */
11630        public int getChildCount() {
11631            return mRecyclerView.mLayout.getChildCount();
11632        }
11633
11634        /**
11635         * @see RecyclerView.LayoutManager#findViewByPosition(int)
11636         */
11637        public View findViewByPosition(int position) {
11638            return mRecyclerView.mLayout.findViewByPosition(position);
11639        }
11640
11641        /**
11642         * @see RecyclerView#scrollToPosition(int)
11643         * @deprecated Use {@link Action#jumpTo(int)}.
11644         */
11645        @Deprecated
11646        public void instantScrollToPosition(int position) {
11647            mRecyclerView.scrollToPosition(position);
11648        }
11649
11650        protected void onChildAttachedToWindow(View child) {
11651            if (getChildPosition(child) == getTargetPosition()) {
11652                mTargetView = child;
11653                if (DEBUG) {
11654                    Log.d(TAG, "smooth scroll target view has been attached");
11655                }
11656            }
11657        }
11658
11659        /**
11660         * Normalizes the vector.
11661         * @param scrollVector The vector that points to the target scroll position
11662         */
11663        protected void normalize(@NonNull PointF scrollVector) {
11664            final float magnitude = (float) Math.sqrt(scrollVector.x * scrollVector.x
11665                    + scrollVector.y * scrollVector.y);
11666            scrollVector.x /= magnitude;
11667            scrollVector.y /= magnitude;
11668        }
11669
11670        /**
11671         * Called when smooth scroll is started. This might be a good time to do setup.
11672         */
11673        protected abstract void onStart();
11674
11675        /**
11676         * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
11677         * @see #stop()
11678         */
11679        protected abstract void onStop();
11680
11681        /**
11682         * <p>RecyclerView will call this method each time it scrolls until it can find the target
11683         * position in the layout.</p>
11684         * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
11685         * provided {@link Action} to define the next scroll.</p>
11686         *
11687         * @param dx        Last scroll amount horizontally
11688         * @param dy        Last scroll amount vertically
11689         * @param state     Transient state of RecyclerView
11690         * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
11691         *                  update this object.
11692         */
11693        protected abstract void onSeekTargetStep(@Px int dx, @Px int dy, @NonNull State state,
11694                @NonNull Action action);
11695
11696        /**
11697         * Called when the target position is laid out. This is the last callback SmoothScroller
11698         * will receive and it should update the provided {@link Action} to define the scroll
11699         * details towards the target view.
11700         * @param targetView    The view element which render the target position.
11701         * @param state         Transient state of RecyclerView
11702         * @param action        Action instance that you should update to define final scroll action
11703         *                      towards the targetView
11704         */
11705        protected abstract void onTargetFound(@NonNull View targetView, @NonNull State state,
11706                @NonNull Action action);
11707
11708        /**
11709         * Holds information about a smooth scroll request by a {@link SmoothScroller}.
11710         */
11711        public static class Action {
11712
11713            public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
11714
11715            private int mDx;
11716
11717            private int mDy;
11718
11719            private int mDuration;
11720
11721            private int mJumpToPosition = NO_POSITION;
11722
11723            private Interpolator mInterpolator;
11724
11725            private boolean mChanged = false;
11726
11727            // we track this variable to inform custom implementer if they are updating the action
11728            // in every animation callback
11729            private int mConsecutiveUpdates = 0;
11730
11731            /**
11732             * @param dx Pixels to scroll horizontally
11733             * @param dy Pixels to scroll vertically
11734             */
11735            public Action(@Px int dx, @Px int dy) {
11736                this(dx, dy, UNDEFINED_DURATION, null);
11737            }
11738
11739            /**
11740             * @param dx       Pixels to scroll horizontally
11741             * @param dy       Pixels to scroll vertically
11742             * @param duration Duration of the animation in milliseconds
11743             */
11744            public Action(@Px int dx, @Px int dy, int duration) {
11745                this(dx, dy, duration, null);
11746            }
11747
11748            /**
11749             * @param dx           Pixels to scroll horizontally
11750             * @param dy           Pixels to scroll vertically
11751             * @param duration     Duration of the animation in milliseconds
11752             * @param interpolator Interpolator to be used when calculating scroll position in each
11753             *                     animation step
11754             */
11755            public Action(@Px int dx, @Px int dy, int duration,
11756                    @Nullable Interpolator interpolator) {
11757                mDx = dx;
11758                mDy = dy;
11759                mDuration = duration;
11760                mInterpolator = interpolator;
11761            }
11762
11763            /**
11764             * Instead of specifying pixels to scroll, use the target position to jump using
11765             * {@link RecyclerView#scrollToPosition(int)}.
11766             * <p>
11767             * You may prefer using this method if scroll target is really far away and you prefer
11768             * to jump to a location and smooth scroll afterwards.
11769             * <p>
11770             * Note that calling this method takes priority over other update methods such as
11771             * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
11772             * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
11773             * {@link #jumpTo(int)}, the other changes will not be considered for this animation
11774             * frame.
11775             *
11776             * @param targetPosition The target item position to scroll to using instant scrolling.
11777             */
11778            public void jumpTo(int targetPosition) {
11779                mJumpToPosition = targetPosition;
11780            }
11781
11782            boolean hasJumpTarget() {
11783                return mJumpToPosition >= 0;
11784            }
11785
11786            void runIfNecessary(RecyclerView recyclerView) {
11787                if (mJumpToPosition >= 0) {
11788                    final int position = mJumpToPosition;
11789                    mJumpToPosition = NO_POSITION;
11790                    recyclerView.jumpToPositionForSmoothScroller(position);
11791                    mChanged = false;
11792                    return;
11793                }
11794                if (mChanged) {
11795                    validate();
11796                    if (mInterpolator == null) {
11797                        if (mDuration == UNDEFINED_DURATION) {
11798                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
11799                        } else {
11800                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
11801                        }
11802                    } else {
11803                        recyclerView.mViewFlinger.smoothScrollBy(
11804                                mDx, mDy, mDuration, mInterpolator);
11805                    }
11806                    mConsecutiveUpdates++;
11807                    if (mConsecutiveUpdates > 10) {
11808                        // A new action is being set in every animation step. This looks like a bad
11809                        // implementation. Inform developer.
11810                        Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
11811                                + " you are not changing it unless necessary");
11812                    }
11813                    mChanged = false;
11814                } else {
11815                    mConsecutiveUpdates = 0;
11816                }
11817            }
11818
11819            private void validate() {
11820                if (mInterpolator != null && mDuration < 1) {
11821                    throw new IllegalStateException("If you provide an interpolator, you must"
11822                            + " set a positive duration");
11823                } else if (mDuration < 1) {
11824                    throw new IllegalStateException("Scroll duration must be a positive number");
11825                }
11826            }
11827
11828            @Px
11829            public int getDx() {
11830                return mDx;
11831            }
11832
11833            public void setDx(@Px int dx) {
11834                mChanged = true;
11835                mDx = dx;
11836            }
11837
11838            @Px
11839            public int getDy() {
11840                return mDy;
11841            }
11842
11843            public void setDy(@Px int dy) {
11844                mChanged = true;
11845                mDy = dy;
11846            }
11847
11848            public int getDuration() {
11849                return mDuration;
11850            }
11851
11852            public void setDuration(int duration) {
11853                mChanged = true;
11854                mDuration = duration;
11855            }
11856
11857            @Nullable
11858            public Interpolator getInterpolator() {
11859                return mInterpolator;
11860            }
11861
11862            /**
11863             * Sets the interpolator to calculate scroll steps
11864             * @param interpolator The interpolator to use. If you specify an interpolator, you must
11865             *                     also set the duration.
11866             * @see #setDuration(int)
11867             */
11868            public void setInterpolator(@Nullable Interpolator interpolator) {
11869                mChanged = true;
11870                mInterpolator = interpolator;
11871            }
11872
11873            /**
11874             * Updates the action with given parameters.
11875             * @param dx Pixels to scroll horizontally
11876             * @param dy Pixels to scroll vertically
11877             * @param duration Duration of the animation in milliseconds
11878             * @param interpolator Interpolator to be used when calculating scroll position in each
11879             *                     animation step
11880             */
11881            public void update(@Px int dx, @Px int dy, int duration,
11882                    @Nullable Interpolator interpolator) {
11883                mDx = dx;
11884                mDy = dy;
11885                mDuration = duration;
11886                mInterpolator = interpolator;
11887                mChanged = true;
11888            }
11889        }
11890
11891        /**
11892         * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
11893         * to provide a hint to a {@link SmoothScroller} about the location of the target position.
11894         */
11895        public interface ScrollVectorProvider {
11896            /**
11897             * Should calculate the vector that points to the direction where the target position
11898             * can be found.
11899             * <p>
11900             * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
11901             * the target position.
11902             * <p>
11903             * The magnitude of the vector is not important. It is always normalized before being
11904             * used by the {@link LinearSmoothScroller}.
11905             * <p>
11906             * LayoutManager should not check whether the position exists in the adapter or not.
11907             *
11908             * @param targetPosition the target position to which the returned vector should point
11909             *
11910             * @return the scroll vector for a given position.
11911             */
11912            @Nullable
11913            PointF computeScrollVectorForPosition(int targetPosition);
11914        }
11915    }
11916
11917    static class AdapterDataObservable extends Observable<AdapterDataObserver> {
11918        public boolean hasObservers() {
11919            return !mObservers.isEmpty();
11920        }
11921
11922        public void notifyChanged() {
11923            // since onChanged() is implemented by the app, it could do anything, including
11924            // removing itself from {@link mObservers} - and that could cause problems if
11925            // an iterator is used on the ArrayList {@link mObservers}.
11926            // to avoid such problems, just march thru the list in the reverse order.
11927            for (int i = mObservers.size() - 1; i >= 0; i--) {
11928                mObservers.get(i).onChanged();
11929            }
11930        }
11931
11932        public void notifyItemRangeChanged(int positionStart, int itemCount) {
11933            notifyItemRangeChanged(positionStart, itemCount, null);
11934        }
11935
11936        public void notifyItemRangeChanged(int positionStart, int itemCount,
11937                @Nullable Object payload) {
11938            // since onItemRangeChanged() is implemented by the app, it could do anything, including
11939            // removing itself from {@link mObservers} - and that could cause problems if
11940            // an iterator is used on the ArrayList {@link mObservers}.
11941            // to avoid such problems, just march thru the list in the reverse order.
11942            for (int i = mObservers.size() - 1; i >= 0; i--) {
11943                mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
11944            }
11945        }
11946
11947        public void notifyItemRangeInserted(int positionStart, int itemCount) {
11948            // since onItemRangeInserted() is implemented by the app, it could do anything,
11949            // including removing itself from {@link mObservers} - and that could cause problems if
11950            // an iterator is used on the ArrayList {@link mObservers}.
11951            // to avoid such problems, just march thru the list in the reverse order.
11952            for (int i = mObservers.size() - 1; i >= 0; i--) {
11953                mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
11954            }
11955        }
11956
11957        public void notifyItemRangeRemoved(int positionStart, int itemCount) {
11958            // since onItemRangeRemoved() is implemented by the app, it could do anything, including
11959            // removing itself from {@link mObservers} - and that could cause problems if
11960            // an iterator is used on the ArrayList {@link mObservers}.
11961            // to avoid such problems, just march thru the list in the reverse order.
11962            for (int i = mObservers.size() - 1; i >= 0; i--) {
11963                mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
11964            }
11965        }
11966
11967        public void notifyItemMoved(int fromPosition, int toPosition) {
11968            for (int i = mObservers.size() - 1; i >= 0; i--) {
11969                mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
11970            }
11971        }
11972    }
11973
11974    /**
11975     * This is public so that the CREATOR can be accessed on cold launch.
11976     * @hide
11977     */
11978    @RestrictTo(LIBRARY_GROUP)
11979    public static class SavedState extends AbsSavedState {
11980
11981        Parcelable mLayoutState;
11982
11983        /**
11984         * called by CREATOR
11985         */
11986        SavedState(Parcel in, ClassLoader loader) {
11987            super(in, loader);
11988            mLayoutState = in.readParcelable(
11989                    loader != null ? loader : LayoutManager.class.getClassLoader());
11990        }
11991
11992        /**
11993         * Called by onSaveInstanceState
11994         */
11995        SavedState(Parcelable superState) {
11996            super(superState);
11997        }
11998
11999        @Override
12000        public void writeToParcel(Parcel dest, int flags) {
12001            super.writeToParcel(dest, flags);
12002            dest.writeParcelable(mLayoutState, 0);
12003        }
12004
12005        void copyFrom(SavedState other) {
12006            mLayoutState = other.mLayoutState;
12007        }
12008
12009        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
12010            @Override
12011            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
12012                return new SavedState(in, loader);
12013            }
12014
12015            @Override
12016            public SavedState createFromParcel(Parcel in) {
12017                return new SavedState(in, null);
12018            }
12019
12020            @Override
12021            public SavedState[] newArray(int size) {
12022                return new SavedState[size];
12023            }
12024        };
12025    }
12026
12027    /**
12028     * <p>Contains useful information about the current RecyclerView state like target scroll
12029     * position or view focus. State object can also keep arbitrary data, identified by resource
12030     * ids.</p>
12031     * <p>Often times, RecyclerView components will need to pass information between each other.
12032     * To provide a well defined data bus between components, RecyclerView passes the same State
12033     * object to component callbacks and these components can use it to exchange data.</p>
12034     * <p>If you implement custom components, you can use State's put/get/remove methods to pass
12035     * data between your components without needing to manage their lifecycles.</p>
12036     */
12037    public static class State {
12038        static final int STEP_START = 1;
12039        static final int STEP_LAYOUT = 1 << 1;
12040        static final int STEP_ANIMATIONS = 1 << 2;
12041
12042        void assertLayoutStep(int accepted) {
12043            if ((accepted & mLayoutStep) == 0) {
12044                throw new IllegalStateException("Layout state should be one of "
12045                        + Integer.toBinaryString(accepted) + " but it is "
12046                        + Integer.toBinaryString(mLayoutStep));
12047            }
12048        }
12049
12050
12051        /** Owned by SmoothScroller */
12052        private int mTargetPosition = RecyclerView.NO_POSITION;
12053
12054        private SparseArray<Object> mData;
12055
12056        ////////////////////////////////////////////////////////////////////////////////////////////
12057        // Fields below are carried from one layout pass to the next
12058        ////////////////////////////////////////////////////////////////////////////////////////////
12059
12060        /**
12061         * Number of items adapter had in the previous layout.
12062         */
12063        int mPreviousLayoutItemCount = 0;
12064
12065        /**
12066         * Number of items that were NOT laid out but has been deleted from the adapter after the
12067         * previous layout.
12068         */
12069        int mDeletedInvisibleItemCountSincePreviousLayout = 0;
12070
12071        ////////////////////////////////////////////////////////////////////////////////////////////
12072        // Fields below must be updated or cleared before they are used (generally before a pass)
12073        ////////////////////////////////////////////////////////////////////////////////////////////
12074
12075        @IntDef(flag = true, value = {
12076                STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
12077        })
12078        @Retention(RetentionPolicy.SOURCE)
12079        @interface LayoutState {}
12080
12081        @LayoutState
12082        int mLayoutStep = STEP_START;
12083
12084        /**
12085         * Number of items adapter has.
12086         */
12087        int mItemCount = 0;
12088
12089        boolean mStructureChanged = false;
12090
12091        /**
12092         * True if the associated {@link RecyclerView} is in the pre-layout step where it is having
12093         * its {@link LayoutManager} layout items where they will be at the beginning of a set of
12094         * predictive item animations.
12095         */
12096        boolean mInPreLayout = false;
12097
12098        boolean mTrackOldChangeHolders = false;
12099
12100        boolean mIsMeasuring = false;
12101
12102        ////////////////////////////////////////////////////////////////////////////////////////////
12103        // Fields below are always reset outside of the pass (or passes) that use them
12104        ////////////////////////////////////////////////////////////////////////////////////////////
12105
12106        boolean mRunSimpleAnimations = false;
12107
12108        boolean mRunPredictiveAnimations = false;
12109
12110        /**
12111         * This data is saved before a layout calculation happens. After the layout is finished,
12112         * if the previously focused view has been replaced with another view for the same item, we
12113         * move the focus to the new item automatically.
12114         */
12115        int mFocusedItemPosition;
12116        long mFocusedItemId;
12117        // when a sub child has focus, record its id and see if we can directly request focus on
12118        // that one instead
12119        int mFocusedSubChildId;
12120
12121        int mRemainingScrollHorizontal;
12122        int mRemainingScrollVertical;
12123
12124        ////////////////////////////////////////////////////////////////////////////////////////////
12125
12126        State reset() {
12127            mTargetPosition = RecyclerView.NO_POSITION;
12128            if (mData != null) {
12129                mData.clear();
12130            }
12131            mItemCount = 0;
12132            mStructureChanged = false;
12133            mIsMeasuring = false;
12134            return this;
12135        }
12136
12137        /**
12138         * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
12139         * prior to any layout passes.
12140         *
12141         * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
12142         * that Recycler#getViewForPosition() can function safely.</p>
12143         */
12144        void prepareForNestedPrefetch(Adapter adapter) {
12145            mLayoutStep = STEP_START;
12146            mItemCount = adapter.getItemCount();
12147            mInPreLayout = false;
12148            mTrackOldChangeHolders = false;
12149            mIsMeasuring = false;
12150        }
12151
12152        /**
12153         * Returns true if the RecyclerView is currently measuring the layout. This value is
12154         * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
12155         * has non-exact measurement specs.
12156         * <p>
12157         * Note that if the LayoutManager supports predictive animations and it is calculating the
12158         * pre-layout step, this value will be {@code false} even if the RecyclerView is in
12159         * {@code onMeasure} call. This is because pre-layout means the previous state of the
12160         * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
12161         * LayoutManager is always guaranteed to receive another call to
12162         * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
12163         *
12164         * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
12165         */
12166        public boolean isMeasuring() {
12167            return mIsMeasuring;
12168        }
12169
12170        /**
12171         * Returns true if the {@link RecyclerView} is in the pre-layout step where it is having its
12172         * {@link LayoutManager} layout items where they will be at the beginning of a set of
12173         * predictive item animations.
12174         */
12175        public boolean isPreLayout() {
12176            return mInPreLayout;
12177        }
12178
12179        /**
12180         * Returns whether RecyclerView will run predictive animations in this layout pass
12181         * or not.
12182         *
12183         * @return true if RecyclerView is calculating predictive animations to be run at the end
12184         *         of the layout pass.
12185         */
12186        public boolean willRunPredictiveAnimations() {
12187            return mRunPredictiveAnimations;
12188        }
12189
12190        /**
12191         * Returns whether RecyclerView will run simple animations in this layout pass
12192         * or not.
12193         *
12194         * @return true if RecyclerView is calculating simple animations to be run at the end of
12195         *         the layout pass.
12196         */
12197        public boolean willRunSimpleAnimations() {
12198            return mRunSimpleAnimations;
12199        }
12200
12201        /**
12202         * Removes the mapping from the specified id, if there was any.
12203         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
12204         *                   preserve cross functionality and avoid conflicts.
12205         */
12206        public void remove(int resourceId) {
12207            if (mData == null) {
12208                return;
12209            }
12210            mData.remove(resourceId);
12211        }
12212
12213        /**
12214         * Gets the Object mapped from the specified id, or <code>null</code>
12215         * if no such data exists.
12216         *
12217         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
12218         *                   to
12219         *                   preserve cross functionality and avoid conflicts.
12220         */
12221        @SuppressWarnings("TypeParameterUnusedInFormals")
12222        public <T> T get(int resourceId) {
12223            if (mData == null) {
12224                return null;
12225            }
12226            return (T) mData.get(resourceId);
12227        }
12228
12229        /**
12230         * Adds a mapping from the specified id to the specified value, replacing the previous
12231         * mapping from the specified key if there was one.
12232         *
12233         * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
12234         *                   preserve cross functionality and avoid conflicts.
12235         * @param data       The data you want to associate with the resourceId.
12236         */
12237        public void put(int resourceId, Object data) {
12238            if (mData == null) {
12239                mData = new SparseArray<Object>();
12240            }
12241            mData.put(resourceId, data);
12242        }
12243
12244        /**
12245         * If scroll is triggered to make a certain item visible, this value will return the
12246         * adapter index of that item.
12247         * @return Adapter index of the target item or
12248         * {@link RecyclerView#NO_POSITION} if there is no target
12249         * position.
12250         */
12251        public int getTargetScrollPosition() {
12252            return mTargetPosition;
12253        }
12254
12255        /**
12256         * Returns if current scroll has a target position.
12257         * @return true if scroll is being triggered to make a certain position visible
12258         * @see #getTargetScrollPosition()
12259         */
12260        public boolean hasTargetScrollPosition() {
12261            return mTargetPosition != RecyclerView.NO_POSITION;
12262        }
12263
12264        /**
12265         * @return true if the structure of the data set has changed since the last call to
12266         *         onLayoutChildren, false otherwise
12267         */
12268        public boolean didStructureChange() {
12269            return mStructureChanged;
12270        }
12271
12272        /**
12273         * Returns the total number of items that can be laid out. Note that this number is not
12274         * necessarily equal to the number of items in the adapter, so you should always use this
12275         * number for your position calculations and never access the adapter directly.
12276         * <p>
12277         * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
12278         * data changes on existing Views. These calculations are used to decide which animations
12279         * should be run.
12280         * <p>
12281         * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
12282         * present the correct state to LayoutManager in pre-layout pass.
12283         * <p>
12284         * For example, a newly added item is not included in pre-layout item count because
12285         * pre-layout reflects the contents of the adapter before the item is added. Behind the
12286         * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
12287         * LayoutManager does not know about the new item's existence in pre-layout. The item will
12288         * be available in second layout pass and will be included in the item count. Similar
12289         * adjustments are made for moved and removed items as well.
12290         * <p>
12291         * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
12292         *
12293         * @return The number of items currently available
12294         * @see LayoutManager#getItemCount()
12295         */
12296        public int getItemCount() {
12297            return mInPreLayout
12298                    ? (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout)
12299                    : mItemCount;
12300        }
12301
12302        /**
12303         * Returns remaining horizontal scroll distance of an ongoing scroll animation(fling/
12304         * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
12305         * other than {@link #SCROLL_STATE_SETTLING}.
12306         *
12307         * @return Remaining horizontal scroll distance
12308         */
12309        public int getRemainingScrollHorizontal() {
12310            return mRemainingScrollHorizontal;
12311        }
12312
12313        /**
12314         * Returns remaining vertical scroll distance of an ongoing scroll animation(fling/
12315         * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
12316         * other than {@link #SCROLL_STATE_SETTLING}.
12317         *
12318         * @return Remaining vertical scroll distance
12319         */
12320        public int getRemainingScrollVertical() {
12321            return mRemainingScrollVertical;
12322        }
12323
12324        @Override
12325        public String toString() {
12326            return "State{"
12327                    + "mTargetPosition=" + mTargetPosition
12328                    + ", mData=" + mData
12329                    + ", mItemCount=" + mItemCount
12330                    + ", mIsMeasuring=" + mIsMeasuring
12331                    + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
12332                    + ", mDeletedInvisibleItemCountSincePreviousLayout="
12333                    + mDeletedInvisibleItemCountSincePreviousLayout
12334                    + ", mStructureChanged=" + mStructureChanged
12335                    + ", mInPreLayout=" + mInPreLayout
12336                    + ", mRunSimpleAnimations=" + mRunSimpleAnimations
12337                    + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
12338                    + '}';
12339        }
12340    }
12341
12342    /**
12343     * This class defines the behavior of fling if the developer wishes to handle it.
12344     * <p>
12345     * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
12346     *
12347     * @see #setOnFlingListener(OnFlingListener)
12348     */
12349    public abstract static class OnFlingListener {
12350
12351        /**
12352         * Override this to handle a fling given the velocities in both x and y directions.
12353         * Note that this method will only be called if the associated {@link LayoutManager}
12354         * supports scrolling and the fling is not handled by nested scrolls first.
12355         *
12356         * @param velocityX the fling velocity on the X axis
12357         * @param velocityY the fling velocity on the Y axis
12358         *
12359         * @return true if the fling was handled, false otherwise.
12360         */
12361        public abstract boolean onFling(int velocityX, int velocityY);
12362    }
12363
12364    /**
12365     * Internal listener that manages items after animations finish. This is how items are
12366     * retained (not recycled) during animations, but allowed to be recycled afterwards.
12367     * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
12368     * method on the animator's listener when it is done animating any item.
12369     */
12370    private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
12371
12372        ItemAnimatorRestoreListener() {
12373        }
12374
12375        @Override
12376        public void onAnimationFinished(ViewHolder item) {
12377            item.setIsRecyclable(true);
12378            if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
12379                item.mShadowedHolder = null;
12380            }
12381            // always null this because an OldViewHolder can never become NewViewHolder w/o being
12382            // recycled.
12383            item.mShadowingHolder = null;
12384            if (!item.shouldBeKeptAsChild()) {
12385                if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
12386                    removeDetachedView(item.itemView, false);
12387                }
12388            }
12389        }
12390    }
12391
12392    /**
12393     * This class defines the animations that take place on items as changes are made
12394     * to the adapter.
12395     *
12396     * Subclasses of ItemAnimator can be used to implement custom animations for actions on
12397     * ViewHolder items. The RecyclerView will manage retaining these items while they
12398     * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
12399     * when a ViewHolder's animation is finished. In other words, there must be a matching
12400     * {@link #dispatchAnimationFinished(ViewHolder)} call for each
12401     * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
12402     * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12403     * animateChange()}
12404     * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
12405     * and
12406     * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12407     * animateDisappearance()} call.
12408     *
12409     * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
12410     *
12411     * @see #setItemAnimator(ItemAnimator)
12412     */
12413    @SuppressWarnings("UnusedParameters")
12414    public abstract static class ItemAnimator {
12415
12416        /**
12417         * The Item represented by this ViewHolder is updated.
12418         * <p>
12419         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12420         */
12421        public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
12422
12423        /**
12424         * The Item represented by this ViewHolder is removed from the adapter.
12425         * <p>
12426         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12427         */
12428        public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
12429
12430        /**
12431         * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
12432         * represented by this ViewHolder is invalid.
12433         * <p>
12434         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12435         */
12436        public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
12437
12438        /**
12439         * The position of the Item represented by this ViewHolder has been changed. This flag is
12440         * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
12441         * any adapter change that may have a side effect on this item. (e.g. The item before this
12442         * one has been removed from the Adapter).
12443         * <p>
12444         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12445         */
12446        public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
12447
12448        /**
12449         * This ViewHolder was not laid out but has been added to the layout in pre-layout state
12450         * by the {@link LayoutManager}. This means that the item was already in the Adapter but
12451         * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
12452         * to add new items in pre-layout to specify their virtual location when they are invisible
12453         * (e.g. to specify the item should <i>animate in</i> from below the visible area).
12454         * <p>
12455         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12456         */
12457        public static final int FLAG_APPEARED_IN_PRE_LAYOUT =
12458                ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
12459
12460        /**
12461         * The set of flags that might be passed to
12462         * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12463         */
12464        @IntDef(flag = true, value = {
12465                FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
12466                FLAG_APPEARED_IN_PRE_LAYOUT
12467        })
12468        @Retention(RetentionPolicy.SOURCE)
12469        public @interface AdapterChanges {}
12470        private ItemAnimatorListener mListener = null;
12471        private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
12472                new ArrayList<ItemAnimatorFinishedListener>();
12473
12474        private long mAddDuration = 120;
12475        private long mRemoveDuration = 120;
12476        private long mMoveDuration = 250;
12477        private long mChangeDuration = 250;
12478
12479        /**
12480         * Gets the current duration for which all move animations will run.
12481         *
12482         * @return The current move duration
12483         */
12484        public long getMoveDuration() {
12485            return mMoveDuration;
12486        }
12487
12488        /**
12489         * Sets the duration for which all move animations will run.
12490         *
12491         * @param moveDuration The move duration
12492         */
12493        public void setMoveDuration(long moveDuration) {
12494            mMoveDuration = moveDuration;
12495        }
12496
12497        /**
12498         * Gets the current duration for which all add animations will run.
12499         *
12500         * @return The current add duration
12501         */
12502        public long getAddDuration() {
12503            return mAddDuration;
12504        }
12505
12506        /**
12507         * Sets the duration for which all add animations will run.
12508         *
12509         * @param addDuration The add duration
12510         */
12511        public void setAddDuration(long addDuration) {
12512            mAddDuration = addDuration;
12513        }
12514
12515        /**
12516         * Gets the current duration for which all remove animations will run.
12517         *
12518         * @return The current remove duration
12519         */
12520        public long getRemoveDuration() {
12521            return mRemoveDuration;
12522        }
12523
12524        /**
12525         * Sets the duration for which all remove animations will run.
12526         *
12527         * @param removeDuration The remove duration
12528         */
12529        public void setRemoveDuration(long removeDuration) {
12530            mRemoveDuration = removeDuration;
12531        }
12532
12533        /**
12534         * Gets the current duration for which all change animations will run.
12535         *
12536         * @return The current change duration
12537         */
12538        public long getChangeDuration() {
12539            return mChangeDuration;
12540        }
12541
12542        /**
12543         * Sets the duration for which all change animations will run.
12544         *
12545         * @param changeDuration The change duration
12546         */
12547        public void setChangeDuration(long changeDuration) {
12548            mChangeDuration = changeDuration;
12549        }
12550
12551        /**
12552         * Internal only:
12553         * Sets the listener that must be called when the animator is finished
12554         * animating the item (or immediately if no animation happens). This is set
12555         * internally and is not intended to be set by external code.
12556         *
12557         * @param listener The listener that must be called.
12558         */
12559        void setListener(ItemAnimatorListener listener) {
12560            mListener = listener;
12561        }
12562
12563        /**
12564         * Called by the RecyclerView before the layout begins. Item animator should record
12565         * necessary information about the View before it is potentially rebound, moved or removed.
12566         * <p>
12567         * The data returned from this method will be passed to the related <code>animate**</code>
12568         * methods.
12569         * <p>
12570         * Note that this method may be called after pre-layout phase if LayoutManager adds new
12571         * Views to the layout in pre-layout pass.
12572         * <p>
12573         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
12574         * the View and the adapter change flags.
12575         *
12576         * @param state       The current State of RecyclerView which includes some useful data
12577         *                    about the layout that will be calculated.
12578         * @param viewHolder  The ViewHolder whose information should be recorded.
12579         * @param changeFlags Additional information about what changes happened in the Adapter
12580         *                    about the Item represented by this ViewHolder. For instance, if
12581         *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
12582         * @param payloads    The payload list that was previously passed to
12583         *                    {@link Adapter#notifyItemChanged(int, Object)} or
12584         *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
12585         *
12586         * @return An ItemHolderInfo instance that preserves necessary information about the
12587         * ViewHolder. This object will be passed back to related <code>animate**</code> methods
12588         * after layout is complete.
12589         *
12590         * @see #recordPostLayoutInformation(State, ViewHolder)
12591         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12592         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12593         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12594         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12595         */
12596        public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
12597                @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
12598                @NonNull List<Object> payloads) {
12599            return obtainHolderInfo().setFrom(viewHolder);
12600        }
12601
12602        /**
12603         * Called by the RecyclerView after the layout is complete. Item animator should record
12604         * necessary information about the View's final state.
12605         * <p>
12606         * The data returned from this method will be passed to the related <code>animate**</code>
12607         * methods.
12608         * <p>
12609         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
12610         * the View.
12611         *
12612         * @param state      The current State of RecyclerView which includes some useful data about
12613         *                   the layout that will be calculated.
12614         * @param viewHolder The ViewHolder whose information should be recorded.
12615         *
12616         * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
12617         * This object will be passed back to related <code>animate**</code> methods when
12618         * RecyclerView decides how items should be animated.
12619         *
12620         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12621         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12622         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12623         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12624         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12625         */
12626        public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
12627                @NonNull ViewHolder viewHolder) {
12628            return obtainHolderInfo().setFrom(viewHolder);
12629        }
12630
12631        /**
12632         * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
12633         * <p>
12634         * This means that the View was a child of the LayoutManager when layout started but has
12635         * been removed by the LayoutManager. It might have been removed from the adapter or simply
12636         * become invisible due to other factors. You can distinguish these two cases by checking
12637         * the change flags that were passed to
12638         * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12639         * <p>
12640         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
12641         * animation callback method which will be called by the RecyclerView depends on the
12642         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
12643         * LayoutManager's decision whether to layout the changed version of a disappearing
12644         * ViewHolder or not. RecyclerView will call
12645         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12646         * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
12647         * returns {@code false} from
12648         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
12649         * LayoutManager lays out a new disappearing view that holds the updated information.
12650         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
12651         * <p>
12652         * If LayoutManager supports predictive animations, it might provide a target disappear
12653         * location for the View by laying it out in that location. When that happens,
12654         * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
12655         * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
12656         * <p>
12657         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12658         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12659         * decides not to animate the view).
12660         *
12661         * @param viewHolder    The ViewHolder which should be animated
12662         * @param preLayoutInfo The information that was returned from
12663         *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12664         * @param postLayoutInfo The information that was returned from
12665         *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
12666         *                       null if the LayoutManager did not layout the item.
12667         *
12668         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12669         * false otherwise.
12670         */
12671        public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
12672                @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
12673
12674        /**
12675         * Called by the RecyclerView when a ViewHolder is added to the layout.
12676         * <p>
12677         * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
12678         * but has  been added by the LayoutManager. It might be newly added to the adapter or
12679         * simply become visible due to other factors.
12680         * <p>
12681         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12682         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12683         * decides not to animate the view).
12684         *
12685         * @param viewHolder     The ViewHolder which should be animated
12686         * @param preLayoutInfo  The information that was returned from
12687         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12688         *                       Might be null if Item was just added to the adapter or
12689         *                       LayoutManager does not support predictive animations or it could
12690         *                       not predict that this ViewHolder will become visible.
12691         * @param postLayoutInfo The information that was returned from {@link
12692         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12693         *
12694         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12695         * false otherwise.
12696         */
12697        public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
12698                @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12699
12700        /**
12701         * Called by the RecyclerView when a ViewHolder is present in both before and after the
12702         * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
12703         * for it or a {@link Adapter#notifyDataSetChanged()} call.
12704         * <p>
12705         * This ViewHolder still represents the same data that it was representing when the layout
12706         * started but its position / size may be changed by the LayoutManager.
12707         * <p>
12708         * If the Item's layout position didn't change, RecyclerView still calls this method because
12709         * it does not track this information (or does not necessarily know that an animation is
12710         * not required). Your ItemAnimator should handle this case and if there is nothing to
12711         * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
12712         * <code>false</code>.
12713         * <p>
12714         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12715         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12716         * decides not to animate the view).
12717         *
12718         * @param viewHolder     The ViewHolder which should be animated
12719         * @param preLayoutInfo  The information that was returned from
12720         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12721         * @param postLayoutInfo The information that was returned from {@link
12722         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12723         *
12724         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12725         * false otherwise.
12726         */
12727        public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
12728                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12729
12730        /**
12731         * Called by the RecyclerView when an adapter item is present both before and after the
12732         * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
12733         * for it. This method may also be called when
12734         * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
12735         * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
12736         * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
12737         * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
12738         * called for the new ViewHolder and the old one will be recycled.
12739         * <p>
12740         * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
12741         * a good possibility that item contents didn't really change but it is rebound from the
12742         * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
12743         * screen didn't change and your animator should handle this case as well and avoid creating
12744         * unnecessary animations.
12745         * <p>
12746         * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
12747         * previous presentation of the item as-is and supply a new ViewHolder for the updated
12748         * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
12749         * This is useful if you don't know the contents of the Item and would like
12750         * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
12751         * <p>
12752         * When you are writing a custom item animator for your layout, it might be more performant
12753         * and elegant to re-use the same ViewHolder and animate the content changes manually.
12754         * <p>
12755         * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
12756         * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
12757         * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
12758         * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
12759         * which represent the same Item. In that case, only the new ViewHolder is visible
12760         * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
12761         * <p>
12762         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
12763         * ViewHolder when their animation is complete
12764         * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
12765         * animate the view).
12766         * <p>
12767         *  If oldHolder and newHolder are the same instance, you should call
12768         * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
12769         * <p>
12770         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
12771         * animation callback method which will be called by the RecyclerView depends on the
12772         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
12773         * LayoutManager's decision whether to layout the changed version of a disappearing
12774         * ViewHolder or not. RecyclerView will call
12775         * {@code animateChange} instead of
12776         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12777         * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
12778         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
12779         * LayoutManager lays out a new disappearing view that holds the updated information.
12780         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
12781         *
12782         * @param oldHolder     The ViewHolder before the layout is started, might be the same
12783         *                      instance with newHolder.
12784         * @param newHolder     The ViewHolder after the layout is finished, might be the same
12785         *                      instance with oldHolder.
12786         * @param preLayoutInfo  The information that was returned from
12787         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12788         * @param postLayoutInfo The information that was returned from {@link
12789         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12790         *
12791         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12792         * false otherwise.
12793         */
12794        public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
12795                @NonNull ViewHolder newHolder,
12796                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12797
12798        @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
12799            int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
12800            if (viewHolder.isInvalid()) {
12801                return FLAG_INVALIDATED;
12802            }
12803            if ((flags & FLAG_INVALIDATED) == 0) {
12804                final int oldPos = viewHolder.getOldPosition();
12805                final int pos = viewHolder.getAdapterPosition();
12806                if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
12807                    flags |= FLAG_MOVED;
12808                }
12809            }
12810            return flags;
12811        }
12812
12813        /**
12814         * Called when there are pending animations waiting to be started. This state
12815         * is governed by the return values from
12816         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12817         * animateAppearance()},
12818         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12819         * animateChange()}
12820         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12821         * animatePersistence()}, and
12822         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12823         * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
12824         * called later to start the associated animations. runPendingAnimations() will be scheduled
12825         * to be run on the next frame.
12826         */
12827        public abstract void runPendingAnimations();
12828
12829        /**
12830         * Method called when an animation on a view should be ended immediately.
12831         * This could happen when other events, like scrolling, occur, so that
12832         * animating views can be quickly put into their proper end locations.
12833         * Implementations should ensure that any animations running on the item
12834         * are canceled and affected properties are set to their end values.
12835         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
12836         * animation since the animations are effectively done when this method is called.
12837         *
12838         * @param item The item for which an animation should be stopped.
12839         */
12840        public abstract void endAnimation(@NonNull ViewHolder item);
12841
12842        /**
12843         * Method called when all item animations should be ended immediately.
12844         * This could happen when other events, like scrolling, occur, so that
12845         * animating views can be quickly put into their proper end locations.
12846         * Implementations should ensure that any animations running on any items
12847         * are canceled and affected properties are set to their end values.
12848         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
12849         * animation since the animations are effectively done when this method is called.
12850         */
12851        public abstract void endAnimations();
12852
12853        /**
12854         * Method which returns whether there are any item animations currently running.
12855         * This method can be used to determine whether to delay other actions until
12856         * animations end.
12857         *
12858         * @return true if there are any item animations currently running, false otherwise.
12859         */
12860        public abstract boolean isRunning();
12861
12862        /**
12863         * Method to be called by subclasses when an animation is finished.
12864         * <p>
12865         * For each call RecyclerView makes to
12866         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12867         * animateAppearance()},
12868         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12869         * animatePersistence()}, or
12870         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12871         * animateDisappearance()}, there
12872         * should
12873         * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
12874         * <p>
12875         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12876         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
12877         * and <code>newHolder</code>  (if they are not the same instance).
12878         *
12879         * @param viewHolder The ViewHolder whose animation is finished.
12880         * @see #onAnimationFinished(ViewHolder)
12881         */
12882        public final void dispatchAnimationFinished(@NonNull ViewHolder viewHolder) {
12883            onAnimationFinished(viewHolder);
12884            if (mListener != null) {
12885                mListener.onAnimationFinished(viewHolder);
12886            }
12887        }
12888
12889        /**
12890         * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
12891         * ItemAnimator.
12892         *
12893         * @param viewHolder The ViewHolder whose animation is finished. There might still be other
12894         *                   animations running on this ViewHolder.
12895         * @see #dispatchAnimationFinished(ViewHolder)
12896         */
12897        public void onAnimationFinished(@NonNull ViewHolder viewHolder) {
12898        }
12899
12900        /**
12901         * Method to be called by subclasses when an animation is started.
12902         * <p>
12903         * For each call RecyclerView makes to
12904         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12905         * animateAppearance()},
12906         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12907         * animatePersistence()}, or
12908         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12909         * animateDisappearance()}, there should be a matching
12910         * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
12911         * <p>
12912         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12913         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
12914         * and <code>newHolder</code> (if they are not the same instance).
12915         * <p>
12916         * If your ItemAnimator decides not to animate a ViewHolder, it should call
12917         * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
12918         * {@link #dispatchAnimationStarted(ViewHolder)}.
12919         *
12920         * @param viewHolder The ViewHolder whose animation is starting.
12921         * @see #onAnimationStarted(ViewHolder)
12922         */
12923        public final void dispatchAnimationStarted(@NonNull ViewHolder viewHolder) {
12924            onAnimationStarted(viewHolder);
12925        }
12926
12927        /**
12928         * Called when a new animation is started on the given ViewHolder.
12929         *
12930         * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
12931         *                   might already be animating and this might be another animation.
12932         * @see #dispatchAnimationStarted(ViewHolder)
12933         */
12934        public void onAnimationStarted(@NonNull ViewHolder viewHolder) {
12935
12936        }
12937
12938        /**
12939         * Like {@link #isRunning()}, this method returns whether there are any item
12940         * animations currently running. Additionally, the listener passed in will be called
12941         * when there are no item animations running, either immediately (before the method
12942         * returns) if no animations are currently running, or when the currently running
12943         * animations are {@link #dispatchAnimationsFinished() finished}.
12944         *
12945         * <p>Note that the listener is transient - it is either called immediately and not
12946         * stored at all, or stored only until it is called when running animations
12947         * are finished sometime later.</p>
12948         *
12949         * @param listener A listener to be called immediately if no animations are running
12950         * or later when currently-running animations have finished. A null listener is
12951         * equivalent to calling {@link #isRunning()}.
12952         * @return true if there are any item animations currently running, false otherwise.
12953         */
12954        public final boolean isRunning(@Nullable ItemAnimatorFinishedListener listener) {
12955            boolean running = isRunning();
12956            if (listener != null) {
12957                if (!running) {
12958                    listener.onAnimationsFinished();
12959                } else {
12960                    mFinishedListeners.add(listener);
12961                }
12962            }
12963            return running;
12964        }
12965
12966        /**
12967         * When an item is changed, ItemAnimator can decide whether it wants to re-use
12968         * the same ViewHolder for animations or RecyclerView should create a copy of the
12969         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12970         * <p>
12971         * Note that this method will only be called if the {@link ViewHolder} still has the same
12972         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12973         * both {@link ViewHolder}s in the
12974         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12975         * <p>
12976         * If your application is using change payloads, you can override
12977         * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
12978         *
12979         * @param viewHolder The ViewHolder which represents the changed item's old content.
12980         *
12981         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
12982         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
12983         *         ItemAnimator to animate. Default implementation returns <code>true</code>.
12984         *
12985         * @see #canReuseUpdatedViewHolder(ViewHolder, List)
12986         */
12987        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
12988            return true;
12989        }
12990
12991        /**
12992         * When an item is changed, ItemAnimator can decide whether it wants to re-use
12993         * the same ViewHolder for animations or RecyclerView should create a copy of the
12994         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12995         * <p>
12996         * Note that this method will only be called if the {@link ViewHolder} still has the same
12997         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12998         * both {@link ViewHolder}s in the
12999         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
13000         *
13001         * @param viewHolder The ViewHolder which represents the changed item's old content.
13002         * @param payloads A non-null list of merged payloads that were sent with change
13003         *                 notifications. Can be empty if the adapter is invalidated via
13004         *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
13005         *                 payloads will be passed into
13006         *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
13007         *                 method <b>if</b> this method returns <code>true</code>.
13008         *
13009         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
13010         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
13011         *         ItemAnimator to animate. Default implementation calls
13012         *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
13013         *
13014         * @see #canReuseUpdatedViewHolder(ViewHolder)
13015         */
13016        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
13017                @NonNull List<Object> payloads) {
13018            return canReuseUpdatedViewHolder(viewHolder);
13019        }
13020
13021        /**
13022         * This method should be called by ItemAnimator implementations to notify
13023         * any listeners that all pending and active item animations are finished.
13024         */
13025        public final void dispatchAnimationsFinished() {
13026            final int count = mFinishedListeners.size();
13027            for (int i = 0; i < count; ++i) {
13028                mFinishedListeners.get(i).onAnimationsFinished();
13029            }
13030            mFinishedListeners.clear();
13031        }
13032
13033        /**
13034         * Returns a new {@link ItemHolderInfo} which will be used to store information about the
13035         * ViewHolder. This information will later be passed into <code>animate**</code> methods.
13036         * <p>
13037         * You can override this method if you want to extend {@link ItemHolderInfo} and provide
13038         * your own instances.
13039         *
13040         * @return A new {@link ItemHolderInfo}.
13041         */
13042        @NonNull
13043        public ItemHolderInfo obtainHolderInfo() {
13044            return new ItemHolderInfo();
13045        }
13046
13047        /**
13048         * The interface to be implemented by listeners to animation events from this
13049         * ItemAnimator. This is used internally and is not intended for developers to
13050         * create directly.
13051         */
13052        interface ItemAnimatorListener {
13053            void onAnimationFinished(@NonNull ViewHolder item);
13054        }
13055
13056        /**
13057         * This interface is used to inform listeners when all pending or running animations
13058         * in an ItemAnimator are finished. This can be used, for example, to delay an action
13059         * in a data set until currently-running animations are complete.
13060         *
13061         * @see #isRunning(ItemAnimatorFinishedListener)
13062         */
13063        public interface ItemAnimatorFinishedListener {
13064            /**
13065             * Notifies when all pending or running animations in an ItemAnimator are finished.
13066             */
13067            void onAnimationsFinished();
13068        }
13069
13070        /**
13071         * A simple data structure that holds information about an item's bounds.
13072         * This information is used in calculating item animations. Default implementation of
13073         * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
13074         * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
13075         * structure. You can extend this class if you would like to keep more information about
13076         * the Views.
13077         * <p>
13078         * If you want to provide your own implementation but still use `super` methods to record
13079         * basic information, you can override {@link #obtainHolderInfo()} to provide your own
13080         * instances.
13081         */
13082        public static class ItemHolderInfo {
13083
13084            /**
13085             * The left edge of the View (excluding decorations)
13086             */
13087            public int left;
13088
13089            /**
13090             * The top edge of the View (excluding decorations)
13091             */
13092            public int top;
13093
13094            /**
13095             * The right edge of the View (excluding decorations)
13096             */
13097            public int right;
13098
13099            /**
13100             * The bottom edge of the View (excluding decorations)
13101             */
13102            public int bottom;
13103
13104            /**
13105             * The change flags that were passed to
13106             * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
13107             */
13108            @AdapterChanges
13109            public int changeFlags;
13110
13111            public ItemHolderInfo() {
13112            }
13113
13114            /**
13115             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
13116             * the given ViewHolder. Clears all {@link #changeFlags}.
13117             *
13118             * @param holder The ViewHolder whose bounds should be copied.
13119             * @return This {@link ItemHolderInfo}
13120             */
13121            @NonNull
13122            public ItemHolderInfo setFrom(@NonNull RecyclerView.ViewHolder holder) {
13123                return setFrom(holder, 0);
13124            }
13125
13126            /**
13127             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
13128             * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
13129             *
13130             * @param holder The ViewHolder whose bounds should be copied.
13131             * @param flags  The adapter change flags that were passed into
13132             *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
13133             *               List)}.
13134             * @return This {@link ItemHolderInfo}
13135             */
13136            @NonNull
13137            public ItemHolderInfo setFrom(@NonNull RecyclerView.ViewHolder holder,
13138                    @AdapterChanges int flags) {
13139                final View view = holder.itemView;
13140                this.left = view.getLeft();
13141                this.top = view.getTop();
13142                this.right = view.getRight();
13143                this.bottom = view.getBottom();
13144                return this;
13145            }
13146        }
13147    }
13148
13149    @Override
13150    protected int getChildDrawingOrder(int childCount, int i) {
13151        if (mChildDrawingOrderCallback == null) {
13152            return super.getChildDrawingOrder(childCount, i);
13153        } else {
13154            return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
13155        }
13156    }
13157
13158    /**
13159     * A callback interface that can be used to alter the drawing order of RecyclerView children.
13160     * <p>
13161     * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
13162     * that applies to that method also applies to this callback. For example, changing the drawing
13163     * order of two views will not have any effect if their elevation values are different since
13164     * elevation overrides the result of this callback.
13165     */
13166    public interface ChildDrawingOrderCallback {
13167        /**
13168         * Returns the index of the child to draw for this iteration. Override this
13169         * if you want to change the drawing order of children. By default, it
13170         * returns i.
13171         *
13172         * @param i The current iteration.
13173         * @return The index of the child to draw this iteration.
13174         *
13175         * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
13176         */
13177        int onGetChildDrawingOrder(int childCount, int i);
13178    }
13179
13180    private NestedScrollingChildHelper getScrollingChildHelper() {
13181        if (mScrollingChildHelper == null) {
13182            mScrollingChildHelper = new NestedScrollingChildHelper(this);
13183        }
13184        return mScrollingChildHelper;
13185    }
13186}
13187