RecyclerView.java revision c76578ea4138aa224f8142a5de111ff38b79d9c3
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.content.Context;
25import android.content.res.Resources;
26import android.content.res.TypedArray;
27import android.database.Observable;
28import android.graphics.Canvas;
29import android.graphics.Matrix;
30import android.graphics.PointF;
31import android.graphics.Rect;
32import android.graphics.RectF;
33import android.graphics.drawable.Drawable;
34import android.graphics.drawable.StateListDrawable;
35import android.os.Build;
36import android.os.Bundle;
37import android.os.Parcel;
38import android.os.Parcelable;
39import android.os.SystemClock;
40import android.util.AttributeSet;
41import android.util.Log;
42import android.util.SparseArray;
43import android.view.Display;
44import android.view.FocusFinder;
45import android.view.InputDevice;
46import android.view.MotionEvent;
47import android.view.VelocityTracker;
48import android.view.View;
49import android.view.ViewConfiguration;
50import android.view.ViewGroup;
51import android.view.ViewParent;
52import android.view.accessibility.AccessibilityEvent;
53import android.view.accessibility.AccessibilityManager;
54import android.view.animation.Interpolator;
55import android.widget.EdgeEffect;
56import android.widget.LinearLayout;
57import android.widget.OverScroller;
58
59import androidx.annotation.CallSuper;
60import androidx.annotation.IntDef;
61import androidx.annotation.NonNull;
62import androidx.annotation.Nullable;
63import androidx.annotation.Px;
64import androidx.annotation.RestrictTo;
65import androidx.annotation.VisibleForTesting;
66import androidx.core.os.TraceCompat;
67import androidx.core.util.Preconditions;
68import androidx.core.view.InputDeviceCompat;
69import androidx.core.view.MotionEventCompat;
70import androidx.core.view.NestedScrollingChild2;
71import androidx.core.view.NestedScrollingChildHelper;
72import androidx.core.view.ScrollingView;
73import androidx.core.view.ViewCompat;
74import androidx.core.view.ViewConfigurationCompat;
75import androidx.core.view.accessibility.AccessibilityEventCompat;
76import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
77import androidx.core.widget.EdgeEffectCompat;
78import androidx.customview.view.AbsSavedState;
79import androidx.recyclerview.R;
80import androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
81import androidx.viewpager.widget.ViewPager;
82
83import java.lang.annotation.Retention;
84import java.lang.annotation.RetentionPolicy;
85import java.lang.ref.WeakReference;
86import java.lang.reflect.Constructor;
87import java.lang.reflect.InvocationTargetException;
88import java.util.ArrayList;
89import java.util.Collections;
90import java.util.List;
91
92
93/**
94 * A flexible view for providing a limited window into a large data set.
95 *
96 * <h3>Glossary of terms:</h3>
97 *
98 * <ul>
99 *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
100 *     that represent items in a data set.</li>
101 *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
102 *     <li><em>Index:</em> The index of an attached child view as used in a call to
103 *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
104 *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
105 *     to a <em>position</em> within the adapter.</li>
106 *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
107 *     position may be placed in a cache for later reuse to display the same type of data again
108 *     later. This can drastically improve performance by skipping initial layout inflation
109 *     or construction.</li>
110 *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
111 *     state during layout. Scrap views may be reused without becoming fully detached
112 *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
113 *     by the adapter if the view was considered <em>dirty</em>.</li>
114 *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
115 *     being displayed.</li>
116 * </ul>
117 *
118 * <h4>Positions in RecyclerView:</h4>
119 * <p>
120 * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
121 * {@link LayoutManager} to be able to detect data set changes in batches during a layout
122 * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
123 * It also helps with performance because all view bindings happen at the same time and unnecessary
124 * bindings are avoided.
125 * <p>
126 * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
127 * <ul>
128 *     <li>layout position: Position of an item in the latest layout calculation. This is the
129 *     position from the LayoutManager's perspective.</li>
130 *     <li>adapter position: Position of an item in the adapter. This is the position from
131 *     the Adapter's perspective.</li>
132 * </ul>
133 * <p>
134 * These two positions are the same except the time between dispatching <code>adapter.notify*
135 * </code> events and calculating the updated layout.
136 * <p>
137 * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
138 * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
139 * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
140 * last layout calculation. You can rely on these positions to be consistent with what user is
141 * currently seeing on the screen. For example, if you have a list of items on the screen and user
142 * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
143 * is seeing.
144 * <p>
145 * The other set of position related methods are in the form of
146 * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
147 * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
148 * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
149 * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
150 * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
151 * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
152 * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
153 * <code>null</code> results from these methods.
154 * <p>
155 * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
156 * writing an {@link Adapter}, you probably want to use adapter positions.
157 *
158 * @attr ref androidx.recyclerview.R.styleable#RecyclerView_layoutManager
159 */
160public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild2 {
161
162    static final String TAG = "RecyclerView";
163
164    static final boolean DEBUG = false;
165
166    static final boolean VERBOSE_TRACING = false;
167
168    private static final int[]  NESTED_SCROLLING_ATTRS =
169            {16843830 /* android.R.attr.nestedScrollingEnabled */};
170
171    private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
172
173    /**
174     * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
175     * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
176     * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
177     * recursively traverses itemView and invalidates display list for each ViewGroup that matches
178     * this criteria.
179     */
180    static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
181            || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
182    /**
183     * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
184     * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
185     * 0 when mode is unspecified.
186     */
187    static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
188
189    static final boolean POST_UPDATES_ON_ANIMATION = Build.VERSION.SDK_INT >= 16;
190
191    /**
192     * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
193     * RenderThread but before the next frame begins. We schedule prefetch work in this window.
194     */
195    private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
196
197    /**
198     * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
199     * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
200     */
201    private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
202
203    /**
204     * on API 15-, a focused child can still be considered a focused child of RV even after
205     * it's being removed or its focusable flag is set to false. This is because when this focused
206     * child is detached, the reference to this child is not removed in clearFocus. API 16 and above
207     * properly handle this case by calling ensureInputFocusOnFirstFocusable or rootViewRequestFocus
208     * to request focus on a new child, which will clear the focus on the old (detached) child as a
209     * side-effect.
210     */
211    private static final boolean IGNORE_DETACHED_FOCUSED_CHILD = Build.VERSION.SDK_INT <= 15;
212
213    static final boolean DISPATCH_TEMP_DETACH = false;
214
215    /** @hide */
216    @RestrictTo(LIBRARY_GROUP)
217    @IntDef({HORIZONTAL, VERTICAL})
218    @Retention(RetentionPolicy.SOURCE)
219    public @interface Orientation {}
220
221    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
222    public static final int VERTICAL = LinearLayout.VERTICAL;
223
224    static final int DEFAULT_ORIENTATION = VERTICAL;
225    public static final int NO_POSITION = -1;
226    public static final long NO_ID = -1;
227    public static final int INVALID_TYPE = -1;
228
229    /**
230     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
231     * that the RecyclerView should use the standard touch slop for smooth,
232     * continuous scrolling.
233     */
234    public static final int TOUCH_SLOP_DEFAULT = 0;
235
236    /**
237     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
238     * that the RecyclerView should use the standard touch slop for scrolling
239     * widgets that snap to a page or other coarse-grained barrier.
240     */
241    public static final int TOUCH_SLOP_PAGING = 1;
242
243    static final int MAX_SCROLL_DURATION = 2000;
244
245    /**
246     * RecyclerView is calculating a scroll.
247     * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
248     * it. Try to avoid using EditText, focusable views or handle them with care.
249     */
250    static final String TRACE_SCROLL_TAG = "RV Scroll";
251
252    /**
253     * OnLayout has been called by the View system.
254     * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
255     * update themselves directly. This will cause a full re-layout but when it happens via the
256     * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
257     */
258    private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
259
260    /**
261     * NotifyDataSetChanged or equal has been called.
262     * If this is taking a long time, try sending granular notify adapter changes instead of just
263     * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
264     * might help.
265     */
266    private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
267
268    /**
269     * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
270     * If this is taking a long time, you may have dispatched too many Adapter updates causing too
271     * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
272     * methods.
273     */
274    private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
275
276    /**
277     * RecyclerView is rebinding a View.
278     * If this is taking a lot of time, consider optimizing your layout or make sure you are not
279     * doing extra operations in onBindViewHolder call.
280     */
281    static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
282
283    /**
284     * RecyclerView is attempting to pre-populate off screen views.
285     */
286    static final String TRACE_PREFETCH_TAG = "RV Prefetch";
287
288    /**
289     * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
290     * RecyclerView.
291     */
292    static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
293
294    /**
295     * RecyclerView is creating a new View.
296     * If too many of these present in Systrace:
297     * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
298     * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
299     * > Adapter#onFailedToRecycleView(ViewHolder)})
300     *
301     * - There might be too many item view types.
302     * > Try merging them
303     *
304     * - There might be too many itemChange animations and not enough space in RecyclerPool.
305     * >Try increasing your pool size and item cache size.
306     */
307    static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
308    private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
309            new Class[]{Context.class, AttributeSet.class, int.class, int.class};
310
311    private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
312
313    final Recycler mRecycler = new Recycler();
314
315    private SavedState mPendingSavedState;
316
317    /**
318     * Handles adapter updates
319     */
320    AdapterHelper mAdapterHelper;
321
322    /**
323     * Handles abstraction between LayoutManager children and RecyclerView children
324     */
325    ChildHelper mChildHelper;
326
327    /**
328     * Keeps data about views to be used for animations
329     */
330    final ViewInfoStore mViewInfoStore = new ViewInfoStore();
331
332    /**
333     * Prior to L, there is no way to query this variable which is why we override the setter and
334     * track it here.
335     */
336    boolean mClipToPadding;
337
338    /**
339     * Note: this Runnable is only ever posted if:
340     * 1) We've been through first layout
341     * 2) We know we have a fixed size (mHasFixedSize)
342     * 3) We're attached
343     */
344    final Runnable mUpdateChildViewsRunnable = new Runnable() {
345        @Override
346        public void run() {
347            if (!mFirstLayoutComplete || isLayoutRequested()) {
348                // a layout request will happen, we should not do layout here.
349                return;
350            }
351            if (!mIsAttached) {
352                requestLayout();
353                // if we are not attached yet, mark us as requiring layout and skip
354                return;
355            }
356            if (mLayoutFrozen) {
357                mLayoutWasDefered = true;
358                return; //we'll process updates when ice age ends.
359            }
360            consumePendingUpdateOperations();
361        }
362    };
363
364    final Rect mTempRect = new Rect();
365    private final Rect mTempRect2 = new Rect();
366    final RectF mTempRectF = new RectF();
367    Adapter mAdapter;
368    @VisibleForTesting LayoutManager mLayout;
369    RecyclerListener mRecyclerListener;
370    final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
371    private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
372            new ArrayList<>();
373    private OnItemTouchListener mActiveOnItemTouchListener;
374    boolean mIsAttached;
375    boolean mHasFixedSize;
376    boolean mEnableFastScroller;
377    @VisibleForTesting boolean mFirstLayoutComplete;
378
379    /**
380     * The current depth of nested calls to {@link #startInterceptRequestLayout()} (number of
381     * calls to {@link #startInterceptRequestLayout()} - number of calls to
382     * {@link #stopInterceptRequestLayout(boolean)} .  This is used to signal whether we
383     * should defer layout operations caused by layout requests from children of
384     * {@link RecyclerView}.
385     */
386    private int mInterceptRequestLayoutDepth = 0;
387
388    /**
389     * True if a call to requestLayout was intercepted and prevented from executing like normal and
390     * we plan on continuing with normal execution later.
391     */
392    boolean mLayoutWasDefered;
393
394    boolean mLayoutFrozen;
395    private boolean mIgnoreMotionEventTillDown;
396
397    // binary OR of change events that were eaten during a layout or scroll.
398    private int mEatenAccessibilityChangeFlags;
399    boolean mAdapterUpdateDuringMeasure;
400
401    private final AccessibilityManager mAccessibilityManager;
402    private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
403
404    /**
405     * True after an event occurs that signals that the entire data set has changed. In that case,
406     * we cannot run any animations since we don't know what happened until layout.
407     *
408     * Attached items are invalid until next layout, at which point layout will animate/replace
409     * items as necessary, building up content from the (effectively) new adapter from scratch.
410     *
411     * Cached items must be discarded when setting this to true, so that the cache may be freely
412     * used by prefetching until the next layout occurs.
413     *
414     * @see #processDataSetCompletelyChanged(boolean)
415     */
416    boolean mDataSetHasChangedAfterLayout = false;
417
418    /**
419     * True after the data set has completely changed and
420     * {@link LayoutManager#onItemsChanged(RecyclerView)} should be called during the subsequent
421     * measure/layout.
422     *
423     * @see #processDataSetCompletelyChanged(boolean)
424     */
425    boolean mDispatchItemsChangedEvent = false;
426
427    /**
428     * This variable is incremented during a dispatchLayout and/or scroll.
429     * Some methods should not be called during these periods (e.g. adapter data change).
430     * Doing so will create hard to find bugs so we better check it and throw an exception.
431     *
432     * @see #assertInLayoutOrScroll(String)
433     * @see #assertNotInLayoutOrScroll(String)
434     */
435    private int mLayoutOrScrollCounter = 0;
436
437    /**
438     * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
439     * (for API compatibility).
440     * <p>
441     * It is a bad practice for a developer to update the data in a scroll callback since it is
442     * potentially called during a layout.
443     */
444    private int mDispatchScrollCounter = 0;
445
446    @NonNull
447    private EdgeEffectFactory mEdgeEffectFactory = new EdgeEffectFactory();
448    private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
449
450    ItemAnimator mItemAnimator = new DefaultItemAnimator();
451
452    private static final int INVALID_POINTER = -1;
453
454    /**
455     * The RecyclerView is not currently scrolling.
456     * @see #getScrollState()
457     */
458    public static final int SCROLL_STATE_IDLE = 0;
459
460    /**
461     * The RecyclerView is currently being dragged by outside input such as user touch input.
462     * @see #getScrollState()
463     */
464    public static final int SCROLL_STATE_DRAGGING = 1;
465
466    /**
467     * The RecyclerView is currently animating to a final position while not under
468     * outside control.
469     * @see #getScrollState()
470     */
471    public static final int SCROLL_STATE_SETTLING = 2;
472
473    static final long FOREVER_NS = Long.MAX_VALUE;
474
475    // Touch/scrolling handling
476
477    private int mScrollState = SCROLL_STATE_IDLE;
478    private int mScrollPointerId = INVALID_POINTER;
479    private VelocityTracker mVelocityTracker;
480    private int mInitialTouchX;
481    private int mInitialTouchY;
482    private int mLastTouchX;
483    private int mLastTouchY;
484    private int mTouchSlop;
485    private OnFlingListener mOnFlingListener;
486    private final int mMinFlingVelocity;
487    private final int mMaxFlingVelocity;
488
489    // This value is used when handling rotary encoder generic motion events.
490    private float mScaledHorizontalScrollFactor = Float.MIN_VALUE;
491    private float mScaledVerticalScrollFactor = Float.MIN_VALUE;
492
493    private boolean mPreserveFocusAfterLayout = true;
494
495    final ViewFlinger mViewFlinger = new ViewFlinger();
496
497    GapWorker mGapWorker;
498    GapWorker.LayoutPrefetchRegistryImpl mPrefetchRegistry =
499            ALLOW_THREAD_GAP_WORK ? new GapWorker.LayoutPrefetchRegistryImpl() : null;
500
501    final State mState = new State();
502
503    private OnScrollListener mScrollListener;
504    private List<OnScrollListener> mScrollListeners;
505
506    // For use in item animations
507    boolean mItemsAddedOrRemoved = false;
508    boolean mItemsChanged = false;
509    private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
510            new ItemAnimatorRestoreListener();
511    boolean mPostedAnimatorRunner = false;
512    RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
513    private ChildDrawingOrderCallback mChildDrawingOrderCallback;
514
515    // simple array to keep min and max child position during a layout calculation
516    // preserved not to create a new one in each layout pass
517    private final int[] mMinMaxLayoutPositions = new int[2];
518
519    private NestedScrollingChildHelper mScrollingChildHelper;
520    private final int[] mScrollOffset = new int[2];
521    private final int[] mScrollConsumed = new int[2];
522    private final int[] mNestedOffsets = new int[2];
523
524    /**
525     * Reusable int array for use in calls to {@link #scrollStep(int, int, int[])} so that the
526     * method may mutate it to "return" 2 ints.
527     */
528    private final int[] mScrollStepConsumed = new int[2];
529
530    /**
531     * These are views that had their a11y importance changed during a layout. We defer these events
532     * until the end of the layout because a11y service may make sync calls back to the RV while
533     * the View's state is undefined.
534     */
535    @VisibleForTesting
536    final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList<>();
537
538    private Runnable mItemAnimatorRunner = new Runnable() {
539        @Override
540        public void run() {
541            if (mItemAnimator != null) {
542                mItemAnimator.runPendingAnimations();
543            }
544            mPostedAnimatorRunner = false;
545        }
546    };
547
548    static final Interpolator sQuinticInterpolator = new Interpolator() {
549        @Override
550        public float getInterpolation(float t) {
551            t -= 1.0f;
552            return t * t * t * t * t + 1.0f;
553        }
554    };
555
556    /**
557     * The callback to convert view info diffs into animations.
558     */
559    private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
560            new ViewInfoStore.ProcessCallback() {
561                @Override
562                public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
563                        @Nullable ItemHolderInfo postInfo) {
564                    mRecycler.unscrapView(viewHolder);
565                    animateDisappearance(viewHolder, info, postInfo);
566                }
567                @Override
568                public void processAppeared(ViewHolder viewHolder,
569                        ItemHolderInfo preInfo, ItemHolderInfo info) {
570                    animateAppearance(viewHolder, preInfo, info);
571                }
572
573                @Override
574                public void processPersistent(ViewHolder viewHolder,
575                        @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
576                    viewHolder.setIsRecyclable(false);
577                    if (mDataSetHasChangedAfterLayout) {
578                        // since it was rebound, use change instead as we'll be mapping them from
579                        // stable ids. If stable ids were false, we would not be running any
580                        // animations
581                        if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo,
582                                postInfo)) {
583                            postAnimationRunner();
584                        }
585                    } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
586                        postAnimationRunner();
587                    }
588                }
589                @Override
590                public void unused(ViewHolder viewHolder) {
591                    mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
592                }
593            };
594
595    public RecyclerView(@NonNull Context context) {
596        this(context, null);
597    }
598
599    public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
600        this(context, attrs, 0);
601    }
602
603    public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
604        super(context, attrs, defStyle);
605        if (attrs != null) {
606            TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
607            mClipToPadding = a.getBoolean(0, true);
608            a.recycle();
609        } else {
610            mClipToPadding = true;
611        }
612        setScrollContainer(true);
613        setFocusableInTouchMode(true);
614
615        final ViewConfiguration vc = ViewConfiguration.get(context);
616        mTouchSlop = vc.getScaledTouchSlop();
617        mScaledHorizontalScrollFactor =
618                ViewConfigurationCompat.getScaledHorizontalScrollFactor(vc, context);
619        mScaledVerticalScrollFactor =
620                ViewConfigurationCompat.getScaledVerticalScrollFactor(vc, context);
621        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
622        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
623        setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
624
625        mItemAnimator.setListener(mItemAnimatorListener);
626        initAdapterManager();
627        initChildrenHelper();
628        // If not explicitly specified this view is important for accessibility.
629        if (ViewCompat.getImportantForAccessibility(this)
630                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
631            ViewCompat.setImportantForAccessibility(this,
632                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
633        }
634        mAccessibilityManager = (AccessibilityManager) getContext()
635                .getSystemService(Context.ACCESSIBILITY_SERVICE);
636        setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
637        // Create the layoutManager if specified.
638
639        boolean nestedScrollingEnabled = true;
640
641        if (attrs != null) {
642            int defStyleRes = 0;
643            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
644                    defStyle, defStyleRes);
645            String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
646            int descendantFocusability = a.getInt(
647                    R.styleable.RecyclerView_android_descendantFocusability, -1);
648            if (descendantFocusability == -1) {
649                setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
650            }
651            mEnableFastScroller = a.getBoolean(R.styleable.RecyclerView_fastScrollEnabled, false);
652            if (mEnableFastScroller) {
653                StateListDrawable verticalThumbDrawable = (StateListDrawable) a
654                        .getDrawable(R.styleable.RecyclerView_fastScrollVerticalThumbDrawable);
655                Drawable verticalTrackDrawable = a
656                        .getDrawable(R.styleable.RecyclerView_fastScrollVerticalTrackDrawable);
657                StateListDrawable horizontalThumbDrawable = (StateListDrawable) a
658                        .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalThumbDrawable);
659                Drawable horizontalTrackDrawable = a
660                        .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalTrackDrawable);
661                initFastScroller(verticalThumbDrawable, verticalTrackDrawable,
662                        horizontalThumbDrawable, horizontalTrackDrawable);
663            }
664            a.recycle();
665            createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
666
667            if (Build.VERSION.SDK_INT >= 21) {
668                a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
669                        defStyle, defStyleRes);
670                nestedScrollingEnabled = a.getBoolean(0, true);
671                a.recycle();
672            }
673        } else {
674            setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
675        }
676
677        // Re-set whether nested scrolling is enabled so that it is set on all API levels
678        setNestedScrollingEnabled(nestedScrollingEnabled);
679    }
680
681    /**
682     * Label appended to all public exception strings, used to help find which RV in an app is
683     * hitting an exception.
684     */
685    String exceptionLabel() {
686        return " " + super.toString()
687                + ", adapter:" + mAdapter
688                + ", layout:" + mLayout
689                + ", context:" + getContext();
690    }
691
692    /**
693     * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
694     * @return An instance of AccessibilityDelegateCompat used by RecyclerView
695     */
696    @Nullable
697    public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
698        return mAccessibilityDelegate;
699    }
700
701    /**
702     * Sets the accessibility delegate compatibility implementation used by RecyclerView.
703     * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
704     */
705    public void setAccessibilityDelegateCompat(
706            @Nullable RecyclerViewAccessibilityDelegate accessibilityDelegate) {
707        mAccessibilityDelegate = accessibilityDelegate;
708        ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
709    }
710
711    /**
712     * Instantiate and set a LayoutManager, if specified in the attributes.
713     */
714    private void createLayoutManager(Context context, String className, AttributeSet attrs,
715            int defStyleAttr, int defStyleRes) {
716        if (className != null) {
717            className = className.trim();
718            if (!className.isEmpty()) {
719                className = getFullClassName(context, className);
720                try {
721                    ClassLoader classLoader;
722                    if (isInEditMode()) {
723                        // Stupid layoutlib cannot handle simple class loaders.
724                        classLoader = this.getClass().getClassLoader();
725                    } else {
726                        classLoader = context.getClassLoader();
727                    }
728                    Class<? extends LayoutManager> layoutManagerClass =
729                            classLoader.loadClass(className).asSubclass(LayoutManager.class);
730                    Constructor<? extends LayoutManager> constructor;
731                    Object[] constructorArgs = null;
732                    try {
733                        constructor = layoutManagerClass
734                                .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
735                        constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
736                    } catch (NoSuchMethodException e) {
737                        try {
738                            constructor = layoutManagerClass.getConstructor();
739                        } catch (NoSuchMethodException e1) {
740                            e1.initCause(e);
741                            throw new IllegalStateException(attrs.getPositionDescription()
742                                    + ": Error creating LayoutManager " + className, e1);
743                        }
744                    }
745                    constructor.setAccessible(true);
746                    setLayoutManager(constructor.newInstance(constructorArgs));
747                } catch (ClassNotFoundException e) {
748                    throw new IllegalStateException(attrs.getPositionDescription()
749                            + ": Unable to find LayoutManager " + className, e);
750                } catch (InvocationTargetException e) {
751                    throw new IllegalStateException(attrs.getPositionDescription()
752                            + ": Could not instantiate the LayoutManager: " + className, e);
753                } catch (InstantiationException e) {
754                    throw new IllegalStateException(attrs.getPositionDescription()
755                            + ": Could not instantiate the LayoutManager: " + className, e);
756                } catch (IllegalAccessException e) {
757                    throw new IllegalStateException(attrs.getPositionDescription()
758                            + ": Cannot access non-public constructor " + className, e);
759                } catch (ClassCastException e) {
760                    throw new IllegalStateException(attrs.getPositionDescription()
761                            + ": Class is not a LayoutManager " + className, e);
762                }
763            }
764        }
765    }
766
767    private String getFullClassName(Context context, String className) {
768        if (className.charAt(0) == '.') {
769            return context.getPackageName() + className;
770        }
771        if (className.contains(".")) {
772            return className;
773        }
774        return RecyclerView.class.getPackage().getName() + '.' + className;
775    }
776
777    private void initChildrenHelper() {
778        mChildHelper = new ChildHelper(new ChildHelper.Callback() {
779            @Override
780            public int getChildCount() {
781                return RecyclerView.this.getChildCount();
782            }
783
784            @Override
785            public void addView(View child, int index) {
786                if (VERBOSE_TRACING) {
787                    TraceCompat.beginSection("RV addView");
788                }
789                RecyclerView.this.addView(child, index);
790                if (VERBOSE_TRACING) {
791                    TraceCompat.endSection();
792                }
793                dispatchChildAttached(child);
794            }
795
796            @Override
797            public int indexOfChild(View view) {
798                return RecyclerView.this.indexOfChild(view);
799            }
800
801            @Override
802            public void removeViewAt(int index) {
803                final View child = RecyclerView.this.getChildAt(index);
804                if (child != null) {
805                    dispatchChildDetached(child);
806
807                    // Clear any android.view.animation.Animation that may prevent the item from
808                    // detaching when being removed. If a child is re-added before the
809                    // lazy detach occurs, it will receive invalid attach/detach sequencing.
810                    child.clearAnimation();
811                }
812                if (VERBOSE_TRACING) {
813                    TraceCompat.beginSection("RV removeViewAt");
814                }
815                RecyclerView.this.removeViewAt(index);
816                if (VERBOSE_TRACING) {
817                    TraceCompat.endSection();
818                }
819            }
820
821            @Override
822            public View getChildAt(int offset) {
823                return RecyclerView.this.getChildAt(offset);
824            }
825
826            @Override
827            public void removeAllViews() {
828                final int count = getChildCount();
829                for (int i = 0; i < count; i++) {
830                    View child = getChildAt(i);
831                    dispatchChildDetached(child);
832
833                    // Clear any android.view.animation.Animation that may prevent the item from
834                    // detaching when being removed. If a child is re-added before the
835                    // lazy detach occurs, it will receive invalid attach/detach sequencing.
836                    child.clearAnimation();
837                }
838                RecyclerView.this.removeAllViews();
839            }
840
841            @Override
842            public ViewHolder getChildViewHolder(View view) {
843                return getChildViewHolderInt(view);
844            }
845
846            @Override
847            public void attachViewToParent(View child, int index,
848                    ViewGroup.LayoutParams layoutParams) {
849                final ViewHolder vh = getChildViewHolderInt(child);
850                if (vh != null) {
851                    if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
852                        throw new IllegalArgumentException("Called attach on a child which is not"
853                                + " detached: " + vh + exceptionLabel());
854                    }
855                    if (DEBUG) {
856                        Log.d(TAG, "reAttach " + vh);
857                    }
858                    vh.clearTmpDetachFlag();
859                }
860                RecyclerView.this.attachViewToParent(child, index, layoutParams);
861            }
862
863            @Override
864            public void detachViewFromParent(int offset) {
865                final View view = getChildAt(offset);
866                if (view != null) {
867                    final ViewHolder vh = getChildViewHolderInt(view);
868                    if (vh != null) {
869                        if (vh.isTmpDetached() && !vh.shouldIgnore()) {
870                            throw new IllegalArgumentException("called detach on an already"
871                                    + " detached child " + vh + exceptionLabel());
872                        }
873                        if (DEBUG) {
874                            Log.d(TAG, "tmpDetach " + vh);
875                        }
876                        vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
877                    }
878                }
879                RecyclerView.this.detachViewFromParent(offset);
880            }
881
882            @Override
883            public void onEnteredHiddenState(View child) {
884                final ViewHolder vh = getChildViewHolderInt(child);
885                if (vh != null) {
886                    vh.onEnteredHiddenState(RecyclerView.this);
887                }
888            }
889
890            @Override
891            public void onLeftHiddenState(View child) {
892                final ViewHolder vh = getChildViewHolderInt(child);
893                if (vh != null) {
894                    vh.onLeftHiddenState(RecyclerView.this);
895                }
896            }
897        });
898    }
899
900    void initAdapterManager() {
901        mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
902            @Override
903            public ViewHolder findViewHolder(int position) {
904                final ViewHolder vh = findViewHolderForPosition(position, true);
905                if (vh == null) {
906                    return null;
907                }
908                // ensure it is not hidden because for adapter helper, the only thing matter is that
909                // LM thinks view is a child.
910                if (mChildHelper.isHidden(vh.itemView)) {
911                    if (DEBUG) {
912                        Log.d(TAG, "assuming view holder cannot be find because it is hidden");
913                    }
914                    return null;
915                }
916                return vh;
917            }
918
919            @Override
920            public void offsetPositionsForRemovingInvisible(int start, int count) {
921                offsetPositionRecordsForRemove(start, count, true);
922                mItemsAddedOrRemoved = true;
923                mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
924            }
925
926            @Override
927            public void offsetPositionsForRemovingLaidOutOrNewView(
928                    int positionStart, int itemCount) {
929                offsetPositionRecordsForRemove(positionStart, itemCount, false);
930                mItemsAddedOrRemoved = true;
931            }
932
933
934            @Override
935            public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
936                viewRangeUpdate(positionStart, itemCount, payload);
937                mItemsChanged = true;
938            }
939
940            @Override
941            public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
942                dispatchUpdate(op);
943            }
944
945            void dispatchUpdate(AdapterHelper.UpdateOp op) {
946                switch (op.cmd) {
947                    case AdapterHelper.UpdateOp.ADD:
948                        mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
949                        break;
950                    case AdapterHelper.UpdateOp.REMOVE:
951                        mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
952                        break;
953                    case AdapterHelper.UpdateOp.UPDATE:
954                        mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
955                                op.payload);
956                        break;
957                    case AdapterHelper.UpdateOp.MOVE:
958                        mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
959                        break;
960                }
961            }
962
963            @Override
964            public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
965                dispatchUpdate(op);
966            }
967
968            @Override
969            public void offsetPositionsForAdd(int positionStart, int itemCount) {
970                offsetPositionRecordsForInsert(positionStart, itemCount);
971                mItemsAddedOrRemoved = true;
972            }
973
974            @Override
975            public void offsetPositionsForMove(int from, int to) {
976                offsetPositionRecordsForMove(from, to);
977                // should we create mItemsMoved ?
978                mItemsAddedOrRemoved = true;
979            }
980        });
981    }
982
983    /**
984     * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
985     * size is not affected by the adapter contents. RecyclerView can still change its size based
986     * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
987     * size of its children or contents of its adapter (except the number of items in the adapter).
988     * <p>
989     * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
990     * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
991     *
992     * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
993     */
994    public void setHasFixedSize(boolean hasFixedSize) {
995        mHasFixedSize = hasFixedSize;
996    }
997
998    /**
999     * @return true if the app has specified that changes in adapter content cannot change
1000     * the size of the RecyclerView itself.
1001     */
1002    public boolean hasFixedSize() {
1003        return mHasFixedSize;
1004    }
1005
1006    @Override
1007    public void setClipToPadding(boolean clipToPadding) {
1008        if (clipToPadding != mClipToPadding) {
1009            invalidateGlows();
1010        }
1011        mClipToPadding = clipToPadding;
1012        super.setClipToPadding(clipToPadding);
1013        if (mFirstLayoutComplete) {
1014            requestLayout();
1015        }
1016    }
1017
1018    /**
1019     * Returns whether this RecyclerView will clip its children to its padding, and resize (but
1020     * not clip) any EdgeEffect to the padded region, if padding is present.
1021     * <p>
1022     * By default, children are clipped to the padding of their parent
1023     * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
1024     *
1025     * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
1026     *         clip) any EdgeEffect to the padded region, false otherwise.
1027     *
1028     * @attr name android:clipToPadding
1029     */
1030    @Override
1031    public boolean getClipToPadding() {
1032        return mClipToPadding;
1033    }
1034
1035    /**
1036     * Configure the scrolling touch slop for a specific use case.
1037     *
1038     * Set up the RecyclerView's scrolling motion threshold based on common usages.
1039     * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
1040     *
1041     * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
1042     *                     the intended usage of this RecyclerView
1043     */
1044    public void setScrollingTouchSlop(int slopConstant) {
1045        final ViewConfiguration vc = ViewConfiguration.get(getContext());
1046        switch (slopConstant) {
1047            default:
1048                Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
1049                        + slopConstant + "; using default value");
1050                // fall-through
1051            case TOUCH_SLOP_DEFAULT:
1052                mTouchSlop = vc.getScaledTouchSlop();
1053                break;
1054
1055            case TOUCH_SLOP_PAGING:
1056                mTouchSlop = vc.getScaledPagingTouchSlop();
1057                break;
1058        }
1059    }
1060
1061    /**
1062     * Swaps the current adapter with the provided one. It is similar to
1063     * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
1064     * {@link ViewHolder} and does not clear the RecycledViewPool.
1065     * <p>
1066     * Note that it still calls onAdapterChanged callbacks.
1067     *
1068     * @param adapter The new adapter to set, or null to set no adapter.
1069     * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
1070     *                                      Views. If adapters have stable ids and/or you want to
1071     *                                      animate the disappearing views, you may prefer to set
1072     *                                      this to false.
1073     * @see #setAdapter(Adapter)
1074     */
1075    public void swapAdapter(@Nullable Adapter adapter, boolean removeAndRecycleExistingViews) {
1076        // bail out if layout is frozen
1077        setLayoutFrozen(false);
1078        setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
1079        processDataSetCompletelyChanged(true);
1080        requestLayout();
1081    }
1082    /**
1083     * Set a new adapter to provide child views on demand.
1084     * <p>
1085     * When adapter is changed, all existing views are recycled back to the pool. If the pool has
1086     * only one adapter, it will be cleared.
1087     *
1088     * @param adapter The new adapter to set, or null to set no adapter.
1089     * @see #swapAdapter(Adapter, boolean)
1090     */
1091    public void setAdapter(@Nullable Adapter adapter) {
1092        // bail out if layout is frozen
1093        setLayoutFrozen(false);
1094        setAdapterInternal(adapter, false, true);
1095        processDataSetCompletelyChanged(false);
1096        requestLayout();
1097    }
1098
1099    /**
1100     * Removes and recycles all views - both those currently attached, and those in the Recycler.
1101     */
1102    void removeAndRecycleViews() {
1103        // end all running animations
1104        if (mItemAnimator != null) {
1105            mItemAnimator.endAnimations();
1106        }
1107        // Since animations are ended, mLayout.children should be equal to
1108        // recyclerView.children. This may not be true if item animator's end does not work as
1109        // expected. (e.g. not release children instantly). It is safer to use mLayout's child
1110        // count.
1111        if (mLayout != null) {
1112            mLayout.removeAndRecycleAllViews(mRecycler);
1113            mLayout.removeAndRecycleScrapInt(mRecycler);
1114        }
1115        // we should clear it here before adapters are swapped to ensure correct callbacks.
1116        mRecycler.clear();
1117    }
1118
1119    /**
1120     * Replaces the current adapter with the new one and triggers listeners.
1121     * @param adapter The new adapter
1122     * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
1123     *                               item types with the current adapter (helps us avoid cache
1124     *                               invalidation).
1125     * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
1126     *                               compatibleWithPrevious is false, this parameter is ignored.
1127     */
1128    private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
1129            boolean removeAndRecycleViews) {
1130        if (mAdapter != null) {
1131            mAdapter.unregisterAdapterDataObserver(mObserver);
1132            mAdapter.onDetachedFromRecyclerView(this);
1133        }
1134        if (!compatibleWithPrevious || removeAndRecycleViews) {
1135            removeAndRecycleViews();
1136        }
1137        mAdapterHelper.reset();
1138        final Adapter oldAdapter = mAdapter;
1139        mAdapter = adapter;
1140        if (adapter != null) {
1141            adapter.registerAdapterDataObserver(mObserver);
1142            adapter.onAttachedToRecyclerView(this);
1143        }
1144        if (mLayout != null) {
1145            mLayout.onAdapterChanged(oldAdapter, mAdapter);
1146        }
1147        mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
1148        mState.mStructureChanged = true;
1149    }
1150
1151    /**
1152     * Retrieves the previously set adapter or null if no adapter is set.
1153     *
1154     * @return The previously set adapter
1155     * @see #setAdapter(Adapter)
1156     */
1157    @Nullable
1158    public Adapter getAdapter() {
1159        return mAdapter;
1160    }
1161
1162    /**
1163     * Register a listener that will be notified whenever a child view is recycled.
1164     *
1165     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1166     * that a child view is no longer needed. If an application associates expensive
1167     * or heavyweight data with item views, this may be a good place to release
1168     * or free those resources.</p>
1169     *
1170     * @param listener Listener to register, or null to clear
1171     */
1172    public void setRecyclerListener(@Nullable RecyclerListener listener) {
1173        mRecyclerListener = listener;
1174    }
1175
1176    /**
1177     * <p>Return the offset of the RecyclerView's text baseline from the its top
1178     * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
1179     * this method returns -1.</p>
1180     *
1181     * @return the offset of the baseline within the RecyclerView's bounds or -1
1182     *         if baseline alignment is not supported
1183     */
1184    @Override
1185    public int getBaseline() {
1186        if (mLayout != null) {
1187            return mLayout.getBaseline();
1188        } else {
1189            return super.getBaseline();
1190        }
1191    }
1192
1193    /**
1194     * Register a listener that will be notified whenever a child view is attached to or detached
1195     * from RecyclerView.
1196     *
1197     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1198     * that a child view is no longer needed. If an application associates expensive
1199     * or heavyweight data with item views, this may be a good place to release
1200     * or free those resources.</p>
1201     *
1202     * @param listener Listener to register
1203     */
1204    public void addOnChildAttachStateChangeListener(
1205            @NonNull OnChildAttachStateChangeListener listener) {
1206        if (mOnChildAttachStateListeners == null) {
1207            mOnChildAttachStateListeners = new ArrayList<>();
1208        }
1209        mOnChildAttachStateListeners.add(listener);
1210    }
1211
1212    /**
1213     * Removes the provided listener from child attached state listeners list.
1214     *
1215     * @param listener Listener to unregister
1216     */
1217    public void removeOnChildAttachStateChangeListener(
1218            @NonNull OnChildAttachStateChangeListener listener) {
1219        if (mOnChildAttachStateListeners == null) {
1220            return;
1221        }
1222        mOnChildAttachStateListeners.remove(listener);
1223    }
1224
1225    /**
1226     * Removes all listeners that were added via
1227     * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
1228     */
1229    public void clearOnChildAttachStateChangeListeners() {
1230        if (mOnChildAttachStateListeners != null) {
1231            mOnChildAttachStateListeners.clear();
1232        }
1233    }
1234
1235    /**
1236     * Set the {@link LayoutManager} that this RecyclerView will use.
1237     *
1238     * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
1239     * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
1240     * layout arrangements for child views. These arrangements are controlled by the
1241     * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
1242     *
1243     * <p>Several default strategies are provided for common uses such as lists and grids.</p>
1244     *
1245     * @param layout LayoutManager to use
1246     */
1247    public void setLayoutManager(@Nullable LayoutManager layout) {
1248        if (layout == mLayout) {
1249            return;
1250        }
1251        stopScroll();
1252        // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
1253        // chance that LayoutManagers will re-use views.
1254        if (mLayout != null) {
1255            // end all running animations
1256            if (mItemAnimator != null) {
1257                mItemAnimator.endAnimations();
1258            }
1259            mLayout.removeAndRecycleAllViews(mRecycler);
1260            mLayout.removeAndRecycleScrapInt(mRecycler);
1261            mRecycler.clear();
1262
1263            if (mIsAttached) {
1264                mLayout.dispatchDetachedFromWindow(this, mRecycler);
1265            }
1266            mLayout.setRecyclerView(null);
1267            mLayout = null;
1268        } else {
1269            mRecycler.clear();
1270        }
1271        // this is just a defensive measure for faulty item animators.
1272        mChildHelper.removeAllViewsUnfiltered();
1273        mLayout = layout;
1274        if (layout != null) {
1275            if (layout.mRecyclerView != null) {
1276                throw new IllegalArgumentException("LayoutManager " + layout
1277                        + " is already attached to a RecyclerView:"
1278                        + layout.mRecyclerView.exceptionLabel());
1279            }
1280            mLayout.setRecyclerView(this);
1281            if (mIsAttached) {
1282                mLayout.dispatchAttachedToWindow(this);
1283            }
1284        }
1285        mRecycler.updateViewCacheSize();
1286        requestLayout();
1287    }
1288
1289    /**
1290     * Set a {@link OnFlingListener} for this {@link RecyclerView}.
1291     * <p>
1292     * If the {@link OnFlingListener} is set then it will receive
1293     * calls to {@link #fling(int,int)} and will be able to intercept them.
1294     *
1295     * @param onFlingListener The {@link OnFlingListener} instance.
1296     */
1297    public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
1298        mOnFlingListener = onFlingListener;
1299    }
1300
1301    /**
1302     * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
1303     *
1304     * @return The {@link OnFlingListener} instance currently set (can be null).
1305     */
1306    @Nullable
1307    public OnFlingListener getOnFlingListener() {
1308        return mOnFlingListener;
1309    }
1310
1311    @Override
1312    protected Parcelable onSaveInstanceState() {
1313        SavedState state = new SavedState(super.onSaveInstanceState());
1314        if (mPendingSavedState != null) {
1315            state.copyFrom(mPendingSavedState);
1316        } else if (mLayout != null) {
1317            state.mLayoutState = mLayout.onSaveInstanceState();
1318        } else {
1319            state.mLayoutState = null;
1320        }
1321
1322        return state;
1323    }
1324
1325    @Override
1326    protected void onRestoreInstanceState(Parcelable state) {
1327        if (!(state instanceof SavedState)) {
1328            super.onRestoreInstanceState(state);
1329            return;
1330        }
1331
1332        mPendingSavedState = (SavedState) state;
1333        super.onRestoreInstanceState(mPendingSavedState.getSuperState());
1334        if (mLayout != null && mPendingSavedState.mLayoutState != null) {
1335            mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
1336        }
1337    }
1338
1339    /**
1340     * Override to prevent freezing of any views created by the adapter.
1341     */
1342    @Override
1343    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1344        dispatchFreezeSelfOnly(container);
1345    }
1346
1347    /**
1348     * Override to prevent thawing of any views created by the adapter.
1349     */
1350    @Override
1351    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1352        dispatchThawSelfOnly(container);
1353    }
1354
1355    /**
1356     * Adds a view to the animatingViews list.
1357     * mAnimatingViews holds the child views that are currently being kept around
1358     * purely for the purpose of being animated out of view. They are drawn as a regular
1359     * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
1360     * as they are managed separately from the regular child views.
1361     * @param viewHolder The ViewHolder to be removed
1362     */
1363    private void addAnimatingView(ViewHolder viewHolder) {
1364        final View view = viewHolder.itemView;
1365        final boolean alreadyParented = view.getParent() == this;
1366        mRecycler.unscrapView(getChildViewHolder(view));
1367        if (viewHolder.isTmpDetached()) {
1368            // re-attach
1369            mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
1370        } else if (!alreadyParented) {
1371            mChildHelper.addView(view, true);
1372        } else {
1373            mChildHelper.hide(view);
1374        }
1375    }
1376
1377    /**
1378     * Removes a view from the animatingViews list.
1379     * @param view The view to be removed
1380     * @see #addAnimatingView(RecyclerView.ViewHolder)
1381     * @return true if an animating view is removed
1382     */
1383    boolean removeAnimatingView(View view) {
1384        startInterceptRequestLayout();
1385        final boolean removed = mChildHelper.removeViewIfHidden(view);
1386        if (removed) {
1387            final ViewHolder viewHolder = getChildViewHolderInt(view);
1388            mRecycler.unscrapView(viewHolder);
1389            mRecycler.recycleViewHolderInternal(viewHolder);
1390            if (DEBUG) {
1391                Log.d(TAG, "after removing animated view: " + view + ", " + this);
1392            }
1393        }
1394        // only clear request eaten flag if we removed the view.
1395        stopInterceptRequestLayout(!removed);
1396        return removed;
1397    }
1398
1399    /**
1400     * Return the {@link LayoutManager} currently responsible for
1401     * layout policy for this RecyclerView.
1402     *
1403     * @return The currently bound LayoutManager
1404     */
1405    @Nullable
1406    public LayoutManager getLayoutManager() {
1407        return mLayout;
1408    }
1409
1410    /**
1411     * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
1412     * if no pool is set for this view a new one will be created. See
1413     * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
1414     *
1415     * @return The pool used to store recycled item views for reuse.
1416     * @see #setRecycledViewPool(RecycledViewPool)
1417     */
1418    @NonNull
1419    public RecycledViewPool getRecycledViewPool() {
1420        return mRecycler.getRecycledViewPool();
1421    }
1422
1423    /**
1424     * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
1425     * This can be useful if you have multiple RecyclerViews with adapters that use the same
1426     * view types, for example if you have several data sets with the same kinds of item views
1427     * displayed by a {@link ViewPager ViewPager}.
1428     *
1429     * @param pool Pool to set. If this parameter is null a new pool will be created and used.
1430     */
1431    public void setRecycledViewPool(@Nullable RecycledViewPool pool) {
1432        mRecycler.setRecycledViewPool(pool);
1433    }
1434
1435    /**
1436     * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
1437     *
1438     * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
1439     *
1440     * @see ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)
1441     */
1442    public void setViewCacheExtension(@Nullable ViewCacheExtension extension) {
1443        mRecycler.setViewCacheExtension(extension);
1444    }
1445
1446    /**
1447     * Set the number of offscreen views to retain before adding them to the potentially shared
1448     * {@link #getRecycledViewPool() recycled view pool}.
1449     *
1450     * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
1451     * a LayoutManager to reuse those views unmodified without needing to return to the adapter
1452     * to rebind them.</p>
1453     *
1454     * @param size Number of views to cache offscreen before returning them to the general
1455     *             recycled view pool
1456     */
1457    public void setItemViewCacheSize(int size) {
1458        mRecycler.setViewCacheSize(size);
1459    }
1460
1461    /**
1462     * Return the current scrolling state of the RecyclerView.
1463     *
1464     * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
1465     * {@link #SCROLL_STATE_SETTLING}
1466     */
1467    public int getScrollState() {
1468        return mScrollState;
1469    }
1470
1471    void setScrollState(int state) {
1472        if (state == mScrollState) {
1473            return;
1474        }
1475        if (DEBUG) {
1476            Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
1477                    new Exception());
1478        }
1479        mScrollState = state;
1480        if (state != SCROLL_STATE_SETTLING) {
1481            stopScrollersInternal();
1482        }
1483        dispatchOnScrollStateChanged(state);
1484    }
1485
1486    /**
1487     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1488     * affect both measurement and drawing of individual item views.
1489     *
1490     * <p>Item decorations are ordered. Decorations placed earlier in the list will
1491     * be run/queried/drawn first for their effects on item views. Padding added to views
1492     * will be nested; a padding added by an earlier decoration will mean further
1493     * item decorations in the list will be asked to draw/pad within the previous decoration's
1494     * given area.</p>
1495     *
1496     * @param decor Decoration to add
1497     * @param index Position in the decoration chain to insert this decoration at. If this value
1498     *              is negative the decoration will be added at the end.
1499     */
1500    public void addItemDecoration(@NonNull ItemDecoration decor, int index) {
1501        if (mLayout != null) {
1502            mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
1503                    + " layout");
1504        }
1505        if (mItemDecorations.isEmpty()) {
1506            setWillNotDraw(false);
1507        }
1508        if (index < 0) {
1509            mItemDecorations.add(decor);
1510        } else {
1511            mItemDecorations.add(index, decor);
1512        }
1513        markItemDecorInsetsDirty();
1514        requestLayout();
1515    }
1516
1517    /**
1518     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1519     * affect both measurement and drawing of individual item views.
1520     *
1521     * <p>Item decorations are ordered. Decorations placed earlier in the list will
1522     * be run/queried/drawn first for their effects on item views. Padding added to views
1523     * will be nested; a padding added by an earlier decoration will mean further
1524     * item decorations in the list will be asked to draw/pad within the previous decoration's
1525     * given area.</p>
1526     *
1527     * @param decor Decoration to add
1528     */
1529    public void addItemDecoration(@NonNull ItemDecoration decor) {
1530        addItemDecoration(decor, -1);
1531    }
1532
1533    /**
1534     * Returns an {@link ItemDecoration} previously added to this RecyclerView.
1535     *
1536     * @param index The index position of the desired ItemDecoration.
1537     * @return the ItemDecoration at index position
1538     * @throws IndexOutOfBoundsException on invalid index
1539     */
1540    @NonNull
1541    public ItemDecoration getItemDecorationAt(int index) {
1542        final int size = getItemDecorationCount();
1543        if (index < 0 || index >= size) {
1544            throw new IndexOutOfBoundsException(index + " is an invalid index for size " + size);
1545        }
1546
1547        return mItemDecorations.get(index);
1548    }
1549
1550    /**
1551     * Returns the number of {@link ItemDecoration} currently added to this RecyclerView.
1552     *
1553     * @return number of ItemDecorations currently added added to this RecyclerView.
1554     */
1555    public int getItemDecorationCount() {
1556        return mItemDecorations.size();
1557    }
1558
1559    /**
1560     * Removes the {@link ItemDecoration} associated with the supplied index position.
1561     *
1562     * @param index The index position of the ItemDecoration to be removed.
1563     */
1564    public void removeItemDecorationAt(int index) {
1565        final int size = getItemDecorationCount();
1566        if (index < 0 || index >= size) {
1567            throw new IndexOutOfBoundsException(index + " is an invalid index for size " + size);
1568        }
1569
1570        removeItemDecoration(getItemDecorationAt(index));
1571    }
1572
1573    /**
1574     * Remove an {@link ItemDecoration} from this RecyclerView.
1575     *
1576     * <p>The given decoration will no longer impact the measurement and drawing of
1577     * item views.</p>
1578     *
1579     * @param decor Decoration to remove
1580     * @see #addItemDecoration(ItemDecoration)
1581     */
1582    public void removeItemDecoration(@NonNull ItemDecoration decor) {
1583        if (mLayout != null) {
1584            mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
1585                    + " layout");
1586        }
1587        mItemDecorations.remove(decor);
1588        if (mItemDecorations.isEmpty()) {
1589            setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
1590        }
1591        markItemDecorInsetsDirty();
1592        requestLayout();
1593    }
1594
1595    /**
1596     * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
1597     * <p>
1598     * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
1599     * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
1600     * true if childDrawingOrderCallback is not null, false otherwise.
1601     * <p>
1602     * Note that child drawing order may be overridden by View's elevation.
1603     *
1604     * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
1605     *                                  system.
1606     */
1607    public void setChildDrawingOrderCallback(
1608            @Nullable ChildDrawingOrderCallback childDrawingOrderCallback) {
1609        if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
1610            return;
1611        }
1612        mChildDrawingOrderCallback = childDrawingOrderCallback;
1613        setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
1614    }
1615
1616    /**
1617     * Set a listener that will be notified of any changes in scroll state or position.
1618     *
1619     * @param listener Listener to set or null to clear
1620     *
1621     * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
1622     *             {@link #removeOnScrollListener(OnScrollListener)}
1623     */
1624    @Deprecated
1625    public void setOnScrollListener(@Nullable OnScrollListener listener) {
1626        mScrollListener = listener;
1627    }
1628
1629    /**
1630     * Add a listener that will be notified of any changes in scroll state or position.
1631     *
1632     * <p>Components that add a listener should take care to remove it when finished.
1633     * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
1634     * to remove all attached listeners.</p>
1635     *
1636     * @param listener listener to set
1637     */
1638    public void addOnScrollListener(@NonNull OnScrollListener listener) {
1639        if (mScrollListeners == null) {
1640            mScrollListeners = new ArrayList<>();
1641        }
1642        mScrollListeners.add(listener);
1643    }
1644
1645    /**
1646     * Remove a listener that was notified of any changes in scroll state or position.
1647     *
1648     * @param listener listener to set or null to clear
1649     */
1650    public void removeOnScrollListener(@NonNull OnScrollListener listener) {
1651        if (mScrollListeners != null) {
1652            mScrollListeners.remove(listener);
1653        }
1654    }
1655
1656    /**
1657     * Remove all secondary listener that were notified of any changes in scroll state or position.
1658     */
1659    public void clearOnScrollListeners() {
1660        if (mScrollListeners != null) {
1661            mScrollListeners.clear();
1662        }
1663    }
1664
1665    /**
1666     * Convenience method to scroll to a certain position.
1667     *
1668     * RecyclerView does not implement scrolling logic, rather forwards the call to
1669     * {@link RecyclerView.LayoutManager#scrollToPosition(int)}
1670     * @param position Scroll to this adapter position
1671     * @see RecyclerView.LayoutManager#scrollToPosition(int)
1672     */
1673    public void scrollToPosition(int position) {
1674        if (mLayoutFrozen) {
1675            return;
1676        }
1677        stopScroll();
1678        if (mLayout == null) {
1679            Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
1680                    + "Call setLayoutManager with a non-null argument.");
1681            return;
1682        }
1683        mLayout.scrollToPosition(position);
1684        awakenScrollBars();
1685    }
1686
1687    void jumpToPositionForSmoothScroller(int position) {
1688        if (mLayout == null) {
1689            return;
1690        }
1691        mLayout.scrollToPosition(position);
1692        awakenScrollBars();
1693    }
1694
1695    /**
1696     * Starts a smooth scroll to an adapter position.
1697     * <p>
1698     * To support smooth scrolling, you must override
1699     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
1700     * {@link SmoothScroller}.
1701     * <p>
1702     * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
1703     * provide a custom smooth scroll logic, override
1704     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
1705     * LayoutManager.
1706     *
1707     * @param position The adapter position to scroll to
1708     * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
1709     */
1710    public void smoothScrollToPosition(int position) {
1711        if (mLayoutFrozen) {
1712            return;
1713        }
1714        if (mLayout == null) {
1715            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
1716                    + "Call setLayoutManager with a non-null argument.");
1717            return;
1718        }
1719        mLayout.smoothScrollToPosition(this, mState, position);
1720    }
1721
1722    @Override
1723    public void scrollTo(int x, int y) {
1724        Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
1725                + "Use scrollToPosition instead");
1726    }
1727
1728    @Override
1729    public void scrollBy(int x, int y) {
1730        if (mLayout == null) {
1731            Log.e(TAG, "Cannot scroll without a LayoutManager set. "
1732                    + "Call setLayoutManager with a non-null argument.");
1733            return;
1734        }
1735        if (mLayoutFrozen) {
1736            return;
1737        }
1738        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1739        final boolean canScrollVertical = mLayout.canScrollVertically();
1740        if (canScrollHorizontal || canScrollVertical) {
1741            scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
1742        }
1743    }
1744
1745    /**
1746     * Scrolls the RV by 'dx' and 'dy' via calls to
1747     * {@link LayoutManager#scrollHorizontallyBy(int, Recycler, State)} and
1748     * {@link LayoutManager#scrollVerticallyBy(int, Recycler, State)}.
1749     *
1750     * Also sets how much of the scroll was actually consumed in 'consumed' parameter (indexes 0 and
1751     * 1 for the x axis and y axis, respectively).
1752     *
1753     * This method should only be called in the context of an existing scroll operation such that
1754     * any other necessary operations (such as a call to {@link #consumePendingUpdateOperations()})
1755     * is already handled.
1756     */
1757    private void scrollStep(int dx, int dy, @Nullable int[] consumed) {
1758        startInterceptRequestLayout();
1759        onEnterLayoutOrScroll();
1760
1761        TraceCompat.beginSection(TRACE_SCROLL_TAG);
1762        fillRemainingScrollValues(mState);
1763
1764        int consumedX = 0;
1765        int consumedY = 0;
1766        if (dx != 0) {
1767            consumedX = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
1768        }
1769        if (dy != 0) {
1770            consumedY = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
1771        }
1772
1773        TraceCompat.endSection();
1774        repositionShadowingViews();
1775
1776        onExitLayoutOrScroll();
1777        stopInterceptRequestLayout(false);
1778
1779        if (consumed != null) {
1780            consumed[0] = consumedX;
1781            consumed[1] = consumedY;
1782        }
1783    }
1784
1785    /**
1786     * Helper method reflect data changes to the state.
1787     * <p>
1788     * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
1789     * but data actually changed.
1790     * <p>
1791     * This method consumes all deferred changes to avoid that case.
1792     */
1793    void consumePendingUpdateOperations() {
1794        if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
1795            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1796            dispatchLayout();
1797            TraceCompat.endSection();
1798            return;
1799        }
1800        if (!mAdapterHelper.hasPendingUpdates()) {
1801            return;
1802        }
1803
1804        // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
1805        // of the visible items is affected and if not, just ignore the change.
1806        if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
1807                .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
1808                        | AdapterHelper.UpdateOp.MOVE)) {
1809            TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
1810            startInterceptRequestLayout();
1811            onEnterLayoutOrScroll();
1812            mAdapterHelper.preProcess();
1813            if (!mLayoutWasDefered) {
1814                if (hasUpdatedView()) {
1815                    dispatchLayout();
1816                } else {
1817                    // no need to layout, clean state
1818                    mAdapterHelper.consumePostponedUpdates();
1819                }
1820            }
1821            stopInterceptRequestLayout(true);
1822            onExitLayoutOrScroll();
1823            TraceCompat.endSection();
1824        } else if (mAdapterHelper.hasPendingUpdates()) {
1825            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1826            dispatchLayout();
1827            TraceCompat.endSection();
1828        }
1829    }
1830
1831    /**
1832     * @return True if an existing view holder needs to be updated
1833     */
1834    private boolean hasUpdatedView() {
1835        final int childCount = mChildHelper.getChildCount();
1836        for (int i = 0; i < childCount; i++) {
1837            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
1838            if (holder == null || holder.shouldIgnore()) {
1839                continue;
1840            }
1841            if (holder.isUpdated()) {
1842                return true;
1843            }
1844        }
1845        return false;
1846    }
1847
1848    /**
1849     * Does not perform bounds checking. Used by internal methods that have already validated input.
1850     * <p>
1851     * It also reports any unused scroll request to the related EdgeEffect.
1852     *
1853     * @param x The amount of horizontal scroll request
1854     * @param y The amount of vertical scroll request
1855     * @param ev The originating MotionEvent, or null if not from a touch event.
1856     *
1857     * @return Whether any scroll was consumed in either direction.
1858     */
1859    boolean scrollByInternal(int x, int y, MotionEvent ev) {
1860        int unconsumedX = 0, unconsumedY = 0;
1861        int consumedX = 0, consumedY = 0;
1862
1863        consumePendingUpdateOperations();
1864        if (mAdapter != null) {
1865            scrollStep(x, y, mScrollStepConsumed);
1866            consumedX = mScrollStepConsumed[0];
1867            consumedY = mScrollStepConsumed[1];
1868            unconsumedX = x - consumedX;
1869            unconsumedY = y - consumedY;
1870        }
1871        if (!mItemDecorations.isEmpty()) {
1872            invalidate();
1873        }
1874
1875        if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset,
1876                TYPE_TOUCH)) {
1877            // Update the last touch co-ords, taking any scroll offset into account
1878            mLastTouchX -= mScrollOffset[0];
1879            mLastTouchY -= mScrollOffset[1];
1880            if (ev != null) {
1881                ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
1882            }
1883            mNestedOffsets[0] += mScrollOffset[0];
1884            mNestedOffsets[1] += mScrollOffset[1];
1885        } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
1886            if (ev != null && !MotionEventCompat.isFromSource(ev, InputDevice.SOURCE_MOUSE)) {
1887                pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
1888            }
1889            considerReleasingGlowsOnScroll(x, y);
1890        }
1891        if (consumedX != 0 || consumedY != 0) {
1892            dispatchOnScrolled(consumedX, consumedY);
1893        }
1894        if (!awakenScrollBars()) {
1895            invalidate();
1896        }
1897        return consumedX != 0 || consumedY != 0;
1898    }
1899
1900    /**
1901     * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
1902     * range. This value is used to compute the length of the thumb within the scrollbar's track.
1903     * </p>
1904     *
1905     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1906     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
1907     *
1908     * <p>Default implementation returns 0.</p>
1909     *
1910     * <p>If you want to support scroll bars, override
1911     * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
1912     * LayoutManager. </p>
1913     *
1914     * @return The horizontal offset of the scrollbar's thumb
1915     * @see RecyclerView.LayoutManager#computeHorizontalScrollOffset
1916     * (RecyclerView.State)
1917     */
1918    @Override
1919    public int computeHorizontalScrollOffset() {
1920        if (mLayout == null) {
1921            return 0;
1922        }
1923        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
1924    }
1925
1926    /**
1927     * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
1928     * horizontal range. This value is used to compute the length of the thumb within the
1929     * scrollbar's track.</p>
1930     *
1931     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1932     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
1933     *
1934     * <p>Default implementation returns 0.</p>
1935     *
1936     * <p>If you want to support scroll bars, override
1937     * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
1938     * LayoutManager.</p>
1939     *
1940     * @return The horizontal extent of the scrollbar's thumb
1941     * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
1942     */
1943    @Override
1944    public int computeHorizontalScrollExtent() {
1945        if (mLayout == null) {
1946            return 0;
1947        }
1948        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
1949    }
1950
1951    /**
1952     * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
1953     *
1954     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1955     * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
1956     *
1957     * <p>Default implementation returns 0.</p>
1958     *
1959     * <p>If you want to support scroll bars, override
1960     * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
1961     * LayoutManager.</p>
1962     *
1963     * @return The total horizontal range represented by the vertical scrollbar
1964     * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
1965     */
1966    @Override
1967    public int computeHorizontalScrollRange() {
1968        if (mLayout == null) {
1969            return 0;
1970        }
1971        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
1972    }
1973
1974    /**
1975     * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
1976     * This value is used to compute the length of the thumb within the scrollbar's track. </p>
1977     *
1978     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1979     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
1980     *
1981     * <p>Default implementation returns 0.</p>
1982     *
1983     * <p>If you want to support scroll bars, override
1984     * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
1985     * LayoutManager.</p>
1986     *
1987     * @return The vertical offset of the scrollbar's thumb
1988     * @see RecyclerView.LayoutManager#computeVerticalScrollOffset
1989     * (RecyclerView.State)
1990     */
1991    @Override
1992    public int computeVerticalScrollOffset() {
1993        if (mLayout == null) {
1994            return 0;
1995        }
1996        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
1997    }
1998
1999    /**
2000     * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
2001     * This value is used to compute the length of the thumb within the scrollbar's track.</p>
2002     *
2003     * <p>The range is expressed in arbitrary units that must be the same as the units used by
2004     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
2005     *
2006     * <p>Default implementation returns 0.</p>
2007     *
2008     * <p>If you want to support scroll bars, override
2009     * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
2010     * LayoutManager.</p>
2011     *
2012     * @return The vertical extent of the scrollbar's thumb
2013     * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
2014     */
2015    @Override
2016    public int computeVerticalScrollExtent() {
2017        if (mLayout == null) {
2018            return 0;
2019        }
2020        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
2021    }
2022
2023    /**
2024     * <p>Compute the vertical range that the vertical scrollbar represents.</p>
2025     *
2026     * <p>The range is expressed in arbitrary units that must be the same as the units used by
2027     * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
2028     *
2029     * <p>Default implementation returns 0.</p>
2030     *
2031     * <p>If you want to support scroll bars, override
2032     * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
2033     * LayoutManager.</p>
2034     *
2035     * @return The total vertical range represented by the vertical scrollbar
2036     * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
2037     */
2038    @Override
2039    public int computeVerticalScrollRange() {
2040        if (mLayout == null) {
2041            return 0;
2042        }
2043        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
2044    }
2045
2046    /**
2047     * This method should be called before any code that may trigger a child view to cause a call to
2048     * {@link RecyclerView#requestLayout()}.  Doing so enables {@link RecyclerView} to avoid
2049     * reacting to additional redundant calls to {@link #requestLayout()}.
2050     * <p>
2051     * A call to this method must always be accompanied by a call to
2052     * {@link #stopInterceptRequestLayout(boolean)} that follows the code that may trigger a
2053     * child View to cause a call to {@link RecyclerView#requestLayout()}.
2054     *
2055     * @see #stopInterceptRequestLayout(boolean)
2056     */
2057    void startInterceptRequestLayout() {
2058        mInterceptRequestLayoutDepth++;
2059        if (mInterceptRequestLayoutDepth == 1 && !mLayoutFrozen) {
2060            mLayoutWasDefered = false;
2061        }
2062    }
2063
2064    /**
2065     * This method should be called after any code that may trigger a child view to cause a call to
2066     * {@link RecyclerView#requestLayout()}.
2067     * <p>
2068     * A call to this method must always be accompanied by a call to
2069     * {@link #startInterceptRequestLayout()} that precedes the code that may trigger a child
2070     * View to cause a call to {@link RecyclerView#requestLayout()}.
2071     *
2072     * @see #startInterceptRequestLayout()
2073     */
2074    void stopInterceptRequestLayout(boolean performLayoutChildren) {
2075        if (mInterceptRequestLayoutDepth < 1) {
2076            //noinspection PointlessBooleanExpression
2077            if (DEBUG) {
2078                throw new IllegalStateException("stopInterceptRequestLayout was called more "
2079                        + "times than startInterceptRequestLayout."
2080                        + exceptionLabel());
2081            }
2082            mInterceptRequestLayoutDepth = 1;
2083        }
2084        if (!performLayoutChildren && !mLayoutFrozen) {
2085            // Reset the layout request eaten counter.
2086            // This is necessary since eatRequest calls can be nested in which case the other
2087            // call will override the inner one.
2088            // for instance:
2089            // eat layout for process adapter updates
2090            //   eat layout for dispatchLayout
2091            //     a bunch of req layout calls arrive
2092
2093            mLayoutWasDefered = false;
2094        }
2095        if (mInterceptRequestLayoutDepth == 1) {
2096            // when layout is frozen we should delay dispatchLayout()
2097            if (performLayoutChildren && mLayoutWasDefered && !mLayoutFrozen
2098                    && mLayout != null && mAdapter != null) {
2099                dispatchLayout();
2100            }
2101            if (!mLayoutFrozen) {
2102                mLayoutWasDefered = false;
2103            }
2104        }
2105        mInterceptRequestLayoutDepth--;
2106    }
2107
2108    /**
2109     * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
2110     * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
2111     * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
2112     * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
2113     * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
2114     * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
2115     * called.
2116     *
2117     * <p>
2118     * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
2119     * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
2120     * RecyclerView, State, int)}.
2121     * <p>
2122     * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
2123     * stop frozen.
2124     * <p>
2125     * Note: Running ItemAnimator is not stopped automatically,  it's caller's
2126     * responsibility to call ItemAnimator.end().
2127     *
2128     * @param frozen   true to freeze layout and scroll, false to re-enable.
2129     */
2130    public void setLayoutFrozen(boolean frozen) {
2131        if (frozen != mLayoutFrozen) {
2132            assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
2133            if (!frozen) {
2134                mLayoutFrozen = false;
2135                if (mLayoutWasDefered && mLayout != null && mAdapter != null) {
2136                    requestLayout();
2137                }
2138                mLayoutWasDefered = false;
2139            } else {
2140                final long now = SystemClock.uptimeMillis();
2141                MotionEvent cancelEvent = MotionEvent.obtain(now, now,
2142                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2143                onTouchEvent(cancelEvent);
2144                mLayoutFrozen = true;
2145                mIgnoreMotionEventTillDown = true;
2146                stopScroll();
2147            }
2148        }
2149    }
2150
2151    /**
2152     * Returns true if layout and scroll are frozen.
2153     *
2154     * @return true if layout and scroll are frozen
2155     * @see #setLayoutFrozen(boolean)
2156     */
2157    public boolean isLayoutFrozen() {
2158        return mLayoutFrozen;
2159    }
2160
2161    /**
2162     * Animate a scroll by the given amount of pixels along either axis.
2163     *
2164     * @param dx Pixels to scroll horizontally
2165     * @param dy Pixels to scroll vertically
2166     */
2167    public void smoothScrollBy(@Px int dx, @Px int dy) {
2168        smoothScrollBy(dx, dy, null);
2169    }
2170
2171    /**
2172     * Animate a scroll by the given amount of pixels along either axis.
2173     *
2174     * @param dx Pixels to scroll horizontally
2175     * @param dy Pixels to scroll vertically
2176     * @param interpolator {@link Interpolator} to be used for scrolling. If it is
2177     *                     {@code null}, RecyclerView is going to use the default interpolator.
2178     */
2179    public void smoothScrollBy(@Px int dx, @Px int dy, @Nullable Interpolator interpolator) {
2180        if (mLayout == null) {
2181            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
2182                    + "Call setLayoutManager with a non-null argument.");
2183            return;
2184        }
2185        if (mLayoutFrozen) {
2186            return;
2187        }
2188        if (!mLayout.canScrollHorizontally()) {
2189            dx = 0;
2190        }
2191        if (!mLayout.canScrollVertically()) {
2192            dy = 0;
2193        }
2194        if (dx != 0 || dy != 0) {
2195            mViewFlinger.smoothScrollBy(dx, dy, interpolator);
2196        }
2197    }
2198
2199    /**
2200     * Begin a standard fling with an initial velocity along each axis in pixels per second.
2201     * If the velocity given is below the system-defined minimum this method will return false
2202     * and no fling will occur.
2203     *
2204     * @param velocityX Initial horizontal velocity in pixels per second
2205     * @param velocityY Initial vertical velocity in pixels per second
2206     * @return true if the fling was started, false if the velocity was too low to fling or
2207     * LayoutManager does not support scrolling in the axis fling is issued.
2208     *
2209     * @see LayoutManager#canScrollVertically()
2210     * @see LayoutManager#canScrollHorizontally()
2211     */
2212    public boolean fling(int velocityX, int velocityY) {
2213        if (mLayout == null) {
2214            Log.e(TAG, "Cannot fling without a LayoutManager set. "
2215                    + "Call setLayoutManager with a non-null argument.");
2216            return false;
2217        }
2218        if (mLayoutFrozen) {
2219            return false;
2220        }
2221
2222        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
2223        final boolean canScrollVertical = mLayout.canScrollVertically();
2224
2225        if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
2226            velocityX = 0;
2227        }
2228        if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
2229            velocityY = 0;
2230        }
2231        if (velocityX == 0 && velocityY == 0) {
2232            // If we don't have any velocity, return false
2233            return false;
2234        }
2235
2236        if (!dispatchNestedPreFling(velocityX, velocityY)) {
2237            final boolean canScroll = canScrollHorizontal || canScrollVertical;
2238            dispatchNestedFling(velocityX, velocityY, canScroll);
2239
2240            if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
2241                return true;
2242            }
2243
2244            if (canScroll) {
2245                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2246                if (canScrollHorizontal) {
2247                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2248                }
2249                if (canScrollVertical) {
2250                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2251                }
2252                startNestedScroll(nestedScrollAxis, TYPE_NON_TOUCH);
2253
2254                velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
2255                velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
2256                mViewFlinger.fling(velocityX, velocityY);
2257                return true;
2258            }
2259        }
2260        return false;
2261    }
2262
2263    /**
2264     * Stop any current scroll in progress, such as one started by
2265     * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
2266     */
2267    public void stopScroll() {
2268        setScrollState(SCROLL_STATE_IDLE);
2269        stopScrollersInternal();
2270    }
2271
2272    /**
2273     * Similar to {@link #stopScroll()} but does not set the state.
2274     */
2275    private void stopScrollersInternal() {
2276        mViewFlinger.stop();
2277        if (mLayout != null) {
2278            mLayout.stopSmoothScroller();
2279        }
2280    }
2281
2282    /**
2283     * Returns the minimum velocity to start a fling.
2284     *
2285     * @return The minimum velocity to start a fling
2286     */
2287    public int getMinFlingVelocity() {
2288        return mMinFlingVelocity;
2289    }
2290
2291
2292    /**
2293     * Returns the maximum fling velocity used by this RecyclerView.
2294     *
2295     * @return The maximum fling velocity used by this RecyclerView.
2296     */
2297    public int getMaxFlingVelocity() {
2298        return mMaxFlingVelocity;
2299    }
2300
2301    /**
2302     * Apply a pull to relevant overscroll glow effects
2303     */
2304    private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
2305        boolean invalidate = false;
2306        if (overscrollX < 0) {
2307            ensureLeftGlow();
2308            EdgeEffectCompat.onPull(mLeftGlow, -overscrollX / getWidth(), 1f - y  / getHeight());
2309            invalidate = true;
2310        } else if (overscrollX > 0) {
2311            ensureRightGlow();
2312            EdgeEffectCompat.onPull(mRightGlow, overscrollX / getWidth(), y / getHeight());
2313            invalidate = true;
2314        }
2315
2316        if (overscrollY < 0) {
2317            ensureTopGlow();
2318            EdgeEffectCompat.onPull(mTopGlow, -overscrollY / getHeight(), x / getWidth());
2319            invalidate = true;
2320        } else if (overscrollY > 0) {
2321            ensureBottomGlow();
2322            EdgeEffectCompat.onPull(mBottomGlow, overscrollY / getHeight(), 1f - x / getWidth());
2323            invalidate = true;
2324        }
2325
2326        if (invalidate || overscrollX != 0 || overscrollY != 0) {
2327            ViewCompat.postInvalidateOnAnimation(this);
2328        }
2329    }
2330
2331    private void releaseGlows() {
2332        boolean needsInvalidate = false;
2333        if (mLeftGlow != null) {
2334            mLeftGlow.onRelease();
2335            needsInvalidate = mLeftGlow.isFinished();
2336        }
2337        if (mTopGlow != null) {
2338            mTopGlow.onRelease();
2339            needsInvalidate |= mTopGlow.isFinished();
2340        }
2341        if (mRightGlow != null) {
2342            mRightGlow.onRelease();
2343            needsInvalidate |= mRightGlow.isFinished();
2344        }
2345        if (mBottomGlow != null) {
2346            mBottomGlow.onRelease();
2347            needsInvalidate |= mBottomGlow.isFinished();
2348        }
2349        if (needsInvalidate) {
2350            ViewCompat.postInvalidateOnAnimation(this);
2351        }
2352    }
2353
2354    void considerReleasingGlowsOnScroll(int dx, int dy) {
2355        boolean needsInvalidate = false;
2356        if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
2357            mLeftGlow.onRelease();
2358            needsInvalidate = mLeftGlow.isFinished();
2359        }
2360        if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
2361            mRightGlow.onRelease();
2362            needsInvalidate |= mRightGlow.isFinished();
2363        }
2364        if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
2365            mTopGlow.onRelease();
2366            needsInvalidate |= mTopGlow.isFinished();
2367        }
2368        if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
2369            mBottomGlow.onRelease();
2370            needsInvalidate |= mBottomGlow.isFinished();
2371        }
2372        if (needsInvalidate) {
2373            ViewCompat.postInvalidateOnAnimation(this);
2374        }
2375    }
2376
2377    void absorbGlows(int velocityX, int velocityY) {
2378        if (velocityX < 0) {
2379            ensureLeftGlow();
2380            mLeftGlow.onAbsorb(-velocityX);
2381        } else if (velocityX > 0) {
2382            ensureRightGlow();
2383            mRightGlow.onAbsorb(velocityX);
2384        }
2385
2386        if (velocityY < 0) {
2387            ensureTopGlow();
2388            mTopGlow.onAbsorb(-velocityY);
2389        } else if (velocityY > 0) {
2390            ensureBottomGlow();
2391            mBottomGlow.onAbsorb(velocityY);
2392        }
2393
2394        if (velocityX != 0 || velocityY != 0) {
2395            ViewCompat.postInvalidateOnAnimation(this);
2396        }
2397    }
2398
2399    void ensureLeftGlow() {
2400        if (mLeftGlow != null) {
2401            return;
2402        }
2403        mLeftGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_LEFT);
2404        if (mClipToPadding) {
2405            mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2406                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2407        } else {
2408            mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2409        }
2410    }
2411
2412    void ensureRightGlow() {
2413        if (mRightGlow != null) {
2414            return;
2415        }
2416        mRightGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_RIGHT);
2417        if (mClipToPadding) {
2418            mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2419                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2420        } else {
2421            mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2422        }
2423    }
2424
2425    void ensureTopGlow() {
2426        if (mTopGlow != null) {
2427            return;
2428        }
2429        mTopGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_TOP);
2430        if (mClipToPadding) {
2431            mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2432                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2433        } else {
2434            mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2435        }
2436
2437    }
2438
2439    void ensureBottomGlow() {
2440        if (mBottomGlow != null) {
2441            return;
2442        }
2443        mBottomGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_BOTTOM);
2444        if (mClipToPadding) {
2445            mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2446                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2447        } else {
2448            mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2449        }
2450    }
2451
2452    void invalidateGlows() {
2453        mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
2454    }
2455
2456    /**
2457     * Set a {@link EdgeEffectFactory} for this {@link RecyclerView}.
2458     * <p>
2459     * When a new {@link EdgeEffectFactory} is set, any existing over-scroll effects are cleared
2460     * and new effects are created as needed using
2461     * {@link EdgeEffectFactory#createEdgeEffect(RecyclerView, int)}
2462     *
2463     * @param edgeEffectFactory The {@link EdgeEffectFactory} instance.
2464     */
2465    public void setEdgeEffectFactory(@NonNull EdgeEffectFactory edgeEffectFactory) {
2466        Preconditions.checkNotNull(edgeEffectFactory);
2467        mEdgeEffectFactory = edgeEffectFactory;
2468        invalidateGlows();
2469    }
2470
2471    /**
2472     * Retrieves the previously set {@link EdgeEffectFactory} or the default factory if nothing
2473     * was set.
2474     *
2475     * @return The previously set {@link EdgeEffectFactory}
2476     * @see #setEdgeEffectFactory(EdgeEffectFactory)
2477     */
2478    @NonNull
2479    public EdgeEffectFactory getEdgeEffectFactory() {
2480        return mEdgeEffectFactory;
2481    }
2482
2483    /**
2484     * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
2485     * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
2486     * that differs from other ViewGroups.
2487     * <p>
2488     * It first does a focus search within the RecyclerView. If this search finds a View that is in
2489     * the focus direction with respect to the currently focused View, RecyclerView returns that
2490     * child as the next focus target. When it cannot find such child, it calls
2491     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
2492     * in the focus search direction. If LayoutManager adds a View that matches the
2493     * focus search criteria, it will be returned as the focus search result. Otherwise,
2494     * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
2495     * <p>
2496     * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
2497     * is not in the focus direction is still valid focus target which may not be the desired
2498     * behavior if the Adapter has more children in the focus direction. To handle this case,
2499     * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
2500     * focus search in that direction. If there are no Views to gain focus, it will call
2501     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
2502     * focus search with the original (relative) direction. This allows RecyclerView to provide
2503     * better candidates to the focus search while still allowing the view system to take focus from
2504     * the RecyclerView and give it to a more suitable child if such child exists.
2505     *
2506     * @param focused The view that currently has focus
2507     * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
2508     * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
2509     * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
2510     *
2511     * @return A new View that can be the next focus after the focused View
2512     */
2513    @Override
2514    public View focusSearch(View focused, int direction) {
2515        View result = mLayout.onInterceptFocusSearch(focused, direction);
2516        if (result != null) {
2517            return result;
2518        }
2519        final boolean canRunFocusFailure = mAdapter != null && mLayout != null
2520                && !isComputingLayout() && !mLayoutFrozen;
2521
2522        final FocusFinder ff = FocusFinder.getInstance();
2523        if (canRunFocusFailure
2524                && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
2525            // convert direction to absolute direction and see if we have a view there and if not
2526            // tell LayoutManager to add if it can.
2527            boolean needsFocusFailureLayout = false;
2528            if (mLayout.canScrollVertically()) {
2529                final int absDir =
2530                        direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
2531                final View found = ff.findNextFocus(this, focused, absDir);
2532                needsFocusFailureLayout = found == null;
2533                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2534                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2535                    direction = absDir;
2536                }
2537            }
2538            if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
2539                boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
2540                final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
2541                        ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2542                final View found = ff.findNextFocus(this, focused, absDir);
2543                needsFocusFailureLayout = found == null;
2544                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2545                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2546                    direction = absDir;
2547                }
2548            }
2549            if (needsFocusFailureLayout) {
2550                consumePendingUpdateOperations();
2551                final View focusedItemView = findContainingItemView(focused);
2552                if (focusedItemView == null) {
2553                    // panic, focused view is not a child anymore, cannot call super.
2554                    return null;
2555                }
2556                startInterceptRequestLayout();
2557                mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2558                stopInterceptRequestLayout(false);
2559            }
2560            result = ff.findNextFocus(this, focused, direction);
2561        } else {
2562            result = ff.findNextFocus(this, focused, direction);
2563            if (result == null && canRunFocusFailure) {
2564                consumePendingUpdateOperations();
2565                final View focusedItemView = findContainingItemView(focused);
2566                if (focusedItemView == null) {
2567                    // panic, focused view is not a child anymore, cannot call super.
2568                    return null;
2569                }
2570                startInterceptRequestLayout();
2571                result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2572                stopInterceptRequestLayout(false);
2573            }
2574        }
2575        if (result != null && !result.hasFocusable()) {
2576            if (getFocusedChild() == null) {
2577                // Scrolling to this unfocusable view is not meaningful since there is no currently
2578                // focused view which RV needs to keep visible.
2579                return super.focusSearch(focused, direction);
2580            }
2581            // If the next view returned by onFocusSearchFailed in layout manager has no focusable
2582            // views, we still scroll to that view in order to make it visible on the screen.
2583            // If it's focusable, framework already calls RV's requestChildFocus which handles
2584            // bringing this newly focused item onto the screen.
2585            requestChildOnScreen(result, null);
2586            return focused;
2587        }
2588        return isPreferredNextFocus(focused, result, direction)
2589                ? result : super.focusSearch(focused, direction);
2590    }
2591
2592    /**
2593     * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
2594     * assign it as the next focus View instead of letting view hierarchy decide.
2595     * A good candidate means a View that is aligned in the focus direction wrt the focused View
2596     * and is not the RecyclerView itself.
2597     * When this method returns false, RecyclerView will let the parent make the decision so the
2598     * same View may still get the focus as a result of that search.
2599     */
2600    private boolean isPreferredNextFocus(View focused, View next, int direction) {
2601        if (next == null || next == this) {
2602            return false;
2603        }
2604        // panic, result view is not a child anymore, maybe workaround b/37864393
2605        if (findContainingItemView(next) == null) {
2606            return false;
2607        }
2608        if (focused == null) {
2609            return true;
2610        }
2611        // panic, focused view is not a child anymore, maybe workaround b/37864393
2612        if (findContainingItemView(focused) == null) {
2613            return true;
2614        }
2615
2616        mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2617        mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
2618        offsetDescendantRectToMyCoords(focused, mTempRect);
2619        offsetDescendantRectToMyCoords(next, mTempRect2);
2620        final int rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL ? -1 : 1;
2621        int rightness = 0;
2622        if ((mTempRect.left < mTempRect2.left
2623                || mTempRect.right <= mTempRect2.left)
2624                && mTempRect.right < mTempRect2.right) {
2625            rightness = 1;
2626        } else if ((mTempRect.right > mTempRect2.right
2627                || mTempRect.left >= mTempRect2.right)
2628                && mTempRect.left > mTempRect2.left) {
2629            rightness = -1;
2630        }
2631        int downness = 0;
2632        if ((mTempRect.top < mTempRect2.top
2633                || mTempRect.bottom <= mTempRect2.top)
2634                && mTempRect.bottom < mTempRect2.bottom) {
2635            downness = 1;
2636        } else if ((mTempRect.bottom > mTempRect2.bottom
2637                || mTempRect.top >= mTempRect2.bottom)
2638                && mTempRect.top > mTempRect2.top) {
2639            downness = -1;
2640        }
2641        switch (direction) {
2642            case View.FOCUS_LEFT:
2643                return rightness < 0;
2644            case View.FOCUS_RIGHT:
2645                return rightness > 0;
2646            case View.FOCUS_UP:
2647                return downness < 0;
2648            case View.FOCUS_DOWN:
2649                return downness > 0;
2650            case View.FOCUS_FORWARD:
2651                return downness > 0 || (downness == 0 && rightness * rtl >= 0);
2652            case View.FOCUS_BACKWARD:
2653                return downness < 0 || (downness == 0 && rightness * rtl <= 0);
2654        }
2655        throw new IllegalArgumentException("Invalid direction: " + direction + exceptionLabel());
2656    }
2657
2658    @Override
2659    public void requestChildFocus(View child, View focused) {
2660        if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
2661            requestChildOnScreen(child, focused);
2662        }
2663        super.requestChildFocus(child, focused);
2664    }
2665
2666    /**
2667     * Requests that the given child of the RecyclerView be positioned onto the screen. This method
2668     * can be called for both unfocusable and focusable child views. For unfocusable child views,
2669     * the {@param focused} parameter passed is null, whereas for a focusable child, this parameter
2670     * indicates the actual descendant view within this child view that holds the focus.
2671     * @param child The child view of this RecyclerView that wants to come onto the screen.
2672     * @param focused The descendant view that actually has the focus if child is focusable, null
2673     *                otherwise.
2674     */
2675    private void requestChildOnScreen(@NonNull View child, @Nullable View focused) {
2676        View rectView = (focused != null) ? focused : child;
2677        mTempRect.set(0, 0, rectView.getWidth(), rectView.getHeight());
2678
2679        // get item decor offsets w/o refreshing. If they are invalid, there will be another
2680        // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
2681        // View in viewport.
2682        final ViewGroup.LayoutParams focusedLayoutParams = rectView.getLayoutParams();
2683        if (focusedLayoutParams instanceof LayoutParams) {
2684            // if focused child has item decors, use them. Otherwise, ignore.
2685            final LayoutParams lp = (LayoutParams) focusedLayoutParams;
2686            if (!lp.mInsetsDirty) {
2687                final Rect insets = lp.mDecorInsets;
2688                mTempRect.left -= insets.left;
2689                mTempRect.right += insets.right;
2690                mTempRect.top -= insets.top;
2691                mTempRect.bottom += insets.bottom;
2692            }
2693        }
2694
2695        if (focused != null) {
2696            offsetDescendantRectToMyCoords(focused, mTempRect);
2697            offsetRectIntoDescendantCoords(child, mTempRect);
2698        }
2699        mLayout.requestChildRectangleOnScreen(this, child, mTempRect, !mFirstLayoutComplete,
2700                (focused == null));
2701    }
2702
2703    @Override
2704    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
2705        return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
2706    }
2707
2708    @Override
2709    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
2710        if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
2711            super.addFocusables(views, direction, focusableMode);
2712        }
2713    }
2714
2715    @Override
2716    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
2717        if (isComputingLayout()) {
2718            // if we are in the middle of a layout calculation, don't let any child take focus.
2719            // RV will handle it after layout calculation is finished.
2720            return false;
2721        }
2722        return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
2723    }
2724
2725    @Override
2726    protected void onAttachedToWindow() {
2727        super.onAttachedToWindow();
2728        mLayoutOrScrollCounter = 0;
2729        mIsAttached = true;
2730        mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
2731        if (mLayout != null) {
2732            mLayout.dispatchAttachedToWindow(this);
2733        }
2734        mPostedAnimatorRunner = false;
2735
2736        if (ALLOW_THREAD_GAP_WORK) {
2737            // Register with gap worker
2738            mGapWorker = GapWorker.sGapWorker.get();
2739            if (mGapWorker == null) {
2740                mGapWorker = new GapWorker();
2741
2742                // break 60 fps assumption if data from display appears valid
2743                // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
2744                Display display = ViewCompat.getDisplay(this);
2745                float refreshRate = 60.0f;
2746                if (!isInEditMode() && display != null) {
2747                    float displayRefreshRate = display.getRefreshRate();
2748                    if (displayRefreshRate >= 30.0f) {
2749                        refreshRate = displayRefreshRate;
2750                    }
2751                }
2752                mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
2753                GapWorker.sGapWorker.set(mGapWorker);
2754            }
2755            mGapWorker.add(this);
2756        }
2757    }
2758
2759    @Override
2760    protected void onDetachedFromWindow() {
2761        super.onDetachedFromWindow();
2762        if (mItemAnimator != null) {
2763            mItemAnimator.endAnimations();
2764        }
2765        stopScroll();
2766        mIsAttached = false;
2767        if (mLayout != null) {
2768            mLayout.dispatchDetachedFromWindow(this, mRecycler);
2769        }
2770        mPendingAccessibilityImportanceChange.clear();
2771        removeCallbacks(mItemAnimatorRunner);
2772        mViewInfoStore.onDetach();
2773
2774        if (ALLOW_THREAD_GAP_WORK && mGapWorker != null) {
2775            // Unregister with gap worker
2776            mGapWorker.remove(this);
2777            mGapWorker = null;
2778        }
2779    }
2780
2781    /**
2782     * Returns true if RecyclerView is attached to window.
2783     */
2784    @Override
2785    public boolean isAttachedToWindow() {
2786        return mIsAttached;
2787    }
2788
2789    /**
2790     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2791     * {@link IllegalStateException} if it <b>is not</b>.
2792     *
2793     * @param message The message for the exception. Can be null.
2794     * @see #assertNotInLayoutOrScroll(String)
2795     */
2796    void assertInLayoutOrScroll(String message) {
2797        if (!isComputingLayout()) {
2798            if (message == null) {
2799                throw new IllegalStateException("Cannot call this method unless RecyclerView is "
2800                        + "computing a layout or scrolling" + exceptionLabel());
2801            }
2802            throw new IllegalStateException(message + exceptionLabel());
2803
2804        }
2805    }
2806
2807    /**
2808     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2809     * {@link IllegalStateException} if it <b>is</b>.
2810     *
2811     * @param message The message for the exception. Can be null.
2812     * @see #assertInLayoutOrScroll(String)
2813     */
2814    void assertNotInLayoutOrScroll(String message) {
2815        if (isComputingLayout()) {
2816            if (message == null) {
2817                throw new IllegalStateException("Cannot call this method while RecyclerView is "
2818                        + "computing a layout or scrolling" + exceptionLabel());
2819            }
2820            throw new IllegalStateException(message);
2821        }
2822        if (mDispatchScrollCounter > 0) {
2823            Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might"
2824                            + "be run during a measure & layout pass where you cannot change the"
2825                            + "RecyclerView data. Any method call that might change the structure"
2826                            + "of the RecyclerView or the adapter contents should be postponed to"
2827                            + "the next frame.",
2828                    new IllegalStateException("" + exceptionLabel()));
2829        }
2830    }
2831
2832    /**
2833     * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
2834     * to child views or this view's standard scrolling behavior.
2835     *
2836     * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
2837     * returns true from
2838     * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
2839     * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
2840     * for each incoming MotionEvent until the end of the gesture.</p>
2841     *
2842     * @param listener Listener to add
2843     * @see SimpleOnItemTouchListener
2844     */
2845    public void addOnItemTouchListener(@NonNull OnItemTouchListener listener) {
2846        mOnItemTouchListeners.add(listener);
2847    }
2848
2849    /**
2850     * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
2851     *
2852     * @param listener Listener to remove
2853     */
2854    public void removeOnItemTouchListener(@NonNull OnItemTouchListener listener) {
2855        mOnItemTouchListeners.remove(listener);
2856        if (mActiveOnItemTouchListener == listener) {
2857            mActiveOnItemTouchListener = null;
2858        }
2859    }
2860
2861    private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
2862        final int action = e.getAction();
2863        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
2864            mActiveOnItemTouchListener = null;
2865        }
2866
2867        final int listenerCount = mOnItemTouchListeners.size();
2868        for (int i = 0; i < listenerCount; i++) {
2869            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2870            if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
2871                mActiveOnItemTouchListener = listener;
2872                return true;
2873            }
2874        }
2875        return false;
2876    }
2877
2878    private boolean dispatchOnItemTouch(MotionEvent e) {
2879        final int action = e.getAction();
2880        if (mActiveOnItemTouchListener != null) {
2881            if (action == MotionEvent.ACTION_DOWN) {
2882                // Stale state from a previous gesture, we're starting a new one. Clear it.
2883                mActiveOnItemTouchListener = null;
2884            } else {
2885                mActiveOnItemTouchListener.onTouchEvent(this, e);
2886                if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
2887                    // Clean up for the next gesture.
2888                    mActiveOnItemTouchListener = null;
2889                }
2890                return true;
2891            }
2892        }
2893
2894        // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
2895        // as called from onInterceptTouchEvent; skip it.
2896        if (action != MotionEvent.ACTION_DOWN) {
2897            final int listenerCount = mOnItemTouchListeners.size();
2898            for (int i = 0; i < listenerCount; i++) {
2899                final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2900                if (listener.onInterceptTouchEvent(this, e)) {
2901                    mActiveOnItemTouchListener = listener;
2902                    return true;
2903                }
2904            }
2905        }
2906        return false;
2907    }
2908
2909    @Override
2910    public boolean onInterceptTouchEvent(MotionEvent e) {
2911        if (mLayoutFrozen) {
2912            // When layout is frozen,  RV does not intercept the motion event.
2913            // A child view e.g. a button may still get the click.
2914            return false;
2915        }
2916        if (dispatchOnItemTouchIntercept(e)) {
2917            cancelTouch();
2918            return true;
2919        }
2920
2921        if (mLayout == null) {
2922            return false;
2923        }
2924
2925        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2926        final boolean canScrollVertically = mLayout.canScrollVertically();
2927
2928        if (mVelocityTracker == null) {
2929            mVelocityTracker = VelocityTracker.obtain();
2930        }
2931        mVelocityTracker.addMovement(e);
2932
2933        final int action = e.getActionMasked();
2934        final int actionIndex = e.getActionIndex();
2935
2936        switch (action) {
2937            case MotionEvent.ACTION_DOWN:
2938                if (mIgnoreMotionEventTillDown) {
2939                    mIgnoreMotionEventTillDown = false;
2940                }
2941                mScrollPointerId = e.getPointerId(0);
2942                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2943                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2944
2945                if (mScrollState == SCROLL_STATE_SETTLING) {
2946                    getParent().requestDisallowInterceptTouchEvent(true);
2947                    setScrollState(SCROLL_STATE_DRAGGING);
2948                }
2949
2950                // Clear the nested offsets
2951                mNestedOffsets[0] = mNestedOffsets[1] = 0;
2952
2953                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2954                if (canScrollHorizontally) {
2955                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2956                }
2957                if (canScrollVertically) {
2958                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2959                }
2960                startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
2961                break;
2962
2963            case MotionEvent.ACTION_POINTER_DOWN:
2964                mScrollPointerId = e.getPointerId(actionIndex);
2965                mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2966                mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2967                break;
2968
2969            case MotionEvent.ACTION_MOVE: {
2970                final int index = e.findPointerIndex(mScrollPointerId);
2971                if (index < 0) {
2972                    Log.e(TAG, "Error processing scroll; pointer index for id "
2973                            + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2974                    return false;
2975                }
2976
2977                final int x = (int) (e.getX(index) + 0.5f);
2978                final int y = (int) (e.getY(index) + 0.5f);
2979                if (mScrollState != SCROLL_STATE_DRAGGING) {
2980                    final int dx = x - mInitialTouchX;
2981                    final int dy = y - mInitialTouchY;
2982                    boolean startScroll = false;
2983                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2984                        mLastTouchX = x;
2985                        startScroll = true;
2986                    }
2987                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2988                        mLastTouchY = y;
2989                        startScroll = true;
2990                    }
2991                    if (startScroll) {
2992                        setScrollState(SCROLL_STATE_DRAGGING);
2993                    }
2994                }
2995            } break;
2996
2997            case MotionEvent.ACTION_POINTER_UP: {
2998                onPointerUp(e);
2999            } break;
3000
3001            case MotionEvent.ACTION_UP: {
3002                mVelocityTracker.clear();
3003                stopNestedScroll(TYPE_TOUCH);
3004            } break;
3005
3006            case MotionEvent.ACTION_CANCEL: {
3007                cancelTouch();
3008            }
3009        }
3010        return mScrollState == SCROLL_STATE_DRAGGING;
3011    }
3012
3013    @Override
3014    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
3015        final int listenerCount = mOnItemTouchListeners.size();
3016        for (int i = 0; i < listenerCount; i++) {
3017            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
3018            listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
3019        }
3020        super.requestDisallowInterceptTouchEvent(disallowIntercept);
3021    }
3022
3023    @Override
3024    public boolean onTouchEvent(MotionEvent e) {
3025        if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
3026            return false;
3027        }
3028        if (dispatchOnItemTouch(e)) {
3029            cancelTouch();
3030            return true;
3031        }
3032
3033        if (mLayout == null) {
3034            return false;
3035        }
3036
3037        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
3038        final boolean canScrollVertically = mLayout.canScrollVertically();
3039
3040        if (mVelocityTracker == null) {
3041            mVelocityTracker = VelocityTracker.obtain();
3042        }
3043        boolean eventAddedToVelocityTracker = false;
3044
3045        final MotionEvent vtev = MotionEvent.obtain(e);
3046        final int action = e.getActionMasked();
3047        final int actionIndex = e.getActionIndex();
3048
3049        if (action == MotionEvent.ACTION_DOWN) {
3050            mNestedOffsets[0] = mNestedOffsets[1] = 0;
3051        }
3052        vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
3053
3054        switch (action) {
3055            case MotionEvent.ACTION_DOWN: {
3056                mScrollPointerId = e.getPointerId(0);
3057                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
3058                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
3059
3060                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
3061                if (canScrollHorizontally) {
3062                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
3063                }
3064                if (canScrollVertically) {
3065                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
3066                }
3067                startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
3068            } break;
3069
3070            case MotionEvent.ACTION_POINTER_DOWN: {
3071                mScrollPointerId = e.getPointerId(actionIndex);
3072                mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
3073                mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
3074            } break;
3075
3076            case MotionEvent.ACTION_MOVE: {
3077                final int index = e.findPointerIndex(mScrollPointerId);
3078                if (index < 0) {
3079                    Log.e(TAG, "Error processing scroll; pointer index for id "
3080                            + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
3081                    return false;
3082                }
3083
3084                final int x = (int) (e.getX(index) + 0.5f);
3085                final int y = (int) (e.getY(index) + 0.5f);
3086                int dx = mLastTouchX - x;
3087                int dy = mLastTouchY - y;
3088
3089                if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset, TYPE_TOUCH)) {
3090                    dx -= mScrollConsumed[0];
3091                    dy -= mScrollConsumed[1];
3092                    vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
3093                    // Updated the nested offsets
3094                    mNestedOffsets[0] += mScrollOffset[0];
3095                    mNestedOffsets[1] += mScrollOffset[1];
3096                }
3097
3098                if (mScrollState != SCROLL_STATE_DRAGGING) {
3099                    boolean startScroll = false;
3100                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
3101                        if (dx > 0) {
3102                            dx -= mTouchSlop;
3103                        } else {
3104                            dx += mTouchSlop;
3105                        }
3106                        startScroll = true;
3107                    }
3108                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
3109                        if (dy > 0) {
3110                            dy -= mTouchSlop;
3111                        } else {
3112                            dy += mTouchSlop;
3113                        }
3114                        startScroll = true;
3115                    }
3116                    if (startScroll) {
3117                        setScrollState(SCROLL_STATE_DRAGGING);
3118                    }
3119                }
3120
3121                if (mScrollState == SCROLL_STATE_DRAGGING) {
3122                    mLastTouchX = x - mScrollOffset[0];
3123                    mLastTouchY = y - mScrollOffset[1];
3124
3125                    if (scrollByInternal(
3126                            canScrollHorizontally ? dx : 0,
3127                            canScrollVertically ? dy : 0,
3128                            vtev)) {
3129                        getParent().requestDisallowInterceptTouchEvent(true);
3130                    }
3131                    if (mGapWorker != null && (dx != 0 || dy != 0)) {
3132                        mGapWorker.postFromTraversal(this, dx, dy);
3133                    }
3134                }
3135            } break;
3136
3137            case MotionEvent.ACTION_POINTER_UP: {
3138                onPointerUp(e);
3139            } break;
3140
3141            case MotionEvent.ACTION_UP: {
3142                mVelocityTracker.addMovement(vtev);
3143                eventAddedToVelocityTracker = true;
3144                mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
3145                final float xvel = canScrollHorizontally
3146                        ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
3147                final float yvel = canScrollVertically
3148                        ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
3149                if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
3150                    setScrollState(SCROLL_STATE_IDLE);
3151                }
3152                resetTouch();
3153            } break;
3154
3155            case MotionEvent.ACTION_CANCEL: {
3156                cancelTouch();
3157            } break;
3158        }
3159
3160        if (!eventAddedToVelocityTracker) {
3161            mVelocityTracker.addMovement(vtev);
3162        }
3163        vtev.recycle();
3164
3165        return true;
3166    }
3167
3168    private void resetTouch() {
3169        if (mVelocityTracker != null) {
3170            mVelocityTracker.clear();
3171        }
3172        stopNestedScroll(TYPE_TOUCH);
3173        releaseGlows();
3174    }
3175
3176    private void cancelTouch() {
3177        resetTouch();
3178        setScrollState(SCROLL_STATE_IDLE);
3179    }
3180
3181    private void onPointerUp(MotionEvent e) {
3182        final int actionIndex = e.getActionIndex();
3183        if (e.getPointerId(actionIndex) == mScrollPointerId) {
3184            // Pick a new pointer to pick up the slack.
3185            final int newIndex = actionIndex == 0 ? 1 : 0;
3186            mScrollPointerId = e.getPointerId(newIndex);
3187            mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
3188            mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
3189        }
3190    }
3191
3192    @Override
3193    public boolean onGenericMotionEvent(MotionEvent event) {
3194        if (mLayout == null) {
3195            return false;
3196        }
3197        if (mLayoutFrozen) {
3198            return false;
3199        }
3200        if (event.getAction() == MotionEventCompat.ACTION_SCROLL) {
3201            final float vScroll, hScroll;
3202            if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
3203                if (mLayout.canScrollVertically()) {
3204                    // Inverse the sign of the vertical scroll to align the scroll orientation
3205                    // with AbsListView.
3206                    vScroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
3207                } else {
3208                    vScroll = 0f;
3209                }
3210                if (mLayout.canScrollHorizontally()) {
3211                    hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
3212                } else {
3213                    hScroll = 0f;
3214                }
3215            } else if ((event.getSource() & InputDeviceCompat.SOURCE_ROTARY_ENCODER) != 0) {
3216                final float axisScroll = event.getAxisValue(MotionEventCompat.AXIS_SCROLL);
3217                if (mLayout.canScrollVertically()) {
3218                    // Invert the sign of the vertical scroll to align the scroll orientation
3219                    // with AbsListView.
3220                    vScroll = -axisScroll;
3221                    hScroll = 0f;
3222                } else if (mLayout.canScrollHorizontally()) {
3223                    vScroll = 0f;
3224                    hScroll = axisScroll;
3225                } else {
3226                    vScroll = 0f;
3227                    hScroll = 0f;
3228                }
3229            } else {
3230                vScroll = 0f;
3231                hScroll = 0f;
3232            }
3233
3234            if (vScroll != 0 || hScroll != 0) {
3235                scrollByInternal((int) (hScroll * mScaledHorizontalScrollFactor),
3236                        (int) (vScroll * mScaledVerticalScrollFactor), event);
3237            }
3238        }
3239        return false;
3240    }
3241
3242    @Override
3243    protected void onMeasure(int widthSpec, int heightSpec) {
3244        if (mLayout == null) {
3245            defaultOnMeasure(widthSpec, heightSpec);
3246            return;
3247        }
3248        if (mLayout.isAutoMeasureEnabled()) {
3249            final int widthMode = MeasureSpec.getMode(widthSpec);
3250            final int heightMode = MeasureSpec.getMode(heightSpec);
3251
3252            /**
3253             * This specific call should be considered deprecated and replaced with
3254             * {@link #defaultOnMeasure(int, int)}. It can't actually be replaced as it could
3255             * break existing third party code but all documentation directs developers to not
3256             * override {@link LayoutManager#onMeasure(int, int)} when
3257             * {@link LayoutManager#isAutoMeasureEnabled()} returns true.
3258             */
3259            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3260
3261            final boolean measureSpecModeIsExactly =
3262                    widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
3263            if (measureSpecModeIsExactly || mAdapter == null) {
3264                return;
3265            }
3266
3267            if (mState.mLayoutStep == State.STEP_START) {
3268                dispatchLayoutStep1();
3269            }
3270            // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
3271            // consistency
3272            mLayout.setMeasureSpecs(widthSpec, heightSpec);
3273            mState.mIsMeasuring = true;
3274            dispatchLayoutStep2();
3275
3276            // now we can get the width and height from the children.
3277            mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
3278
3279            // if RecyclerView has non-exact width and height and if there is at least one child
3280            // which also has non-exact width & height, we have to re-measure.
3281            if (mLayout.shouldMeasureTwice()) {
3282                mLayout.setMeasureSpecs(
3283                        MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
3284                        MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
3285                mState.mIsMeasuring = true;
3286                dispatchLayoutStep2();
3287                // now we can get the width and height from the children.
3288                mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
3289            }
3290        } else {
3291            if (mHasFixedSize) {
3292                mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3293                return;
3294            }
3295            // custom onMeasure
3296            if (mAdapterUpdateDuringMeasure) {
3297                startInterceptRequestLayout();
3298                onEnterLayoutOrScroll();
3299                processAdapterUpdatesAndSetAnimationFlags();
3300                onExitLayoutOrScroll();
3301
3302                if (mState.mRunPredictiveAnimations) {
3303                    mState.mInPreLayout = true;
3304                } else {
3305                    // consume remaining updates to provide a consistent state with the layout pass.
3306                    mAdapterHelper.consumeUpdatesInOnePass();
3307                    mState.mInPreLayout = false;
3308                }
3309                mAdapterUpdateDuringMeasure = false;
3310                stopInterceptRequestLayout(false);
3311            } else if (mState.mRunPredictiveAnimations) {
3312                // If mAdapterUpdateDuringMeasure is false and mRunPredictiveAnimations is true:
3313                // this means there is already an onMeasure() call performed to handle the pending
3314                // adapter change, two onMeasure() calls can happen if RV is a child of LinearLayout
3315                // with layout_width=MATCH_PARENT. RV cannot call LM.onMeasure() second time
3316                // because getViewForPosition() will crash when LM uses a child to measure.
3317                setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
3318                return;
3319            }
3320
3321            if (mAdapter != null) {
3322                mState.mItemCount = mAdapter.getItemCount();
3323            } else {
3324                mState.mItemCount = 0;
3325            }
3326            startInterceptRequestLayout();
3327            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3328            stopInterceptRequestLayout(false);
3329            mState.mInPreLayout = false; // clear
3330        }
3331    }
3332
3333    /**
3334     * An implementation of {@link View#onMeasure(int, int)} to fall back to in various scenarios
3335     * where this RecyclerView is otherwise lacking better information.
3336     */
3337    void defaultOnMeasure(int widthSpec, int heightSpec) {
3338        // calling LayoutManager here is not pretty but that API is already public and it is better
3339        // than creating another method since this is internal.
3340        final int width = LayoutManager.chooseSize(widthSpec,
3341                getPaddingLeft() + getPaddingRight(),
3342                ViewCompat.getMinimumWidth(this));
3343        final int height = LayoutManager.chooseSize(heightSpec,
3344                getPaddingTop() + getPaddingBottom(),
3345                ViewCompat.getMinimumHeight(this));
3346
3347        setMeasuredDimension(width, height);
3348    }
3349
3350    @Override
3351    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
3352        super.onSizeChanged(w, h, oldw, oldh);
3353        if (w != oldw || h != oldh) {
3354            invalidateGlows();
3355            // layout's w/h are updated during measure/layout steps.
3356        }
3357    }
3358
3359    /**
3360     * Sets the {@link ItemAnimator} that will handle animations involving changes
3361     * to the items in this RecyclerView. By default, RecyclerView instantiates and
3362     * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
3363     * enabled for the RecyclerView depends on the ItemAnimator and whether
3364     * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
3365     * supports item animations}.
3366     *
3367     * @param animator The ItemAnimator being set. If null, no animations will occur
3368     * when changes occur to the items in this RecyclerView.
3369     */
3370    public void setItemAnimator(@Nullable ItemAnimator animator) {
3371        if (mItemAnimator != null) {
3372            mItemAnimator.endAnimations();
3373            mItemAnimator.setListener(null);
3374        }
3375        mItemAnimator = animator;
3376        if (mItemAnimator != null) {
3377            mItemAnimator.setListener(mItemAnimatorListener);
3378        }
3379    }
3380
3381    void onEnterLayoutOrScroll() {
3382        mLayoutOrScrollCounter++;
3383    }
3384
3385    void onExitLayoutOrScroll() {
3386        onExitLayoutOrScroll(true);
3387    }
3388
3389    void onExitLayoutOrScroll(boolean enableChangeEvents) {
3390        mLayoutOrScrollCounter--;
3391        if (mLayoutOrScrollCounter < 1) {
3392            if (DEBUG && mLayoutOrScrollCounter < 0) {
3393                throw new IllegalStateException("layout or scroll counter cannot go below zero."
3394                        + "Some calls are not matching" + exceptionLabel());
3395            }
3396            mLayoutOrScrollCounter = 0;
3397            if (enableChangeEvents) {
3398                dispatchContentChangedIfNecessary();
3399                dispatchPendingImportantForAccessibilityChanges();
3400            }
3401        }
3402    }
3403
3404    boolean isAccessibilityEnabled() {
3405        return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
3406    }
3407
3408    private void dispatchContentChangedIfNecessary() {
3409        final int flags = mEatenAccessibilityChangeFlags;
3410        mEatenAccessibilityChangeFlags = 0;
3411        if (flags != 0 && isAccessibilityEnabled()) {
3412            final AccessibilityEvent event = AccessibilityEvent.obtain();
3413            event.setEventType(AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
3414            AccessibilityEventCompat.setContentChangeTypes(event, flags);
3415            sendAccessibilityEventUnchecked(event);
3416        }
3417    }
3418
3419    /**
3420     * Returns whether RecyclerView is currently computing a layout.
3421     * <p>
3422     * If this method returns true, it means that RecyclerView is in a lockdown state and any
3423     * attempt to update adapter contents will result in an exception because adapter contents
3424     * cannot be changed while RecyclerView is trying to compute the layout.
3425     * <p>
3426     * It is very unlikely that your code will be running during this state as it is
3427     * called by the framework when a layout traversal happens or RecyclerView starts to scroll
3428     * in response to system events (touch, accessibility etc).
3429     * <p>
3430     * This case may happen if you have some custom logic to change adapter contents in
3431     * response to a View callback (e.g. focus change callback) which might be triggered during a
3432     * layout calculation. In these cases, you should just postpone the change using a Handler or a
3433     * similar mechanism.
3434     *
3435     * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
3436     *         otherwise
3437     */
3438    public boolean isComputingLayout() {
3439        return mLayoutOrScrollCounter > 0;
3440    }
3441
3442    /**
3443     * Returns true if an accessibility event should not be dispatched now. This happens when an
3444     * accessibility request arrives while RecyclerView does not have a stable state which is very
3445     * hard to handle for a LayoutManager. Instead, this method records necessary information about
3446     * the event and dispatches a window change event after the critical section is finished.
3447     *
3448     * @return True if the accessibility event should be postponed.
3449     */
3450    boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
3451        if (isComputingLayout()) {
3452            int type = 0;
3453            if (event != null) {
3454                type = AccessibilityEventCompat.getContentChangeTypes(event);
3455            }
3456            if (type == 0) {
3457                type = AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED;
3458            }
3459            mEatenAccessibilityChangeFlags |= type;
3460            return true;
3461        }
3462        return false;
3463    }
3464
3465    @Override
3466    public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
3467        if (shouldDeferAccessibilityEvent(event)) {
3468            return;
3469        }
3470        super.sendAccessibilityEventUnchecked(event);
3471    }
3472
3473    /**
3474     * Gets the current ItemAnimator for this RecyclerView. A null return value
3475     * indicates that there is no animator and that item changes will happen without
3476     * any animations. By default, RecyclerView instantiates and
3477     * uses an instance of {@link DefaultItemAnimator}.
3478     *
3479     * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
3480     * when changes occur to the items in this RecyclerView.
3481     */
3482    @Nullable
3483    public ItemAnimator getItemAnimator() {
3484        return mItemAnimator;
3485    }
3486
3487    /**
3488     * Post a runnable to the next frame to run pending item animations. Only the first such
3489     * request will be posted, governed by the mPostedAnimatorRunner flag.
3490     */
3491    void postAnimationRunner() {
3492        if (!mPostedAnimatorRunner && mIsAttached) {
3493            ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
3494            mPostedAnimatorRunner = true;
3495        }
3496    }
3497
3498    private boolean predictiveItemAnimationsEnabled() {
3499        return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
3500    }
3501
3502    /**
3503     * Consumes adapter updates and calculates which type of animations we want to run.
3504     * Called in onMeasure and dispatchLayout.
3505     * <p>
3506     * This method may process only the pre-layout state of updates or all of them.
3507     */
3508    private void processAdapterUpdatesAndSetAnimationFlags() {
3509        if (mDataSetHasChangedAfterLayout) {
3510            // Processing these items have no value since data set changed unexpectedly.
3511            // Instead, we just reset it.
3512            mAdapterHelper.reset();
3513            if (mDispatchItemsChangedEvent) {
3514                mLayout.onItemsChanged(this);
3515            }
3516        }
3517        // simple animations are a subset of advanced animations (which will cause a
3518        // pre-layout step)
3519        // If layout supports predictive animations, pre-process to decide if we want to run them
3520        if (predictiveItemAnimationsEnabled()) {
3521            mAdapterHelper.preProcess();
3522        } else {
3523            mAdapterHelper.consumeUpdatesInOnePass();
3524        }
3525        boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
3526        mState.mRunSimpleAnimations = mFirstLayoutComplete
3527                && mItemAnimator != null
3528                && (mDataSetHasChangedAfterLayout
3529                || animationTypeSupported
3530                || mLayout.mRequestedSimpleAnimations)
3531                && (!mDataSetHasChangedAfterLayout
3532                || mAdapter.hasStableIds());
3533        mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
3534                && animationTypeSupported
3535                && !mDataSetHasChangedAfterLayout
3536                && predictiveItemAnimationsEnabled();
3537    }
3538
3539    /**
3540     * Wrapper around layoutChildren() that handles animating changes caused by layout.
3541     * Animations work on the assumption that there are five different kinds of items
3542     * in play:
3543     * PERSISTENT: items are visible before and after layout
3544     * REMOVED: items were visible before layout and were removed by the app
3545     * ADDED: items did not exist before layout and were added by the app
3546     * DISAPPEARING: items exist in the data set before/after, but changed from
3547     * visible to non-visible in the process of layout (they were moved off
3548     * screen as a side-effect of other changes)
3549     * APPEARING: items exist in the data set before/after, but changed from
3550     * non-visible to visible in the process of layout (they were moved on
3551     * screen as a side-effect of other changes)
3552     * The overall approach figures out what items exist before/after layout and
3553     * infers one of the five above states for each of the items. Then the animations
3554     * are set up accordingly:
3555     * PERSISTENT views are animated via
3556     * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3557     * DISAPPEARING views are animated via
3558     * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3559     * APPEARING views are animated via
3560     * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3561     * and changed views are animated via
3562     * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
3563     */
3564    void dispatchLayout() {
3565        if (mAdapter == null) {
3566            Log.e(TAG, "No adapter attached; skipping layout");
3567            // leave the state in START
3568            return;
3569        }
3570        if (mLayout == null) {
3571            Log.e(TAG, "No layout manager attached; skipping layout");
3572            // leave the state in START
3573            return;
3574        }
3575        mState.mIsMeasuring = false;
3576        if (mState.mLayoutStep == State.STEP_START) {
3577            dispatchLayoutStep1();
3578            mLayout.setExactMeasureSpecsFrom(this);
3579            dispatchLayoutStep2();
3580        } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
3581                || mLayout.getHeight() != getHeight()) {
3582            // First 2 steps are done in onMeasure but looks like we have to run again due to
3583            // changed size.
3584            mLayout.setExactMeasureSpecsFrom(this);
3585            dispatchLayoutStep2();
3586        } else {
3587            // always make sure we sync them (to ensure mode is exact)
3588            mLayout.setExactMeasureSpecsFrom(this);
3589        }
3590        dispatchLayoutStep3();
3591    }
3592
3593    private void saveFocusInfo() {
3594        View child = null;
3595        if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
3596            child = getFocusedChild();
3597        }
3598
3599        final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
3600        if (focusedVh == null) {
3601            resetFocusInfo();
3602        } else {
3603            mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
3604            // mFocusedItemPosition should hold the current adapter position of the previously
3605            // focused item. If the item is removed, we store the previous adapter position of the
3606            // removed item.
3607            mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION
3608                    : (focusedVh.isRemoved() ? focusedVh.mOldPosition
3609                            : focusedVh.getAdapterPosition());
3610            mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
3611        }
3612    }
3613
3614    private void resetFocusInfo() {
3615        mState.mFocusedItemId = NO_ID;
3616        mState.mFocusedItemPosition = NO_POSITION;
3617        mState.mFocusedSubChildId = View.NO_ID;
3618    }
3619
3620    /**
3621     * Finds the best view candidate to request focus on using mFocusedItemPosition index of the
3622     * previously focused item. It first traverses the adapter forward to find a focusable candidate
3623     * and if no such candidate is found, it reverses the focus search direction for the items
3624     * before the mFocusedItemPosition'th index;
3625     * @return The best candidate to request focus on, or null if no such candidate exists. Null
3626     * indicates all the existing adapter items are unfocusable.
3627     */
3628    @Nullable
3629    private View findNextViewToFocus() {
3630        int startFocusSearchIndex = mState.mFocusedItemPosition != -1 ? mState.mFocusedItemPosition
3631                : 0;
3632        ViewHolder nextFocus;
3633        final int itemCount = mState.getItemCount();
3634        for (int i = startFocusSearchIndex; i < itemCount; i++) {
3635            nextFocus = findViewHolderForAdapterPosition(i);
3636            if (nextFocus == null) {
3637                break;
3638            }
3639            if (nextFocus.itemView.hasFocusable()) {
3640                return nextFocus.itemView;
3641            }
3642        }
3643        final int limit = Math.min(itemCount, startFocusSearchIndex);
3644        for (int i = limit - 1; i >= 0; i--) {
3645            nextFocus = findViewHolderForAdapterPosition(i);
3646            if (nextFocus == null) {
3647                return null;
3648            }
3649            if (nextFocus.itemView.hasFocusable()) {
3650                return nextFocus.itemView;
3651            }
3652        }
3653        return null;
3654    }
3655
3656    private void recoverFocusFromState() {
3657        if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()
3658                || getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
3659                || (getDescendantFocusability() == FOCUS_BEFORE_DESCENDANTS && isFocused())) {
3660            // No-op if either of these cases happens:
3661            // 1. RV has no focus, or 2. RV blocks focus to its children, or 3. RV takes focus
3662            // before its children and is focused (i.e. it already stole the focus away from its
3663            // descendants).
3664            return;
3665        }
3666        // only recover focus if RV itself has the focus or the focused view is hidden
3667        if (!isFocused()) {
3668            final View focusedChild = getFocusedChild();
3669            if (IGNORE_DETACHED_FOCUSED_CHILD
3670                    && (focusedChild.getParent() == null || !focusedChild.hasFocus())) {
3671                // Special handling of API 15-. A focused child can be invalid because mFocus is not
3672                // cleared when the child is detached (mParent = null),
3673                // This happens because clearFocus on API 15- does not invalidate mFocus of its
3674                // parent when this child is detached.
3675                // For API 16+, this is not an issue because requestFocus takes care of clearing the
3676                // prior detached focused child. For API 15- the problem happens in 2 cases because
3677                // clearChild does not call clearChildFocus on RV: 1. setFocusable(false) is called
3678                // for the current focused item which calls clearChild or 2. when the prior focused
3679                // child is removed, removeDetachedView called in layout step 3 which calls
3680                // clearChild. We should ignore this invalid focused child in all our calculations
3681                // for the next view to receive focus, and apply the focus recovery logic instead.
3682                if (mChildHelper.getChildCount() == 0) {
3683                    // No children left. Request focus on the RV itself since one of its children
3684                    // was holding focus previously.
3685                    requestFocus();
3686                    return;
3687                }
3688            } else if (!mChildHelper.isHidden(focusedChild)) {
3689                // If the currently focused child is hidden, apply the focus recovery logic.
3690                // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
3691                return;
3692            }
3693        }
3694        ViewHolder focusTarget = null;
3695        // RV first attempts to locate the previously focused item to request focus on using
3696        // mFocusedItemId. If such an item no longer exists, it then makes a best-effort attempt to
3697        // find the next best candidate to request focus on based on mFocusedItemPosition.
3698        if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
3699            focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
3700        }
3701        View viewToFocus = null;
3702        if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView)
3703                || !focusTarget.itemView.hasFocusable()) {
3704            if (mChildHelper.getChildCount() > 0) {
3705                // At this point, RV has focus and either of these conditions are true:
3706                // 1. There's no previously focused item either because RV received focused before
3707                // layout, or the previously focused item was removed, or RV doesn't have stable IDs
3708                // 2. Previous focus child is hidden, or 3. Previous focused child is no longer
3709                // focusable. In either of these cases, we make sure that RV still passes down the
3710                // focus to one of its focusable children using a best-effort algorithm.
3711                viewToFocus = findNextViewToFocus();
3712            }
3713        } else {
3714            // looks like the focused item has been replaced with another view that represents the
3715            // same item in the adapter. Request focus on that.
3716            viewToFocus = focusTarget.itemView;
3717        }
3718
3719        if (viewToFocus != null) {
3720            if (mState.mFocusedSubChildId != NO_ID) {
3721                View child = viewToFocus.findViewById(mState.mFocusedSubChildId);
3722                if (child != null && child.isFocusable()) {
3723                    viewToFocus = child;
3724                }
3725            }
3726            viewToFocus.requestFocus();
3727        }
3728    }
3729
3730    private int getDeepestFocusedViewWithId(View view) {
3731        int lastKnownId = view.getId();
3732        while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
3733            view = ((ViewGroup) view).getFocusedChild();
3734            final int id = view.getId();
3735            if (id != View.NO_ID) {
3736                lastKnownId = view.getId();
3737            }
3738        }
3739        return lastKnownId;
3740    }
3741
3742    final void fillRemainingScrollValues(State state) {
3743        if (getScrollState() == SCROLL_STATE_SETTLING) {
3744            final OverScroller scroller = mViewFlinger.mScroller;
3745            state.mRemainingScrollHorizontal = scroller.getFinalX() - scroller.getCurrX();
3746            state.mRemainingScrollVertical = scroller.getFinalY() - scroller.getCurrY();
3747        } else {
3748            state.mRemainingScrollHorizontal = 0;
3749            state.mRemainingScrollVertical = 0;
3750        }
3751    }
3752
3753    /**
3754     * The first step of a layout where we;
3755     * - process adapter updates
3756     * - decide which animation should run
3757     * - save information about current views
3758     * - If necessary, run predictive layout and save its information
3759     */
3760    private void dispatchLayoutStep1() {
3761        mState.assertLayoutStep(State.STEP_START);
3762        fillRemainingScrollValues(mState);
3763        mState.mIsMeasuring = false;
3764        startInterceptRequestLayout();
3765        mViewInfoStore.clear();
3766        onEnterLayoutOrScroll();
3767        processAdapterUpdatesAndSetAnimationFlags();
3768        saveFocusInfo();
3769        mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
3770        mItemsAddedOrRemoved = mItemsChanged = false;
3771        mState.mInPreLayout = mState.mRunPredictiveAnimations;
3772        mState.mItemCount = mAdapter.getItemCount();
3773        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3774
3775        if (mState.mRunSimpleAnimations) {
3776            // Step 0: Find out where all non-removed items are, pre-layout
3777            int count = mChildHelper.getChildCount();
3778            for (int i = 0; i < count; ++i) {
3779                final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3780                if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
3781                    continue;
3782                }
3783                final ItemHolderInfo animationInfo = mItemAnimator
3784                        .recordPreLayoutInformation(mState, holder,
3785                                ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
3786                                holder.getUnmodifiedPayloads());
3787                mViewInfoStore.addToPreLayout(holder, animationInfo);
3788                if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
3789                        && !holder.shouldIgnore() && !holder.isInvalid()) {
3790                    long key = getChangedHolderKey(holder);
3791                    // This is NOT the only place where a ViewHolder is added to old change holders
3792                    // list. There is another case where:
3793                    //    * A VH is currently hidden but not deleted
3794                    //    * The hidden item is changed in the adapter
3795                    //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
3796                    // When this case is detected, RV will un-hide that view and add to the old
3797                    // change holders list.
3798                    mViewInfoStore.addToOldChangeHolders(key, holder);
3799                }
3800            }
3801        }
3802        if (mState.mRunPredictiveAnimations) {
3803            // Step 1: run prelayout: This will use the old positions of items. The layout manager
3804            // is expected to layout everything, even removed items (though not to add removed
3805            // items back to the container). This gives the pre-layout position of APPEARING views
3806            // which come into existence as part of the real layout.
3807
3808            // Save old positions so that LayoutManager can run its mapping logic.
3809            saveOldPositions();
3810            final boolean didStructureChange = mState.mStructureChanged;
3811            mState.mStructureChanged = false;
3812            // temporarily disable flag because we are asking for previous layout
3813            mLayout.onLayoutChildren(mRecycler, mState);
3814            mState.mStructureChanged = didStructureChange;
3815
3816            for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
3817                final View child = mChildHelper.getChildAt(i);
3818                final ViewHolder viewHolder = getChildViewHolderInt(child);
3819                if (viewHolder.shouldIgnore()) {
3820                    continue;
3821                }
3822                if (!mViewInfoStore.isInPreLayout(viewHolder)) {
3823                    int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
3824                    boolean wasHidden = viewHolder
3825                            .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3826                    if (!wasHidden) {
3827                        flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
3828                    }
3829                    final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
3830                            mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
3831                    if (wasHidden) {
3832                        recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
3833                    } else {
3834                        mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
3835                    }
3836                }
3837            }
3838            // we don't process disappearing list because they may re-appear in post layout pass.
3839            clearOldPositions();
3840        } else {
3841            clearOldPositions();
3842        }
3843        onExitLayoutOrScroll();
3844        stopInterceptRequestLayout(false);
3845        mState.mLayoutStep = State.STEP_LAYOUT;
3846    }
3847
3848    /**
3849     * The second layout step where we do the actual layout of the views for the final state.
3850     * This step might be run multiple times if necessary (e.g. measure).
3851     */
3852    private void dispatchLayoutStep2() {
3853        startInterceptRequestLayout();
3854        onEnterLayoutOrScroll();
3855        mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
3856        mAdapterHelper.consumeUpdatesInOnePass();
3857        mState.mItemCount = mAdapter.getItemCount();
3858        mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
3859
3860        // Step 2: Run layout
3861        mState.mInPreLayout = false;
3862        mLayout.onLayoutChildren(mRecycler, mState);
3863
3864        mState.mStructureChanged = false;
3865        mPendingSavedState = null;
3866
3867        // onLayoutChildren may have caused client code to disable item animations; re-check
3868        mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
3869        mState.mLayoutStep = State.STEP_ANIMATIONS;
3870        onExitLayoutOrScroll();
3871        stopInterceptRequestLayout(false);
3872    }
3873
3874    /**
3875     * The final step of the layout where we save the information about views for animations,
3876     * trigger animations and do any necessary cleanup.
3877     */
3878    private void dispatchLayoutStep3() {
3879        mState.assertLayoutStep(State.STEP_ANIMATIONS);
3880        startInterceptRequestLayout();
3881        onEnterLayoutOrScroll();
3882        mState.mLayoutStep = State.STEP_START;
3883        if (mState.mRunSimpleAnimations) {
3884            // Step 3: Find out where things are now, and process change animations.
3885            // traverse list in reverse because we may call animateChange in the loop which may
3886            // remove the target view holder.
3887            for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
3888                ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3889                if (holder.shouldIgnore()) {
3890                    continue;
3891                }
3892                long key = getChangedHolderKey(holder);
3893                final ItemHolderInfo animationInfo = mItemAnimator
3894                        .recordPostLayoutInformation(mState, holder);
3895                ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
3896                if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
3897                    // run a change animation
3898
3899                    // If an Item is CHANGED but the updated version is disappearing, it creates
3900                    // a conflicting case.
3901                    // Since a view that is marked as disappearing is likely to be going out of
3902                    // bounds, we run a change animation. Both views will be cleaned automatically
3903                    // once their animations finish.
3904                    // On the other hand, if it is the same view holder instance, we run a
3905                    // disappearing animation instead because we are not going to rebind the updated
3906                    // VH unless it is enforced by the layout manager.
3907                    final boolean oldDisappearing = mViewInfoStore.isDisappearing(
3908                            oldChangeViewHolder);
3909                    final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
3910                    if (oldDisappearing && oldChangeViewHolder == holder) {
3911                        // run disappear animation instead of change
3912                        mViewInfoStore.addToPostLayout(holder, animationInfo);
3913                    } else {
3914                        final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
3915                                oldChangeViewHolder);
3916                        // we add and remove so that any post info is merged.
3917                        mViewInfoStore.addToPostLayout(holder, animationInfo);
3918                        ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
3919                        if (preInfo == null) {
3920                            handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
3921                        } else {
3922                            animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
3923                                    oldDisappearing, newDisappearing);
3924                        }
3925                    }
3926                } else {
3927                    mViewInfoStore.addToPostLayout(holder, animationInfo);
3928                }
3929            }
3930
3931            // Step 4: Process view info lists and trigger animations
3932            mViewInfoStore.process(mViewInfoProcessCallback);
3933        }
3934
3935        mLayout.removeAndRecycleScrapInt(mRecycler);
3936        mState.mPreviousLayoutItemCount = mState.mItemCount;
3937        mDataSetHasChangedAfterLayout = false;
3938        mDispatchItemsChangedEvent = false;
3939        mState.mRunSimpleAnimations = false;
3940
3941        mState.mRunPredictiveAnimations = false;
3942        mLayout.mRequestedSimpleAnimations = false;
3943        if (mRecycler.mChangedScrap != null) {
3944            mRecycler.mChangedScrap.clear();
3945        }
3946        if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
3947            // Initial prefetch has expanded cache, so reset until next prefetch.
3948            // This prevents initial prefetches from expanding the cache permanently.
3949            mLayout.mPrefetchMaxCountObserved = 0;
3950            mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
3951            mRecycler.updateViewCacheSize();
3952        }
3953
3954        mLayout.onLayoutCompleted(mState);
3955        onExitLayoutOrScroll();
3956        stopInterceptRequestLayout(false);
3957        mViewInfoStore.clear();
3958        if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
3959            dispatchOnScrolled(0, 0);
3960        }
3961        recoverFocusFromState();
3962        resetFocusInfo();
3963    }
3964
3965    /**
3966     * This handles the case where there is an unexpected VH missing in the pre-layout map.
3967     * <p>
3968     * We might be able to detect the error in the application which will help the developer to
3969     * resolve the issue.
3970     * <p>
3971     * If it is not an expected error, we at least print an error to notify the developer and ignore
3972     * the animation.
3973     *
3974     * https://code.google.com/p/android/issues/detail?id=193958
3975     *
3976     * @param key The change key
3977     * @param holder Current ViewHolder
3978     * @param oldChangeViewHolder Changed ViewHolder
3979     */
3980    private void handleMissingPreInfoForChangeError(long key,
3981            ViewHolder holder, ViewHolder oldChangeViewHolder) {
3982        // check if two VH have the same key, if so, print that as an error
3983        final int childCount = mChildHelper.getChildCount();
3984        for (int i = 0; i < childCount; i++) {
3985            View view = mChildHelper.getChildAt(i);
3986            ViewHolder other = getChildViewHolderInt(view);
3987            if (other == holder) {
3988                continue;
3989            }
3990            final long otherKey = getChangedHolderKey(other);
3991            if (otherKey == key) {
3992                if (mAdapter != null && mAdapter.hasStableIds()) {
3993                    throw new IllegalStateException("Two different ViewHolders have the same stable"
3994                            + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
3995                            + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
3996                            + exceptionLabel());
3997                } else {
3998                    throw new IllegalStateException("Two different ViewHolders have the same change"
3999                            + " ID. This might happen due to inconsistent Adapter update events or"
4000                            + " if the LayoutManager lays out the same View multiple times."
4001                            + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
4002                            + exceptionLabel());
4003                }
4004            }
4005        }
4006        // Very unlikely to happen but if it does, notify the developer.
4007        Log.e(TAG, "Problem while matching changed view holders with the new"
4008                + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
4009                + " cannot be found but it is necessary for " + holder + exceptionLabel());
4010    }
4011
4012    /**
4013     * Records the animation information for a view holder that was bounced from hidden list. It
4014     * also clears the bounce back flag.
4015     */
4016    void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
4017            ItemHolderInfo animationInfo) {
4018        // looks like this view bounced back from hidden list!
4019        viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
4020        if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
4021                && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
4022            long key = getChangedHolderKey(viewHolder);
4023            mViewInfoStore.addToOldChangeHolders(key, viewHolder);
4024        }
4025        mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
4026    }
4027
4028    private void findMinMaxChildLayoutPositions(int[] into) {
4029        final int count = mChildHelper.getChildCount();
4030        if (count == 0) {
4031            into[0] = NO_POSITION;
4032            into[1] = NO_POSITION;
4033            return;
4034        }
4035        int minPositionPreLayout = Integer.MAX_VALUE;
4036        int maxPositionPreLayout = Integer.MIN_VALUE;
4037        for (int i = 0; i < count; ++i) {
4038            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
4039            if (holder.shouldIgnore()) {
4040                continue;
4041            }
4042            final int pos = holder.getLayoutPosition();
4043            if (pos < minPositionPreLayout) {
4044                minPositionPreLayout = pos;
4045            }
4046            if (pos > maxPositionPreLayout) {
4047                maxPositionPreLayout = pos;
4048            }
4049        }
4050        into[0] = minPositionPreLayout;
4051        into[1] = maxPositionPreLayout;
4052    }
4053
4054    private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
4055        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
4056        return mMinMaxLayoutPositions[0] != minPositionPreLayout
4057                || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
4058    }
4059
4060    @Override
4061    protected void removeDetachedView(View child, boolean animate) {
4062        ViewHolder vh = getChildViewHolderInt(child);
4063        if (vh != null) {
4064            if (vh.isTmpDetached()) {
4065                vh.clearTmpDetachFlag();
4066            } else if (!vh.shouldIgnore()) {
4067                throw new IllegalArgumentException("Called removeDetachedView with a view which"
4068                        + " is not flagged as tmp detached." + vh + exceptionLabel());
4069            }
4070        }
4071
4072        // Clear any android.view.animation.Animation that may prevent the item from
4073        // detaching when being removed. If a child is re-added before the
4074        // lazy detach occurs, it will receive invalid attach/detach sequencing.
4075        child.clearAnimation();
4076
4077        dispatchChildDetached(child);
4078        super.removeDetachedView(child, animate);
4079    }
4080
4081    /**
4082     * Returns a unique key to be used while handling change animations.
4083     * It might be child's position or stable id depending on the adapter type.
4084     */
4085    long getChangedHolderKey(ViewHolder holder) {
4086        return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
4087    }
4088
4089    void animateAppearance(@NonNull ViewHolder itemHolder,
4090            @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
4091        itemHolder.setIsRecyclable(false);
4092        if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
4093            postAnimationRunner();
4094        }
4095    }
4096
4097    void animateDisappearance(@NonNull ViewHolder holder,
4098            @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
4099        addAnimatingView(holder);
4100        holder.setIsRecyclable(false);
4101        if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
4102            postAnimationRunner();
4103        }
4104    }
4105
4106    private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
4107            @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
4108            boolean oldHolderDisappearing, boolean newHolderDisappearing) {
4109        oldHolder.setIsRecyclable(false);
4110        if (oldHolderDisappearing) {
4111            addAnimatingView(oldHolder);
4112        }
4113        if (oldHolder != newHolder) {
4114            if (newHolderDisappearing) {
4115                addAnimatingView(newHolder);
4116            }
4117            oldHolder.mShadowedHolder = newHolder;
4118            // old holder should disappear after animation ends
4119            addAnimatingView(oldHolder);
4120            mRecycler.unscrapView(oldHolder);
4121            newHolder.setIsRecyclable(false);
4122            newHolder.mShadowingHolder = oldHolder;
4123        }
4124        if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
4125            postAnimationRunner();
4126        }
4127    }
4128
4129    @Override
4130    protected void onLayout(boolean changed, int l, int t, int r, int b) {
4131        TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
4132        dispatchLayout();
4133        TraceCompat.endSection();
4134        mFirstLayoutComplete = true;
4135    }
4136
4137    @Override
4138    public void requestLayout() {
4139        if (mInterceptRequestLayoutDepth == 0 && !mLayoutFrozen) {
4140            super.requestLayout();
4141        } else {
4142            mLayoutWasDefered = true;
4143        }
4144    }
4145
4146    void markItemDecorInsetsDirty() {
4147        final int childCount = mChildHelper.getUnfilteredChildCount();
4148        for (int i = 0; i < childCount; i++) {
4149            final View child = mChildHelper.getUnfilteredChildAt(i);
4150            ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
4151        }
4152        mRecycler.markItemDecorInsetsDirty();
4153    }
4154
4155    @Override
4156    public void draw(Canvas c) {
4157        super.draw(c);
4158
4159        final int count = mItemDecorations.size();
4160        for (int i = 0; i < count; i++) {
4161            mItemDecorations.get(i).onDrawOver(c, this, mState);
4162        }
4163        // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
4164        // need find children closest to edges. Not sure if it is worth the effort.
4165        boolean needsInvalidate = false;
4166        if (mLeftGlow != null && !mLeftGlow.isFinished()) {
4167            final int restore = c.save();
4168            final int padding = mClipToPadding ? getPaddingBottom() : 0;
4169            c.rotate(270);
4170            c.translate(-getHeight() + padding, 0);
4171            needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
4172            c.restoreToCount(restore);
4173        }
4174        if (mTopGlow != null && !mTopGlow.isFinished()) {
4175            final int restore = c.save();
4176            if (mClipToPadding) {
4177                c.translate(getPaddingLeft(), getPaddingTop());
4178            }
4179            needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
4180            c.restoreToCount(restore);
4181        }
4182        if (mRightGlow != null && !mRightGlow.isFinished()) {
4183            final int restore = c.save();
4184            final int width = getWidth();
4185            final int padding = mClipToPadding ? getPaddingTop() : 0;
4186            c.rotate(90);
4187            c.translate(-padding, -width);
4188            needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
4189            c.restoreToCount(restore);
4190        }
4191        if (mBottomGlow != null && !mBottomGlow.isFinished()) {
4192            final int restore = c.save();
4193            c.rotate(180);
4194            if (mClipToPadding) {
4195                c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
4196            } else {
4197                c.translate(-getWidth(), -getHeight());
4198            }
4199            needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
4200            c.restoreToCount(restore);
4201        }
4202
4203        // If some views are animating, ItemDecorators are likely to move/change with them.
4204        // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
4205        // display lists are not invalidated.
4206        if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
4207                && mItemAnimator.isRunning()) {
4208            needsInvalidate = true;
4209        }
4210
4211        if (needsInvalidate) {
4212            ViewCompat.postInvalidateOnAnimation(this);
4213        }
4214    }
4215
4216    @Override
4217    public void onDraw(Canvas c) {
4218        super.onDraw(c);
4219
4220        final int count = mItemDecorations.size();
4221        for (int i = 0; i < count; i++) {
4222            mItemDecorations.get(i).onDraw(c, this, mState);
4223        }
4224    }
4225
4226    @Override
4227    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
4228        return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
4229    }
4230
4231    @Override
4232    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
4233        if (mLayout == null) {
4234            throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
4235        }
4236        return mLayout.generateDefaultLayoutParams();
4237    }
4238
4239    @Override
4240    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
4241        if (mLayout == null) {
4242            throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
4243        }
4244        return mLayout.generateLayoutParams(getContext(), attrs);
4245    }
4246
4247    @Override
4248    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
4249        if (mLayout == null) {
4250            throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
4251        }
4252        return mLayout.generateLayoutParams(p);
4253    }
4254
4255    /**
4256     * Returns true if RecyclerView is currently running some animations.
4257     * <p>
4258     * If you want to be notified when animations are finished, use
4259     * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
4260     *
4261     * @return True if there are some item animations currently running or waiting to be started.
4262     */
4263    public boolean isAnimating() {
4264        return mItemAnimator != null && mItemAnimator.isRunning();
4265    }
4266
4267    void saveOldPositions() {
4268        final int childCount = mChildHelper.getUnfilteredChildCount();
4269        for (int i = 0; i < childCount; i++) {
4270            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4271            if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
4272                throw new IllegalStateException("view holder cannot have position -1 unless it"
4273                        + " is removed" + exceptionLabel());
4274            }
4275            if (!holder.shouldIgnore()) {
4276                holder.saveOldPosition();
4277            }
4278        }
4279    }
4280
4281    void clearOldPositions() {
4282        final int childCount = mChildHelper.getUnfilteredChildCount();
4283        for (int i = 0; i < childCount; i++) {
4284            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4285            if (!holder.shouldIgnore()) {
4286                holder.clearOldPosition();
4287            }
4288        }
4289        mRecycler.clearOldPositions();
4290    }
4291
4292    void offsetPositionRecordsForMove(int from, int to) {
4293        final int childCount = mChildHelper.getUnfilteredChildCount();
4294        final int start, end, inBetweenOffset;
4295        if (from < to) {
4296            start = from;
4297            end = to;
4298            inBetweenOffset = -1;
4299        } else {
4300            start = to;
4301            end = from;
4302            inBetweenOffset = 1;
4303        }
4304
4305        for (int i = 0; i < childCount; i++) {
4306            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4307            if (holder == null || holder.mPosition < start || holder.mPosition > end) {
4308                continue;
4309            }
4310            if (DEBUG) {
4311                Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
4312                        + holder);
4313            }
4314            if (holder.mPosition == from) {
4315                holder.offsetPosition(to - from, false);
4316            } else {
4317                holder.offsetPosition(inBetweenOffset, false);
4318            }
4319
4320            mState.mStructureChanged = true;
4321        }
4322        mRecycler.offsetPositionRecordsForMove(from, to);
4323        requestLayout();
4324    }
4325
4326    void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
4327        final int childCount = mChildHelper.getUnfilteredChildCount();
4328        for (int i = 0; i < childCount; i++) {
4329            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4330            if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
4331                if (DEBUG) {
4332                    Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
4333                            + holder + " now at position " + (holder.mPosition + itemCount));
4334                }
4335                holder.offsetPosition(itemCount, false);
4336                mState.mStructureChanged = true;
4337            }
4338        }
4339        mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
4340        requestLayout();
4341    }
4342
4343    void offsetPositionRecordsForRemove(int positionStart, int itemCount,
4344            boolean applyToPreLayout) {
4345        final int positionEnd = positionStart + itemCount;
4346        final int childCount = mChildHelper.getUnfilteredChildCount();
4347        for (int i = 0; i < childCount; i++) {
4348            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4349            if (holder != null && !holder.shouldIgnore()) {
4350                if (holder.mPosition >= positionEnd) {
4351                    if (DEBUG) {
4352                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4353                                + " holder " + holder + " now at position "
4354                                + (holder.mPosition - itemCount));
4355                    }
4356                    holder.offsetPosition(-itemCount, applyToPreLayout);
4357                    mState.mStructureChanged = true;
4358                } else if (holder.mPosition >= positionStart) {
4359                    if (DEBUG) {
4360                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4361                                + " holder " + holder + " now REMOVED");
4362                    }
4363                    holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
4364                            applyToPreLayout);
4365                    mState.mStructureChanged = true;
4366                }
4367            }
4368        }
4369        mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
4370        requestLayout();
4371    }
4372
4373    /**
4374     * Rebind existing views for the given range, or create as needed.
4375     *
4376     * @param positionStart Adapter position to start at
4377     * @param itemCount Number of views that must explicitly be rebound
4378     */
4379    void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
4380        final int childCount = mChildHelper.getUnfilteredChildCount();
4381        final int positionEnd = positionStart + itemCount;
4382
4383        for (int i = 0; i < childCount; i++) {
4384            final View child = mChildHelper.getUnfilteredChildAt(i);
4385            final ViewHolder holder = getChildViewHolderInt(child);
4386            if (holder == null || holder.shouldIgnore()) {
4387                continue;
4388            }
4389            if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
4390                // We re-bind these view holders after pre-processing is complete so that
4391                // ViewHolders have their final positions assigned.
4392                holder.addFlags(ViewHolder.FLAG_UPDATE);
4393                holder.addChangePayload(payload);
4394                // lp cannot be null since we get ViewHolder from it.
4395                ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
4396            }
4397        }
4398        mRecycler.viewRangeUpdate(positionStart, itemCount);
4399    }
4400
4401    boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
4402        return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
4403                viewHolder.getUnmodifiedPayloads());
4404    }
4405
4406    /**
4407     * Processes the fact that, as far as we can tell, the data set has completely changed.
4408     *
4409     * <ul>
4410     *   <li>Once layout occurs, all attached items should be discarded or animated.
4411     *   <li>Attached items are labeled as invalid.
4412     *   <li>Because items may still be prefetched between a "data set completely changed"
4413     *       event and a layout event, all cached items are discarded.
4414     * </ul>
4415     *
4416     * @param dispatchItemsChanged Whether to call
4417     * {@link LayoutManager#onItemsChanged(RecyclerView)} during measure/layout.
4418     */
4419    void processDataSetCompletelyChanged(boolean dispatchItemsChanged) {
4420        mDispatchItemsChangedEvent |= dispatchItemsChanged;
4421        mDataSetHasChangedAfterLayout = true;
4422        markKnownViewsInvalid();
4423    }
4424
4425    /**
4426     * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
4427     * data change event.
4428     */
4429    void markKnownViewsInvalid() {
4430        final int childCount = mChildHelper.getUnfilteredChildCount();
4431        for (int i = 0; i < childCount; i++) {
4432            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4433            if (holder != null && !holder.shouldIgnore()) {
4434                holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
4435            }
4436        }
4437        markItemDecorInsetsDirty();
4438        mRecycler.markKnownViewsInvalid();
4439    }
4440
4441    /**
4442     * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
4443     * will trigger a {@link #requestLayout()} call.
4444     */
4445    public void invalidateItemDecorations() {
4446        if (mItemDecorations.size() == 0) {
4447            return;
4448        }
4449        if (mLayout != null) {
4450            mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
4451                    + " or layout");
4452        }
4453        markItemDecorInsetsDirty();
4454        requestLayout();
4455    }
4456
4457    /**
4458     * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
4459     * focus even if the View representing the Item is replaced during a layout calculation.
4460     * <p>
4461     * By default, this value is {@code true}.
4462     *
4463     * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
4464     * focus.
4465     *
4466     * @see #setPreserveFocusAfterLayout(boolean)
4467     */
4468    public boolean getPreserveFocusAfterLayout() {
4469        return mPreserveFocusAfterLayout;
4470    }
4471
4472    /**
4473     * Set whether the RecyclerView should try to keep the same Item focused after a layout
4474     * calculation or not.
4475     * <p>
4476     * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
4477     * views may lose focus during a layout calculation as their state changes or they are replaced
4478     * with another view due to type change or animation. In these cases, RecyclerView can request
4479     * focus on the new view automatically.
4480     *
4481     * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
4482     *                                 layout calculations. Defaults to true.
4483     *
4484     * @see #getPreserveFocusAfterLayout()
4485     */
4486    public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
4487        mPreserveFocusAfterLayout = preserveFocusAfterLayout;
4488    }
4489
4490    /**
4491     * Retrieve the {@link ViewHolder} for the given child view.
4492     *
4493     * @param child Child of this RecyclerView to query for its ViewHolder
4494     * @return The child view's ViewHolder
4495     */
4496    public ViewHolder getChildViewHolder(@NonNull View child) {
4497        final ViewParent parent = child.getParent();
4498        if (parent != null && parent != this) {
4499            throw new IllegalArgumentException("View " + child + " is not a direct child of "
4500                    + this);
4501        }
4502        return getChildViewHolderInt(child);
4503    }
4504
4505    /**
4506     * Traverses the ancestors of the given view and returns the item view that contains it and
4507     * also a direct child of the RecyclerView. This returned view can be used to get the
4508     * ViewHolder by calling {@link #getChildViewHolder(View)}.
4509     *
4510     * @param view The view that is a descendant of the RecyclerView.
4511     *
4512     * @return The direct child of the RecyclerView which contains the given view or null if the
4513     * provided view is not a descendant of this RecyclerView.
4514     *
4515     * @see #getChildViewHolder(View)
4516     * @see #findContainingViewHolder(View)
4517     */
4518    @Nullable
4519    public View findContainingItemView(@NonNull View view) {
4520        ViewParent parent = view.getParent();
4521        while (parent != null && parent != this && parent instanceof View) {
4522            view = (View) parent;
4523            parent = view.getParent();
4524        }
4525        return parent == this ? view : null;
4526    }
4527
4528    /**
4529     * Returns the ViewHolder that contains the given view.
4530     *
4531     * @param view The view that is a descendant of the RecyclerView.
4532     *
4533     * @return The ViewHolder that contains the given view or null if the provided view is not a
4534     * descendant of this RecyclerView.
4535     */
4536    @Nullable
4537    public ViewHolder findContainingViewHolder(@NonNull View view) {
4538        View itemView = findContainingItemView(view);
4539        return itemView == null ? null : getChildViewHolder(itemView);
4540    }
4541
4542
4543    static ViewHolder getChildViewHolderInt(View child) {
4544        if (child == null) {
4545            return null;
4546        }
4547        return ((LayoutParams) child.getLayoutParams()).mViewHolder;
4548    }
4549
4550    /**
4551     * @deprecated use {@link #getChildAdapterPosition(View)} or
4552     * {@link #getChildLayoutPosition(View)}.
4553     */
4554    @Deprecated
4555    public int getChildPosition(@NonNull View child) {
4556        return getChildAdapterPosition(child);
4557    }
4558
4559    /**
4560     * Return the adapter position that the given child view corresponds to.
4561     *
4562     * @param child Child View to query
4563     * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
4564     */
4565    public int getChildAdapterPosition(@NonNull View child) {
4566        final ViewHolder holder = getChildViewHolderInt(child);
4567        return holder != null ? holder.getAdapterPosition() : NO_POSITION;
4568    }
4569
4570    /**
4571     * Return the adapter position of the given child view as of the latest completed layout pass.
4572     * <p>
4573     * This position may not be equal to Item's adapter position if there are pending changes
4574     * in the adapter which have not been reflected to the layout yet.
4575     *
4576     * @param child Child View to query
4577     * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
4578     * the View is representing a removed item.
4579     */
4580    public int getChildLayoutPosition(@NonNull View child) {
4581        final ViewHolder holder = getChildViewHolderInt(child);
4582        return holder != null ? holder.getLayoutPosition() : NO_POSITION;
4583    }
4584
4585    /**
4586     * Return the stable item id that the given child view corresponds to.
4587     *
4588     * @param child Child View to query
4589     * @return Item id corresponding to the given view or {@link #NO_ID}
4590     */
4591    public long getChildItemId(@NonNull View child) {
4592        if (mAdapter == null || !mAdapter.hasStableIds()) {
4593            return NO_ID;
4594        }
4595        final ViewHolder holder = getChildViewHolderInt(child);
4596        return holder != null ? holder.getItemId() : NO_ID;
4597    }
4598
4599    /**
4600     * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
4601     * {@link #findViewHolderForAdapterPosition(int)}
4602     */
4603    @Deprecated
4604    public ViewHolder findViewHolderForPosition(int position) {
4605        return findViewHolderForPosition(position, false);
4606    }
4607
4608    /**
4609     * Return the ViewHolder for the item in the given position of the data set as of the latest
4610     * layout pass.
4611     * <p>
4612     * This method checks only the children of RecyclerView. If the item at the given
4613     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4614     * <p>
4615     * Note that when Adapter contents change, ViewHolder positions are not updated until the
4616     * next layout calculation. If there are pending adapter updates, the return value of this
4617     * method may not match your adapter contents. You can use
4618     * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
4619     * <p>
4620     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4621     * with the same layout position representing the same Item. In this case, the updated
4622     * ViewHolder will be returned.
4623     *
4624     * @param position The position of the item in the data set of the adapter
4625     * @return The ViewHolder at <code>position</code> or null if there is no such item
4626     */
4627    public ViewHolder findViewHolderForLayoutPosition(int position) {
4628        return findViewHolderForPosition(position, false);
4629    }
4630
4631    /**
4632     * Return the ViewHolder for the item in the given position of the data set. Unlike
4633     * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
4634     * adapter changes that may not be reflected to the layout yet. On the other hand, if
4635     * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
4636     * calculated yet, this method will return <code>null</code> since the new positions of views
4637     * are unknown until the layout is calculated.
4638     * <p>
4639     * This method checks only the children of RecyclerView. If the item at the given
4640     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4641     * <p>
4642     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4643     * representing the same Item. In this case, the updated ViewHolder will be returned.
4644     *
4645     * @param position The position of the item in the data set of the adapter
4646     * @return The ViewHolder at <code>position</code> or null if there is no such item
4647     */
4648    public ViewHolder findViewHolderForAdapterPosition(int position) {
4649        if (mDataSetHasChangedAfterLayout) {
4650            return null;
4651        }
4652        final int childCount = mChildHelper.getUnfilteredChildCount();
4653        // hidden VHs are not preferred but if that is the only one we find, we rather return it
4654        ViewHolder hidden = null;
4655        for (int i = 0; i < childCount; i++) {
4656            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4657            if (holder != null && !holder.isRemoved()
4658                    && getAdapterPositionFor(holder) == position) {
4659                if (mChildHelper.isHidden(holder.itemView)) {
4660                    hidden = holder;
4661                } else {
4662                    return holder;
4663                }
4664            }
4665        }
4666        return hidden;
4667    }
4668
4669    ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
4670        final int childCount = mChildHelper.getUnfilteredChildCount();
4671        ViewHolder hidden = null;
4672        for (int i = 0; i < childCount; i++) {
4673            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4674            if (holder != null && !holder.isRemoved()) {
4675                if (checkNewPosition) {
4676                    if (holder.mPosition != position) {
4677                        continue;
4678                    }
4679                } else if (holder.getLayoutPosition() != position) {
4680                    continue;
4681                }
4682                if (mChildHelper.isHidden(holder.itemView)) {
4683                    hidden = holder;
4684                } else {
4685                    return holder;
4686                }
4687            }
4688        }
4689        // This method should not query cached views. It creates a problem during adapter updates
4690        // when we are dealing with already laid out views. Also, for the public method, it is more
4691        // reasonable to return null if position is not laid out.
4692        return hidden;
4693    }
4694
4695    /**
4696     * Return the ViewHolder for the item with the given id. The RecyclerView must
4697     * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
4698     * return a non-null value.
4699     * <p>
4700     * This method checks only the children of RecyclerView. If the item with the given
4701     * <code>id</code> is not laid out, it <em>will not</em> create a new one.
4702     *
4703     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
4704     * same id. In this case, the updated ViewHolder will be returned.
4705     *
4706     * @param id The id for the requested item
4707     * @return The ViewHolder with the given <code>id</code> or null if there is no such item
4708     */
4709    public ViewHolder findViewHolderForItemId(long id) {
4710        if (mAdapter == null || !mAdapter.hasStableIds()) {
4711            return null;
4712        }
4713        final int childCount = mChildHelper.getUnfilteredChildCount();
4714        ViewHolder hidden = null;
4715        for (int i = 0; i < childCount; i++) {
4716            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4717            if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
4718                if (mChildHelper.isHidden(holder.itemView)) {
4719                    hidden = holder;
4720                } else {
4721                    return holder;
4722                }
4723            }
4724        }
4725        return hidden;
4726    }
4727
4728    /**
4729     * Find the topmost view under the given point.
4730     *
4731     * @param x Horizontal position in pixels to search
4732     * @param y Vertical position in pixels to search
4733     * @return The child view under (x, y) or null if no matching child is found
4734     */
4735    @Nullable
4736    public View findChildViewUnder(float x, float y) {
4737        final int count = mChildHelper.getChildCount();
4738        for (int i = count - 1; i >= 0; i--) {
4739            final View child = mChildHelper.getChildAt(i);
4740            final float translationX = child.getTranslationX();
4741            final float translationY = child.getTranslationY();
4742            if (x >= child.getLeft() + translationX
4743                    && x <= child.getRight() + translationX
4744                    && y >= child.getTop() + translationY
4745                    && y <= child.getBottom() + translationY) {
4746                return child;
4747            }
4748        }
4749        return null;
4750    }
4751
4752    @Override
4753    public boolean drawChild(Canvas canvas, View child, long drawingTime) {
4754        return super.drawChild(canvas, child, drawingTime);
4755    }
4756
4757    /**
4758     * Offset the bounds of all child views by <code>dy</code> pixels.
4759     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4760     *
4761     * @param dy Vertical pixel offset to apply to the bounds of all child views
4762     */
4763    public void offsetChildrenVertical(@Px int dy) {
4764        final int childCount = mChildHelper.getChildCount();
4765        for (int i = 0; i < childCount; i++) {
4766            mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
4767        }
4768    }
4769
4770    /**
4771     * Called when an item view is attached to this RecyclerView.
4772     *
4773     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4774     * of child views as they become attached. This will be called before a
4775     * {@link LayoutManager} measures or lays out the view and is a good time to perform these
4776     * changes.</p>
4777     *
4778     * @param child Child view that is now attached to this RecyclerView and its associated window
4779     */
4780    public void onChildAttachedToWindow(@NonNull View child) {
4781    }
4782
4783    /**
4784     * Called when an item view is detached from this RecyclerView.
4785     *
4786     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4787     * of child views as they become detached. This will be called as a
4788     * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
4789     *
4790     * @param child Child view that is now detached from this RecyclerView and its associated window
4791     */
4792    public void onChildDetachedFromWindow(@NonNull View child) {
4793    }
4794
4795    /**
4796     * Offset the bounds of all child views by <code>dx</code> pixels.
4797     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4798     *
4799     * @param dx Horizontal pixel offset to apply to the bounds of all child views
4800     */
4801    public void offsetChildrenHorizontal(@Px int dx) {
4802        final int childCount = mChildHelper.getChildCount();
4803        for (int i = 0; i < childCount; i++) {
4804            mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
4805        }
4806    }
4807
4808    /**
4809     * Returns the bounds of the view including its decoration and margins.
4810     *
4811     * @param view The view element to check
4812     * @param outBounds A rect that will receive the bounds of the element including its
4813     *                  decoration and margins.
4814     */
4815    public void getDecoratedBoundsWithMargins(@NonNull View view, @NonNull Rect outBounds) {
4816        getDecoratedBoundsWithMarginsInt(view, outBounds);
4817    }
4818
4819    static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
4820        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
4821        final Rect insets = lp.mDecorInsets;
4822        outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
4823                view.getTop() - insets.top - lp.topMargin,
4824                view.getRight() + insets.right + lp.rightMargin,
4825                view.getBottom() + insets.bottom + lp.bottomMargin);
4826    }
4827
4828    Rect getItemDecorInsetsForChild(View child) {
4829        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
4830        if (!lp.mInsetsDirty) {
4831            return lp.mDecorInsets;
4832        }
4833
4834        if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
4835            // changed/invalid items should not be updated until they are rebound.
4836            return lp.mDecorInsets;
4837        }
4838        final Rect insets = lp.mDecorInsets;
4839        insets.set(0, 0, 0, 0);
4840        final int decorCount = mItemDecorations.size();
4841        for (int i = 0; i < decorCount; i++) {
4842            mTempRect.set(0, 0, 0, 0);
4843            mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
4844            insets.left += mTempRect.left;
4845            insets.top += mTempRect.top;
4846            insets.right += mTempRect.right;
4847            insets.bottom += mTempRect.bottom;
4848        }
4849        lp.mInsetsDirty = false;
4850        return insets;
4851    }
4852
4853    /**
4854     * Called when the scroll position of this RecyclerView changes. Subclasses should use
4855     * this method to respond to scrolling within the adapter's data set instead of an explicit
4856     * listener.
4857     *
4858     * <p>This method will always be invoked before listeners. If a subclass needs to perform
4859     * any additional upkeep or bookkeeping after scrolling but before listeners run,
4860     * this is a good place to do so.</p>
4861     *
4862     * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
4863     * the distance scrolled in either direction within the adapter's data set instead of absolute
4864     * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
4865     * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
4866     * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
4867     * do not correspond to the data set scroll position. However, some subclasses may choose
4868     * to use these fields as special offsets.</p>
4869     *
4870     * @param dx horizontal distance scrolled in pixels
4871     * @param dy vertical distance scrolled in pixels
4872     */
4873    public void onScrolled(@Px int dx, @Px int dy) {
4874        // Do nothing
4875    }
4876
4877    void dispatchOnScrolled(int hresult, int vresult) {
4878        mDispatchScrollCounter++;
4879        // Pass the current scrollX/scrollY values; no actual change in these properties occurred
4880        // but some general-purpose code may choose to respond to changes this way.
4881        final int scrollX = getScrollX();
4882        final int scrollY = getScrollY();
4883        onScrollChanged(scrollX, scrollY, scrollX, scrollY);
4884
4885        // Pass the real deltas to onScrolled, the RecyclerView-specific method.
4886        onScrolled(hresult, vresult);
4887
4888        // Invoke listeners last. Subclassed view methods always handle the event first.
4889        // All internal state is consistent by the time listeners are invoked.
4890        if (mScrollListener != null) {
4891            mScrollListener.onScrolled(this, hresult, vresult);
4892        }
4893        if (mScrollListeners != null) {
4894            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4895                mScrollListeners.get(i).onScrolled(this, hresult, vresult);
4896            }
4897        }
4898        mDispatchScrollCounter--;
4899    }
4900
4901    /**
4902     * Called when the scroll state of this RecyclerView changes. Subclasses should use this
4903     * method to respond to state changes instead of an explicit listener.
4904     *
4905     * <p>This method will always be invoked before listeners, but after the LayoutManager
4906     * responds to the scroll state change.</p>
4907     *
4908     * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
4909     *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
4910     */
4911    public void onScrollStateChanged(int state) {
4912        // Do nothing
4913    }
4914
4915    void dispatchOnScrollStateChanged(int state) {
4916        // Let the LayoutManager go first; this allows it to bring any properties into
4917        // a consistent state before the RecyclerView subclass responds.
4918        if (mLayout != null) {
4919            mLayout.onScrollStateChanged(state);
4920        }
4921
4922        // Let the RecyclerView subclass handle this event next; any LayoutManager property
4923        // changes will be reflected by this time.
4924        onScrollStateChanged(state);
4925
4926        // Listeners go last. All other internal state is consistent by this point.
4927        if (mScrollListener != null) {
4928            mScrollListener.onScrollStateChanged(this, state);
4929        }
4930        if (mScrollListeners != null) {
4931            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4932                mScrollListeners.get(i).onScrollStateChanged(this, state);
4933            }
4934        }
4935    }
4936
4937    /**
4938     * Returns whether there are pending adapter updates which are not yet applied to the layout.
4939     * <p>
4940     * If this method returns <code>true</code>, it means that what user is currently seeing may not
4941     * reflect them adapter contents (depending on what has changed).
4942     * You may use this information to defer or cancel some operations.
4943     * <p>
4944     * This method returns true if RecyclerView has not yet calculated the first layout after it is
4945     * attached to the Window or the Adapter has been replaced.
4946     *
4947     * @return True if there are some adapter updates which are not yet reflected to layout or false
4948     * if layout is up to date.
4949     */
4950    public boolean hasPendingAdapterUpdates() {
4951        return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
4952                || mAdapterHelper.hasPendingUpdates();
4953    }
4954
4955    class ViewFlinger implements Runnable {
4956        private int mLastFlingX;
4957        private int mLastFlingY;
4958        private OverScroller mScroller;
4959        Interpolator mInterpolator = sQuinticInterpolator;
4960
4961        // When set to true, postOnAnimation callbacks are delayed until the run method completes
4962        private boolean mEatRunOnAnimationRequest = false;
4963
4964        // Tracks if postAnimationCallback should be re-attached when it is done
4965        private boolean mReSchedulePostAnimationCallback = false;
4966
4967        ViewFlinger() {
4968            mScroller = new OverScroller(getContext(), sQuinticInterpolator);
4969        }
4970
4971        @Override
4972        public void run() {
4973            if (mLayout == null) {
4974                stop();
4975                return; // no layout, cannot scroll.
4976            }
4977            disableRunOnAnimationRequests();
4978            consumePendingUpdateOperations();
4979            // keep a local reference so that if it is changed during onAnimation method, it won't
4980            // cause unexpected behaviors
4981            final OverScroller scroller = mScroller;
4982            final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
4983            if (scroller.computeScrollOffset()) {
4984                final int[] scrollConsumed = mScrollConsumed;
4985                final int x = scroller.getCurrX();
4986                final int y = scroller.getCurrY();
4987                int dx = x - mLastFlingX;
4988                int dy = y - mLastFlingY;
4989                int hresult = 0;
4990                int vresult = 0;
4991                mLastFlingX = x;
4992                mLastFlingY = y;
4993                int overscrollX = 0, overscrollY = 0;
4994
4995                if (dispatchNestedPreScroll(dx, dy, scrollConsumed, null, TYPE_NON_TOUCH)) {
4996                    dx -= scrollConsumed[0];
4997                    dy -= scrollConsumed[1];
4998                }
4999
5000                if (mAdapter != null) {
5001                    scrollStep(dx, dy, mScrollStepConsumed);
5002                    hresult = mScrollStepConsumed[0];
5003                    vresult = mScrollStepConsumed[1];
5004                    overscrollX = dx - hresult;
5005                    overscrollY = dy - vresult;
5006
5007                    if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
5008                            && smoothScroller.isRunning()) {
5009                        final int adapterSize = mState.getItemCount();
5010                        if (adapterSize == 0) {
5011                            smoothScroller.stop();
5012                        } else if (smoothScroller.getTargetPosition() >= adapterSize) {
5013                            smoothScroller.setTargetPosition(adapterSize - 1);
5014                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
5015                        } else {
5016                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
5017                        }
5018                    }
5019                }
5020                if (!mItemDecorations.isEmpty()) {
5021                    invalidate();
5022                }
5023                if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
5024                    considerReleasingGlowsOnScroll(dx, dy);
5025                }
5026
5027                if (!dispatchNestedScroll(hresult, vresult, overscrollX, overscrollY, null,
5028                        TYPE_NON_TOUCH)
5029                        && (overscrollX != 0 || overscrollY != 0)) {
5030                    final int vel = (int) scroller.getCurrVelocity();
5031
5032                    int velX = 0;
5033                    if (overscrollX != x) {
5034                        velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
5035                    }
5036
5037                    int velY = 0;
5038                    if (overscrollY != y) {
5039                        velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
5040                    }
5041
5042                    if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
5043                        absorbGlows(velX, velY);
5044                    }
5045                    if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
5046                            && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
5047                        scroller.abortAnimation();
5048                    }
5049                }
5050                if (hresult != 0 || vresult != 0) {
5051                    dispatchOnScrolled(hresult, vresult);
5052                }
5053
5054                if (!awakenScrollBars()) {
5055                    invalidate();
5056                }
5057
5058                final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
5059                        && vresult == dy;
5060                final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
5061                        && hresult == dx;
5062                final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
5063                        || fullyConsumedVertical;
5064
5065                if (scroller.isFinished() || (!fullyConsumedAny
5066                        && !hasNestedScrollingParent(TYPE_NON_TOUCH))) {
5067                    // setting state to idle will stop this.
5068                    setScrollState(SCROLL_STATE_IDLE);
5069                    if (ALLOW_THREAD_GAP_WORK) {
5070                        mPrefetchRegistry.clearPrefetchPositions();
5071                    }
5072                    stopNestedScroll(TYPE_NON_TOUCH);
5073                } else {
5074                    postOnAnimation();
5075                    if (mGapWorker != null) {
5076                        mGapWorker.postFromTraversal(RecyclerView.this, dx, dy);
5077                    }
5078                }
5079            }
5080            // call this after the onAnimation is complete not to have inconsistent callbacks etc.
5081            if (smoothScroller != null) {
5082                if (smoothScroller.isPendingInitialRun()) {
5083                    smoothScroller.onAnimation(0, 0);
5084                }
5085                if (!mReSchedulePostAnimationCallback) {
5086                    smoothScroller.stop(); //stop if it does not trigger any scroll
5087                }
5088            }
5089            enableRunOnAnimationRequests();
5090        }
5091
5092        private void disableRunOnAnimationRequests() {
5093            mReSchedulePostAnimationCallback = false;
5094            mEatRunOnAnimationRequest = true;
5095        }
5096
5097        private void enableRunOnAnimationRequests() {
5098            mEatRunOnAnimationRequest = false;
5099            if (mReSchedulePostAnimationCallback) {
5100                postOnAnimation();
5101            }
5102        }
5103
5104        void postOnAnimation() {
5105            if (mEatRunOnAnimationRequest) {
5106                mReSchedulePostAnimationCallback = true;
5107            } else {
5108                removeCallbacks(this);
5109                ViewCompat.postOnAnimation(RecyclerView.this, this);
5110            }
5111        }
5112
5113        public void fling(int velocityX, int velocityY) {
5114            setScrollState(SCROLL_STATE_SETTLING);
5115            mLastFlingX = mLastFlingY = 0;
5116            mScroller.fling(0, 0, velocityX, velocityY,
5117                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
5118            postOnAnimation();
5119        }
5120
5121        public void smoothScrollBy(int dx, int dy) {
5122            smoothScrollBy(dx, dy, 0, 0);
5123        }
5124
5125        public void smoothScrollBy(int dx, int dy, int vx, int vy) {
5126            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
5127        }
5128
5129        private float distanceInfluenceForSnapDuration(float f) {
5130            f -= 0.5f; // center the values about 0.
5131            f *= 0.3f * (float) Math.PI / 2.0f;
5132            return (float) Math.sin(f);
5133        }
5134
5135        private int computeScrollDuration(int dx, int dy, int vx, int vy) {
5136            final int absDx = Math.abs(dx);
5137            final int absDy = Math.abs(dy);
5138            final boolean horizontal = absDx > absDy;
5139            final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
5140            final int delta = (int) Math.sqrt(dx * dx + dy * dy);
5141            final int containerSize = horizontal ? getWidth() : getHeight();
5142            final int halfContainerSize = containerSize / 2;
5143            final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
5144            final float distance = halfContainerSize + halfContainerSize
5145                    * distanceInfluenceForSnapDuration(distanceRatio);
5146
5147            final int duration;
5148            if (velocity > 0) {
5149                duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
5150            } else {
5151                float absDelta = (float) (horizontal ? absDx : absDy);
5152                duration = (int) (((absDelta / containerSize) + 1) * 300);
5153            }
5154            return Math.min(duration, MAX_SCROLL_DURATION);
5155        }
5156
5157        public void smoothScrollBy(int dx, int dy, int duration) {
5158            smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
5159        }
5160
5161        public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
5162            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, 0, 0),
5163                    interpolator == null ? sQuinticInterpolator : interpolator);
5164        }
5165
5166        public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
5167            if (mInterpolator != interpolator) {
5168                mInterpolator = interpolator;
5169                mScroller = new OverScroller(getContext(), interpolator);
5170            }
5171            setScrollState(SCROLL_STATE_SETTLING);
5172            mLastFlingX = mLastFlingY = 0;
5173            mScroller.startScroll(0, 0, dx, dy, duration);
5174            if (Build.VERSION.SDK_INT < 23) {
5175                // b/64931938 before API 23, startScroll() does not reset getCurX()/getCurY()
5176                // to start values, which causes fillRemainingScrollValues() put in obsolete values
5177                // for LayoutManager.onLayoutChildren().
5178                mScroller.computeScrollOffset();
5179            }
5180            postOnAnimation();
5181        }
5182
5183        public void stop() {
5184            removeCallbacks(this);
5185            mScroller.abortAnimation();
5186        }
5187
5188    }
5189
5190    void repositionShadowingViews() {
5191        // Fix up shadow views used by change animations
5192        int count = mChildHelper.getChildCount();
5193        for (int i = 0; i < count; i++) {
5194            View view = mChildHelper.getChildAt(i);
5195            ViewHolder holder = getChildViewHolder(view);
5196            if (holder != null && holder.mShadowingHolder != null) {
5197                View shadowingView = holder.mShadowingHolder.itemView;
5198                int left = view.getLeft();
5199                int top = view.getTop();
5200                if (left != shadowingView.getLeft() ||  top != shadowingView.getTop()) {
5201                    shadowingView.layout(left, top,
5202                            left + shadowingView.getWidth(),
5203                            top + shadowingView.getHeight());
5204                }
5205            }
5206        }
5207    }
5208
5209    private class RecyclerViewDataObserver extends AdapterDataObserver {
5210        RecyclerViewDataObserver() {
5211        }
5212
5213        @Override
5214        public void onChanged() {
5215            assertNotInLayoutOrScroll(null);
5216            mState.mStructureChanged = true;
5217
5218            processDataSetCompletelyChanged(true);
5219            if (!mAdapterHelper.hasPendingUpdates()) {
5220                requestLayout();
5221            }
5222        }
5223
5224        @Override
5225        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
5226            assertNotInLayoutOrScroll(null);
5227            if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
5228                triggerUpdateProcessor();
5229            }
5230        }
5231
5232        @Override
5233        public void onItemRangeInserted(int positionStart, int itemCount) {
5234            assertNotInLayoutOrScroll(null);
5235            if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
5236                triggerUpdateProcessor();
5237            }
5238        }
5239
5240        @Override
5241        public void onItemRangeRemoved(int positionStart, int itemCount) {
5242            assertNotInLayoutOrScroll(null);
5243            if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
5244                triggerUpdateProcessor();
5245            }
5246        }
5247
5248        @Override
5249        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
5250            assertNotInLayoutOrScroll(null);
5251            if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
5252                triggerUpdateProcessor();
5253            }
5254        }
5255
5256        void triggerUpdateProcessor() {
5257            if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
5258                ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
5259            } else {
5260                mAdapterUpdateDuringMeasure = true;
5261                requestLayout();
5262            }
5263        }
5264    }
5265
5266    /**
5267     * EdgeEffectFactory lets you customize the over-scroll edge effect for RecyclerViews.
5268     *
5269     * @see RecyclerView#setEdgeEffectFactory(EdgeEffectFactory)
5270     */
5271    public static class EdgeEffectFactory {
5272
5273        @Retention(RetentionPolicy.SOURCE)
5274        @IntDef({DIRECTION_LEFT, DIRECTION_TOP, DIRECTION_RIGHT, DIRECTION_BOTTOM})
5275        public @interface EdgeDirection {}
5276
5277        /**
5278         * Direction constant for the left edge
5279         */
5280        public static final int DIRECTION_LEFT = 0;
5281
5282        /**
5283         * Direction constant for the top edge
5284         */
5285        public static final int DIRECTION_TOP = 1;
5286
5287        /**
5288         * Direction constant for the right edge
5289         */
5290        public static final int DIRECTION_RIGHT = 2;
5291
5292        /**
5293         * Direction constant for the bottom edge
5294         */
5295        public static final int DIRECTION_BOTTOM = 3;
5296
5297        /**
5298         * Create a new EdgeEffect for the provided direction.
5299         */
5300        protected @NonNull EdgeEffect createEdgeEffect(@NonNull RecyclerView view,
5301                @EdgeDirection int direction) {
5302            return new EdgeEffect(view.getContext());
5303        }
5304    }
5305
5306    /**
5307     * RecycledViewPool lets you share Views between multiple RecyclerViews.
5308     * <p>
5309     * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
5310     * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
5311     * <p>
5312     * RecyclerView automatically creates a pool for itself if you don't provide one.
5313     */
5314    public static class RecycledViewPool {
5315        private static final int DEFAULT_MAX_SCRAP = 5;
5316
5317        /**
5318         * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
5319         *
5320         * Note that this tracks running averages of create/bind time across all RecyclerViews
5321         * (and, indirectly, Adapters) that use this pool.
5322         *
5323         * 1) This enables us to track average create and bind times across multiple adapters. Even
5324         * though create (and especially bind) may behave differently for different Adapter
5325         * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
5326         *
5327         * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
5328         * false for all other views of its type for the same deadline. This prevents items
5329         * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
5330         */
5331        static class ScrapData {
5332            final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
5333            int mMaxScrap = DEFAULT_MAX_SCRAP;
5334            long mCreateRunningAverageNs = 0;
5335            long mBindRunningAverageNs = 0;
5336        }
5337        SparseArray<ScrapData> mScrap = new SparseArray<>();
5338
5339        private int mAttachCount = 0;
5340
5341        /**
5342         * Discard all ViewHolders.
5343         */
5344        public void clear() {
5345            for (int i = 0; i < mScrap.size(); i++) {
5346                ScrapData data = mScrap.valueAt(i);
5347                data.mScrapHeap.clear();
5348            }
5349        }
5350
5351        /**
5352         * Sets the maximum number of ViewHolders to hold in the pool before discarding.
5353         *
5354         * @param viewType ViewHolder Type
5355         * @param max Maximum number
5356         */
5357        public void setMaxRecycledViews(int viewType, int max) {
5358            ScrapData scrapData = getScrapDataForType(viewType);
5359            scrapData.mMaxScrap = max;
5360            final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
5361            while (scrapHeap.size() > max) {
5362                scrapHeap.remove(scrapHeap.size() - 1);
5363            }
5364        }
5365
5366        /**
5367         * Returns the current number of Views held by the RecycledViewPool of the given view type.
5368         */
5369        public int getRecycledViewCount(int viewType) {
5370            return getScrapDataForType(viewType).mScrapHeap.size();
5371        }
5372
5373        /**
5374         * Acquire a ViewHolder of the specified type from the pool, or {@code null} if none are
5375         * present.
5376         *
5377         * @param viewType ViewHolder type.
5378         * @return ViewHolder of the specified type acquired from the pool, or {@code null} if none
5379         * are present.
5380         */
5381        @Nullable
5382        public ViewHolder getRecycledView(int viewType) {
5383            final ScrapData scrapData = mScrap.get(viewType);
5384            if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
5385                final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
5386                return scrapHeap.remove(scrapHeap.size() - 1);
5387            }
5388            return null;
5389        }
5390
5391        /**
5392         * Total number of ViewHolders held by the pool.
5393         *
5394         * @return Number of ViewHolders held by the pool.
5395         */
5396        int size() {
5397            int count = 0;
5398            for (int i = 0; i < mScrap.size(); i++) {
5399                ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
5400                if (viewHolders != null) {
5401                    count += viewHolders.size();
5402                }
5403            }
5404            return count;
5405        }
5406
5407        /**
5408         * Add a scrap ViewHolder to the pool.
5409         * <p>
5410         * If the pool is already full for that ViewHolder's type, it will be immediately discarded.
5411         *
5412         * @param scrap ViewHolder to be added to the pool.
5413         */
5414        public void putRecycledView(ViewHolder scrap) {
5415            final int viewType = scrap.getItemViewType();
5416            final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
5417            if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
5418                return;
5419            }
5420            if (DEBUG && scrapHeap.contains(scrap)) {
5421                throw new IllegalArgumentException("this scrap item already exists");
5422            }
5423            scrap.resetInternal();
5424            scrapHeap.add(scrap);
5425        }
5426
5427        long runningAverage(long oldAverage, long newValue) {
5428            if (oldAverage == 0) {
5429                return newValue;
5430            }
5431            return (oldAverage / 4 * 3) + (newValue / 4);
5432        }
5433
5434        void factorInCreateTime(int viewType, long createTimeNs) {
5435            ScrapData scrapData = getScrapDataForType(viewType);
5436            scrapData.mCreateRunningAverageNs = runningAverage(
5437                    scrapData.mCreateRunningAverageNs, createTimeNs);
5438        }
5439
5440        void factorInBindTime(int viewType, long bindTimeNs) {
5441            ScrapData scrapData = getScrapDataForType(viewType);
5442            scrapData.mBindRunningAverageNs = runningAverage(
5443                    scrapData.mBindRunningAverageNs, bindTimeNs);
5444        }
5445
5446        boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5447            long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
5448            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5449        }
5450
5451        boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5452            long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
5453            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5454        }
5455
5456        void attach() {
5457            mAttachCount++;
5458        }
5459
5460        void detach() {
5461            mAttachCount--;
5462        }
5463
5464
5465        /**
5466         * Detaches the old adapter and attaches the new one.
5467         * <p>
5468         * RecycledViewPool will clear its cache if it has only one adapter attached and the new
5469         * adapter uses a different ViewHolder than the oldAdapter.
5470         *
5471         * @param oldAdapter The previous adapter instance. Will be detached.
5472         * @param newAdapter The new adapter instance. Will be attached.
5473         * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
5474         *                               ViewHolder and view types.
5475         */
5476        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
5477                boolean compatibleWithPrevious) {
5478            if (oldAdapter != null) {
5479                detach();
5480            }
5481            if (!compatibleWithPrevious && mAttachCount == 0) {
5482                clear();
5483            }
5484            if (newAdapter != null) {
5485                attach();
5486            }
5487        }
5488
5489        private ScrapData getScrapDataForType(int viewType) {
5490            ScrapData scrapData = mScrap.get(viewType);
5491            if (scrapData == null) {
5492                scrapData = new ScrapData();
5493                mScrap.put(viewType, scrapData);
5494            }
5495            return scrapData;
5496        }
5497    }
5498
5499    /**
5500     * Utility method for finding an internal RecyclerView, if present
5501     */
5502    @Nullable
5503    static RecyclerView findNestedRecyclerView(@NonNull View view) {
5504        if (!(view instanceof ViewGroup)) {
5505            return null;
5506        }
5507        if (view instanceof RecyclerView) {
5508            return (RecyclerView) view;
5509        }
5510        final ViewGroup parent = (ViewGroup) view;
5511        final int count = parent.getChildCount();
5512        for (int i = 0; i < count; i++) {
5513            final View child = parent.getChildAt(i);
5514            final RecyclerView descendant = findNestedRecyclerView(child);
5515            if (descendant != null) {
5516                return descendant;
5517            }
5518        }
5519        return null;
5520    }
5521
5522    /**
5523     * Utility method for clearing holder's internal RecyclerView, if present
5524     */
5525    static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
5526        if (holder.mNestedRecyclerView != null) {
5527            View item = holder.mNestedRecyclerView.get();
5528            while (item != null) {
5529                if (item == holder.itemView) {
5530                    return; // match found, don't need to clear
5531                }
5532
5533                ViewParent parent = item.getParent();
5534                if (parent instanceof View) {
5535                    item = (View) parent;
5536                } else {
5537                    item = null;
5538                }
5539            }
5540            holder.mNestedRecyclerView = null; // not nested
5541        }
5542    }
5543
5544    /**
5545     * Time base for deadline-aware work scheduling. Overridable for testing.
5546     *
5547     * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
5548     * isn't relevant.
5549     */
5550    long getNanoTime() {
5551        if (ALLOW_THREAD_GAP_WORK) {
5552            return System.nanoTime();
5553        } else {
5554            return 0;
5555        }
5556    }
5557
5558    /**
5559     * A Recycler is responsible for managing scrapped or detached item views for reuse.
5560     *
5561     * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
5562     * that has been marked for removal or reuse.</p>
5563     *
5564     * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
5565     * an adapter's data set representing the data at a given position or item ID.
5566     * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
5567     * If not, the view can be quickly reused by the LayoutManager with no further work.
5568     * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
5569     * may be repositioned by a LayoutManager without remeasurement.</p>
5570     */
5571    public final class Recycler {
5572        final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
5573        ArrayList<ViewHolder> mChangedScrap = null;
5574
5575        final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
5576
5577        private final List<ViewHolder>
5578                mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
5579
5580        private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
5581        int mViewCacheMax = DEFAULT_CACHE_SIZE;
5582
5583        RecycledViewPool mRecyclerPool;
5584
5585        private ViewCacheExtension mViewCacheExtension;
5586
5587        static final int DEFAULT_CACHE_SIZE = 2;
5588
5589        /**
5590         * Clear scrap views out of this recycler. Detached views contained within a
5591         * recycled view pool will remain.
5592         */
5593        public void clear() {
5594            mAttachedScrap.clear();
5595            recycleAndClearCachedViews();
5596        }
5597
5598        /**
5599         * Set the maximum number of detached, valid views we should retain for later use.
5600         *
5601         * @param viewCount Number of views to keep before sending views to the shared pool
5602         */
5603        public void setViewCacheSize(int viewCount) {
5604            mRequestedCacheMax = viewCount;
5605            updateViewCacheSize();
5606        }
5607
5608        void updateViewCacheSize() {
5609            int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
5610            mViewCacheMax = mRequestedCacheMax + extraCache;
5611
5612            // first, try the views that can be recycled
5613            for (int i = mCachedViews.size() - 1;
5614                    i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
5615                recycleCachedViewAt(i);
5616            }
5617        }
5618
5619        /**
5620         * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
5621         *
5622         * @return List of ViewHolders in the scrap list.
5623         */
5624        @NonNull
5625        public List<ViewHolder> getScrapList() {
5626            return mUnmodifiableAttachedScrap;
5627        }
5628
5629        /**
5630         * Helper method for getViewForPosition.
5631         * <p>
5632         * Checks whether a given view holder can be used for the provided position.
5633         *
5634         * @param holder ViewHolder
5635         * @return true if ViewHolder matches the provided position, false otherwise
5636         */
5637        boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
5638            // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
5639            // if it is not removed, verify the type and id.
5640            if (holder.isRemoved()) {
5641                if (DEBUG && !mState.isPreLayout()) {
5642                    throw new IllegalStateException("should not receive a removed view unless it"
5643                            + " is pre layout" + exceptionLabel());
5644                }
5645                return mState.isPreLayout();
5646            }
5647            if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
5648                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
5649                        + "adapter position" + holder + exceptionLabel());
5650            }
5651            if (!mState.isPreLayout()) {
5652                // don't check type if it is pre-layout.
5653                final int type = mAdapter.getItemViewType(holder.mPosition);
5654                if (type != holder.getItemViewType()) {
5655                    return false;
5656                }
5657            }
5658            if (mAdapter.hasStableIds()) {
5659                return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
5660            }
5661            return true;
5662        }
5663
5664        /**
5665         * Attempts to bind view, and account for relevant timing information. If
5666         * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
5667         *
5668         * @param holder Holder to be bound.
5669         * @param offsetPosition Position of item to be bound.
5670         * @param position Pre-layout position of item to be bound.
5671         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5672         *                   complete. If FOREVER_NS is passed, this method will not fail to
5673         *                   bind the holder.
5674         * @return
5675         */
5676        private boolean tryBindViewHolderByDeadline(@NonNull ViewHolder holder, int offsetPosition,
5677                int position, long deadlineNs) {
5678            holder.mOwnerRecyclerView = RecyclerView.this;
5679            final int viewType = holder.getItemViewType();
5680            long startBindNs = getNanoTime();
5681            if (deadlineNs != FOREVER_NS
5682                    && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
5683                // abort - we have a deadline we can't meet
5684                return false;
5685            }
5686            mAdapter.bindViewHolder(holder, offsetPosition);
5687            long endBindNs = getNanoTime();
5688            mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
5689            attachAccessibilityDelegateOnBind(holder);
5690            if (mState.isPreLayout()) {
5691                holder.mPreLayoutPosition = position;
5692            }
5693            return true;
5694        }
5695
5696        /**
5697         * Binds the given View to the position. The View can be a View previously retrieved via
5698         * {@link #getViewForPosition(int)} or created by
5699         * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
5700         * <p>
5701         * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
5702         * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
5703         * wants to handle its own recycling logic.
5704         * <p>
5705         * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
5706         * you don't need to call this method unless you want to bind this View to another position.
5707         *
5708         * @param view The view to update.
5709         * @param position The position of the item to bind to this View.
5710         */
5711        public void bindViewToPosition(@NonNull View view, int position) {
5712            ViewHolder holder = getChildViewHolderInt(view);
5713            if (holder == null) {
5714                throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
5715                        + " pass arbitrary views to this method, they should be created by the "
5716                        + "Adapter" + exceptionLabel());
5717            }
5718            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5719            if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5720                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5721                        + "position " + position + "(offset:" + offsetPosition + ")."
5722                        + "state:" + mState.getItemCount() + exceptionLabel());
5723            }
5724            tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
5725
5726            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5727            final LayoutParams rvLayoutParams;
5728            if (lp == null) {
5729                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5730                holder.itemView.setLayoutParams(rvLayoutParams);
5731            } else if (!checkLayoutParams(lp)) {
5732                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5733                holder.itemView.setLayoutParams(rvLayoutParams);
5734            } else {
5735                rvLayoutParams = (LayoutParams) lp;
5736            }
5737
5738            rvLayoutParams.mInsetsDirty = true;
5739            rvLayoutParams.mViewHolder = holder;
5740            rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
5741        }
5742
5743        /**
5744         * RecyclerView provides artificial position range (item count) in pre-layout state and
5745         * automatically maps these positions to {@link Adapter} positions when
5746         * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
5747         * <p>
5748         * Usually, LayoutManager does not need to worry about this. However, in some cases, your
5749         * LayoutManager may need to call some custom component with item positions in which
5750         * case you need the actual adapter position instead of the pre layout position. You
5751         * can use this method to convert a pre-layout position to adapter (post layout) position.
5752         * <p>
5753         * Note that if the provided position belongs to a deleted ViewHolder, this method will
5754         * return -1.
5755         * <p>
5756         * Calling this method in post-layout state returns the same value back.
5757         *
5758         * @param position The pre-layout position to convert. Must be greater or equal to 0 and
5759         *                 less than {@link State#getItemCount()}.
5760         */
5761        public int convertPreLayoutPositionToPostLayout(int position) {
5762            if (position < 0 || position >= mState.getItemCount()) {
5763                throw new IndexOutOfBoundsException("invalid position " + position + ". State "
5764                        + "item count is " + mState.getItemCount() + exceptionLabel());
5765            }
5766            if (!mState.isPreLayout()) {
5767                return position;
5768            }
5769            return mAdapterHelper.findPositionOffset(position);
5770        }
5771
5772        /**
5773         * Obtain a view initialized for the given position.
5774         *
5775         * This method should be used by {@link LayoutManager} implementations to obtain
5776         * views to represent data from an {@link Adapter}.
5777         * <p>
5778         * The Recycler may reuse a scrap or detached view from a shared pool if one is
5779         * available for the correct view type. If the adapter has not indicated that the
5780         * data at the given position has changed, the Recycler will attempt to hand back
5781         * a scrap view that was previously initialized for that data without rebinding.
5782         *
5783         * @param position Position to obtain a view for
5784         * @return A view representing the data at <code>position</code> from <code>adapter</code>
5785         */
5786        @NonNull
5787        public View getViewForPosition(int position) {
5788            return getViewForPosition(position, false);
5789        }
5790
5791        View getViewForPosition(int position, boolean dryRun) {
5792            return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
5793        }
5794
5795        /**
5796         * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
5797         * cache, the RecycledViewPool, or creating it directly.
5798         * <p>
5799         * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
5800         * rather than constructing or binding a ViewHolder if it doesn't think it has time.
5801         * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
5802         * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
5803         * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
5804         *
5805         * @param position Position of ViewHolder to be returned.
5806         * @param dryRun True if the ViewHolder should not be removed from scrap/cache/
5807         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5808         *                   complete. If FOREVER_NS is passed, this method will not fail to
5809         *                   create/bind the holder if needed.
5810         *
5811         * @return ViewHolder for requested position
5812         */
5813        @Nullable
5814        ViewHolder tryGetViewHolderForPositionByDeadline(int position,
5815                boolean dryRun, long deadlineNs) {
5816            if (position < 0 || position >= mState.getItemCount()) {
5817                throw new IndexOutOfBoundsException("Invalid item position " + position
5818                        + "(" + position + "). Item count:" + mState.getItemCount()
5819                        + exceptionLabel());
5820            }
5821            boolean fromScrapOrHiddenOrCache = false;
5822            ViewHolder holder = null;
5823            // 0) If there is a changed scrap, try to find from there
5824            if (mState.isPreLayout()) {
5825                holder = getChangedScrapViewForPosition(position);
5826                fromScrapOrHiddenOrCache = holder != null;
5827            }
5828            // 1) Find by position from scrap/hidden list/cache
5829            if (holder == null) {
5830                holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
5831                if (holder != null) {
5832                    if (!validateViewHolderForOffsetPosition(holder)) {
5833                        // recycle holder (and unscrap if relevant) since it can't be used
5834                        if (!dryRun) {
5835                            // we would like to recycle this but need to make sure it is not used by
5836                            // animation logic etc.
5837                            holder.addFlags(ViewHolder.FLAG_INVALID);
5838                            if (holder.isScrap()) {
5839                                removeDetachedView(holder.itemView, false);
5840                                holder.unScrap();
5841                            } else if (holder.wasReturnedFromScrap()) {
5842                                holder.clearReturnedFromScrapFlag();
5843                            }
5844                            recycleViewHolderInternal(holder);
5845                        }
5846                        holder = null;
5847                    } else {
5848                        fromScrapOrHiddenOrCache = true;
5849                    }
5850                }
5851            }
5852            if (holder == null) {
5853                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5854                if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5855                    throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5856                            + "position " + position + "(offset:" + offsetPosition + ")."
5857                            + "state:" + mState.getItemCount() + exceptionLabel());
5858                }
5859
5860                final int type = mAdapter.getItemViewType(offsetPosition);
5861                // 2) Find from scrap/cache via stable ids, if exists
5862                if (mAdapter.hasStableIds()) {
5863                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
5864                            type, dryRun);
5865                    if (holder != null) {
5866                        // update position
5867                        holder.mPosition = offsetPosition;
5868                        fromScrapOrHiddenOrCache = true;
5869                    }
5870                }
5871                if (holder == null && mViewCacheExtension != null) {
5872                    // We are NOT sending the offsetPosition because LayoutManager does not
5873                    // know it.
5874                    final View view = mViewCacheExtension
5875                            .getViewForPositionAndType(this, position, type);
5876                    if (view != null) {
5877                        holder = getChildViewHolder(view);
5878                        if (holder == null) {
5879                            throw new IllegalArgumentException("getViewForPositionAndType returned"
5880                                    + " a view which does not have a ViewHolder"
5881                                    + exceptionLabel());
5882                        } else if (holder.shouldIgnore()) {
5883                            throw new IllegalArgumentException("getViewForPositionAndType returned"
5884                                    + " a view that is ignored. You must call stopIgnoring before"
5885                                    + " returning this view." + exceptionLabel());
5886                        }
5887                    }
5888                }
5889                if (holder == null) { // fallback to pool
5890                    if (DEBUG) {
5891                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
5892                                + position + ") fetching from shared pool");
5893                    }
5894                    holder = getRecycledViewPool().getRecycledView(type);
5895                    if (holder != null) {
5896                        holder.resetInternal();
5897                        if (FORCE_INVALIDATE_DISPLAY_LIST) {
5898                            invalidateDisplayListInt(holder);
5899                        }
5900                    }
5901                }
5902                if (holder == null) {
5903                    long start = getNanoTime();
5904                    if (deadlineNs != FOREVER_NS
5905                            && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
5906                        // abort - we have a deadline we can't meet
5907                        return null;
5908                    }
5909                    holder = mAdapter.createViewHolder(RecyclerView.this, type);
5910                    if (ALLOW_THREAD_GAP_WORK) {
5911                        // only bother finding nested RV if prefetching
5912                        RecyclerView innerView = findNestedRecyclerView(holder.itemView);
5913                        if (innerView != null) {
5914                            holder.mNestedRecyclerView = new WeakReference<>(innerView);
5915                        }
5916                    }
5917
5918                    long end = getNanoTime();
5919                    mRecyclerPool.factorInCreateTime(type, end - start);
5920                    if (DEBUG) {
5921                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
5922                    }
5923                }
5924            }
5925
5926            // This is very ugly but the only place we can grab this information
5927            // before the View is rebound and returned to the LayoutManager for post layout ops.
5928            // We don't need this in pre-layout since the VH is not updated by the LM.
5929            if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
5930                    .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
5931                holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5932                if (mState.mRunSimpleAnimations) {
5933                    int changeFlags = ItemAnimator
5934                            .buildAdapterChangeFlagsForAnimations(holder);
5935                    changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
5936                    final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
5937                            holder, changeFlags, holder.getUnmodifiedPayloads());
5938                    recordAnimationInfoIfBouncedHiddenView(holder, info);
5939                }
5940            }
5941
5942            boolean bound = false;
5943            if (mState.isPreLayout() && holder.isBound()) {
5944                // do not update unless we absolutely have to.
5945                holder.mPreLayoutPosition = position;
5946            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
5947                if (DEBUG && holder.isRemoved()) {
5948                    throw new IllegalStateException("Removed holder should be bound and it should"
5949                            + " come here only in pre-layout. Holder: " + holder
5950                            + exceptionLabel());
5951                }
5952                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5953                bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
5954            }
5955
5956            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5957            final LayoutParams rvLayoutParams;
5958            if (lp == null) {
5959                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5960                holder.itemView.setLayoutParams(rvLayoutParams);
5961            } else if (!checkLayoutParams(lp)) {
5962                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5963                holder.itemView.setLayoutParams(rvLayoutParams);
5964            } else {
5965                rvLayoutParams = (LayoutParams) lp;
5966            }
5967            rvLayoutParams.mViewHolder = holder;
5968            rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
5969            return holder;
5970        }
5971
5972        private void attachAccessibilityDelegateOnBind(ViewHolder holder) {
5973            if (isAccessibilityEnabled()) {
5974                final View itemView = holder.itemView;
5975                if (ViewCompat.getImportantForAccessibility(itemView)
5976                        == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
5977                    ViewCompat.setImportantForAccessibility(itemView,
5978                            ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
5979                }
5980                if (!ViewCompat.hasAccessibilityDelegate(itemView)) {
5981                    holder.addFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
5982                    ViewCompat.setAccessibilityDelegate(itemView,
5983                            mAccessibilityDelegate.getItemDelegate());
5984                }
5985            }
5986        }
5987
5988        private void invalidateDisplayListInt(ViewHolder holder) {
5989            if (holder.itemView instanceof ViewGroup) {
5990                invalidateDisplayListInt((ViewGroup) holder.itemView, false);
5991            }
5992        }
5993
5994        private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
5995            for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
5996                final View view = viewGroup.getChildAt(i);
5997                if (view instanceof ViewGroup) {
5998                    invalidateDisplayListInt((ViewGroup) view, true);
5999                }
6000            }
6001            if (!invalidateThis) {
6002                return;
6003            }
6004            // we need to force it to become invisible
6005            if (viewGroup.getVisibility() == View.INVISIBLE) {
6006                viewGroup.setVisibility(View.VISIBLE);
6007                viewGroup.setVisibility(View.INVISIBLE);
6008            } else {
6009                final int visibility = viewGroup.getVisibility();
6010                viewGroup.setVisibility(View.INVISIBLE);
6011                viewGroup.setVisibility(visibility);
6012            }
6013        }
6014
6015        /**
6016         * Recycle a detached view. The specified view will be added to a pool of views
6017         * for later rebinding and reuse.
6018         *
6019         * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
6020         * View is scrapped, it will be removed from scrap list.</p>
6021         *
6022         * @param view Removed view for recycling
6023         * @see LayoutManager#removeAndRecycleView(View, Recycler)
6024         */
6025        public void recycleView(@NonNull View view) {
6026            // This public recycle method tries to make view recycle-able since layout manager
6027            // intended to recycle this view (e.g. even if it is in scrap or change cache)
6028            ViewHolder holder = getChildViewHolderInt(view);
6029            if (holder.isTmpDetached()) {
6030                removeDetachedView(view, false);
6031            }
6032            if (holder.isScrap()) {
6033                holder.unScrap();
6034            } else if (holder.wasReturnedFromScrap()) {
6035                holder.clearReturnedFromScrapFlag();
6036            }
6037            recycleViewHolderInternal(holder);
6038        }
6039
6040        /**
6041         * Internally, use this method instead of {@link #recycleView(android.view.View)} to
6042         * catch potential bugs.
6043         * @param view
6044         */
6045        void recycleViewInternal(View view) {
6046            recycleViewHolderInternal(getChildViewHolderInt(view));
6047        }
6048
6049        void recycleAndClearCachedViews() {
6050            final int count = mCachedViews.size();
6051            for (int i = count - 1; i >= 0; i--) {
6052                recycleCachedViewAt(i);
6053            }
6054            mCachedViews.clear();
6055            if (ALLOW_THREAD_GAP_WORK) {
6056                mPrefetchRegistry.clearPrefetchPositions();
6057            }
6058        }
6059
6060        /**
6061         * Recycles a cached view and removes the view from the list. Views are added to cache
6062         * if and only if they are recyclable, so this method does not check it again.
6063         * <p>
6064         * A small exception to this rule is when the view does not have an animator reference
6065         * but transient state is true (due to animations created outside ItemAnimator). In that
6066         * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
6067         * still recyclable since Adapter wants to do so.
6068         *
6069         * @param cachedViewIndex The index of the view in cached views list
6070         */
6071        void recycleCachedViewAt(int cachedViewIndex) {
6072            if (DEBUG) {
6073                Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
6074            }
6075            ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
6076            if (DEBUG) {
6077                Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
6078            }
6079            addViewHolderToRecycledViewPool(viewHolder, true);
6080            mCachedViews.remove(cachedViewIndex);
6081        }
6082
6083        /**
6084         * internal implementation checks if view is scrapped or attached and throws an exception
6085         * if so.
6086         * Public version un-scraps before calling recycle.
6087         */
6088        void recycleViewHolderInternal(ViewHolder holder) {
6089            if (holder.isScrap() || holder.itemView.getParent() != null) {
6090                throw new IllegalArgumentException(
6091                        "Scrapped or attached views may not be recycled. isScrap:"
6092                                + holder.isScrap() + " isAttached:"
6093                                + (holder.itemView.getParent() != null) + exceptionLabel());
6094            }
6095
6096            if (holder.isTmpDetached()) {
6097                throw new IllegalArgumentException("Tmp detached view should be removed "
6098                        + "from RecyclerView before it can be recycled: " + holder
6099                        + exceptionLabel());
6100            }
6101
6102            if (holder.shouldIgnore()) {
6103                throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
6104                        + " should first call stopIgnoringView(view) before calling recycle."
6105                        + exceptionLabel());
6106            }
6107            //noinspection unchecked
6108            final boolean transientStatePreventsRecycling = holder
6109                    .doesTransientStatePreventRecycling();
6110            final boolean forceRecycle = mAdapter != null
6111                    && transientStatePreventsRecycling
6112                    && mAdapter.onFailedToRecycleView(holder);
6113            boolean cached = false;
6114            boolean recycled = false;
6115            if (DEBUG && mCachedViews.contains(holder)) {
6116                throw new IllegalArgumentException("cached view received recycle internal? "
6117                        + holder + exceptionLabel());
6118            }
6119            if (forceRecycle || holder.isRecyclable()) {
6120                if (mViewCacheMax > 0
6121                        && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
6122                        | ViewHolder.FLAG_REMOVED
6123                        | ViewHolder.FLAG_UPDATE
6124                        | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
6125                    // Retire oldest cached view
6126                    int cachedViewSize = mCachedViews.size();
6127                    if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
6128                        recycleCachedViewAt(0);
6129                        cachedViewSize--;
6130                    }
6131
6132                    int targetCacheIndex = cachedViewSize;
6133                    if (ALLOW_THREAD_GAP_WORK
6134                            && cachedViewSize > 0
6135                            && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
6136                        // when adding the view, skip past most recently prefetched views
6137                        int cacheIndex = cachedViewSize - 1;
6138                        while (cacheIndex >= 0) {
6139                            int cachedPos = mCachedViews.get(cacheIndex).mPosition;
6140                            if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
6141                                break;
6142                            }
6143                            cacheIndex--;
6144                        }
6145                        targetCacheIndex = cacheIndex + 1;
6146                    }
6147                    mCachedViews.add(targetCacheIndex, holder);
6148                    cached = true;
6149                }
6150                if (!cached) {
6151                    addViewHolderToRecycledViewPool(holder, true);
6152                    recycled = true;
6153                }
6154            } else {
6155                // NOTE: A view can fail to be recycled when it is scrolled off while an animation
6156                // runs. In this case, the item is eventually recycled by
6157                // ItemAnimatorRestoreListener#onAnimationFinished.
6158
6159                // TODO: consider cancelling an animation when an item is removed scrollBy,
6160                // to return it to the pool faster
6161                if (DEBUG) {
6162                    Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
6163                            + "re-visit here. We are still removing it from animation lists"
6164                            + exceptionLabel());
6165                }
6166            }
6167            // even if the holder is not removed, we still call this method so that it is removed
6168            // from view holder lists.
6169            mViewInfoStore.removeViewHolder(holder);
6170            if (!cached && !recycled && transientStatePreventsRecycling) {
6171                holder.mOwnerRecyclerView = null;
6172            }
6173        }
6174
6175        /**
6176         * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
6177         *
6178         * Pass false to dispatchRecycled for views that have not been bound.
6179         *
6180         * @param holder Holder to be added to the pool.
6181         * @param dispatchRecycled True to dispatch View recycled callbacks.
6182         */
6183        void addViewHolderToRecycledViewPool(@NonNull ViewHolder holder, boolean dispatchRecycled) {
6184            clearNestedRecyclerViewIfNotNested(holder);
6185            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE)) {
6186                holder.setFlags(0, ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
6187                ViewCompat.setAccessibilityDelegate(holder.itemView, null);
6188            }
6189            if (dispatchRecycled) {
6190                dispatchViewRecycled(holder);
6191            }
6192            holder.mOwnerRecyclerView = null;
6193            getRecycledViewPool().putRecycledView(holder);
6194        }
6195
6196        /**
6197         * Used as a fast path for unscrapping and recycling a view during a bulk operation.
6198         * The caller must call {@link #clearScrap()} when it's done to update the recycler's
6199         * internal bookkeeping.
6200         */
6201        void quickRecycleScrapView(View view) {
6202            final ViewHolder holder = getChildViewHolderInt(view);
6203            holder.mScrapContainer = null;
6204            holder.mInChangeScrap = false;
6205            holder.clearReturnedFromScrapFlag();
6206            recycleViewHolderInternal(holder);
6207        }
6208
6209        /**
6210         * Mark an attached view as scrap.
6211         *
6212         * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
6213         * for rebinding and reuse. Requests for a view for a given position may return a
6214         * reused or rebound scrap view instance.</p>
6215         *
6216         * @param view View to scrap
6217         */
6218        void scrapView(View view) {
6219            final ViewHolder holder = getChildViewHolderInt(view);
6220            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
6221                    || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
6222                if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
6223                    throw new IllegalArgumentException("Called scrap view with an invalid view."
6224                            + " Invalid views cannot be reused from scrap, they should rebound from"
6225                            + " recycler pool." + exceptionLabel());
6226                }
6227                holder.setScrapContainer(this, false);
6228                mAttachedScrap.add(holder);
6229            } else {
6230                if (mChangedScrap == null) {
6231                    mChangedScrap = new ArrayList<ViewHolder>();
6232                }
6233                holder.setScrapContainer(this, true);
6234                mChangedScrap.add(holder);
6235            }
6236        }
6237
6238        /**
6239         * Remove a previously scrapped view from the pool of eligible scrap.
6240         *
6241         * <p>This view will no longer be eligible for reuse until re-scrapped or
6242         * until it is explicitly removed and recycled.</p>
6243         */
6244        void unscrapView(ViewHolder holder) {
6245            if (holder.mInChangeScrap) {
6246                mChangedScrap.remove(holder);
6247            } else {
6248                mAttachedScrap.remove(holder);
6249            }
6250            holder.mScrapContainer = null;
6251            holder.mInChangeScrap = false;
6252            holder.clearReturnedFromScrapFlag();
6253        }
6254
6255        int getScrapCount() {
6256            return mAttachedScrap.size();
6257        }
6258
6259        View getScrapViewAt(int index) {
6260            return mAttachedScrap.get(index).itemView;
6261        }
6262
6263        void clearScrap() {
6264            mAttachedScrap.clear();
6265            if (mChangedScrap != null) {
6266                mChangedScrap.clear();
6267            }
6268        }
6269
6270        ViewHolder getChangedScrapViewForPosition(int position) {
6271            // If pre-layout, check the changed scrap for an exact match.
6272            final int changedScrapSize;
6273            if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
6274                return null;
6275            }
6276            // find by position
6277            for (int i = 0; i < changedScrapSize; i++) {
6278                final ViewHolder holder = mChangedScrap.get(i);
6279                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
6280                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6281                    return holder;
6282                }
6283            }
6284            // find by id
6285            if (mAdapter.hasStableIds()) {
6286                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
6287                if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
6288                    final long id = mAdapter.getItemId(offsetPosition);
6289                    for (int i = 0; i < changedScrapSize; i++) {
6290                        final ViewHolder holder = mChangedScrap.get(i);
6291                        if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
6292                            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6293                            return holder;
6294                        }
6295                    }
6296                }
6297            }
6298            return null;
6299        }
6300
6301        /**
6302         * Returns a view for the position either from attach scrap, hidden children, or cache.
6303         *
6304         * @param position Item position
6305         * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
6306         * @return a ViewHolder that can be re-used for this position.
6307         */
6308        ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
6309            final int scrapCount = mAttachedScrap.size();
6310
6311            // Try first for an exact, non-invalid match from scrap.
6312            for (int i = 0; i < scrapCount; i++) {
6313                final ViewHolder holder = mAttachedScrap.get(i);
6314                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
6315                        && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
6316                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6317                    return holder;
6318                }
6319            }
6320
6321            if (!dryRun) {
6322                View view = mChildHelper.findHiddenNonRemovedView(position);
6323                if (view != null) {
6324                    // This View is good to be used. We just need to unhide, detach and move to the
6325                    // scrap list.
6326                    final ViewHolder vh = getChildViewHolderInt(view);
6327                    mChildHelper.unhide(view);
6328                    int layoutIndex = mChildHelper.indexOfChild(view);
6329                    if (layoutIndex == RecyclerView.NO_POSITION) {
6330                        throw new IllegalStateException("layout index should not be -1 after "
6331                                + "unhiding a view:" + vh + exceptionLabel());
6332                    }
6333                    mChildHelper.detachViewFromParent(layoutIndex);
6334                    scrapView(view);
6335                    vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
6336                            | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
6337                    return vh;
6338                }
6339            }
6340
6341            // Search in our first-level recycled view cache.
6342            final int cacheSize = mCachedViews.size();
6343            for (int i = 0; i < cacheSize; i++) {
6344                final ViewHolder holder = mCachedViews.get(i);
6345                // invalid view holders may be in cache if adapter has stable ids as they can be
6346                // retrieved via getScrapOrCachedViewForId
6347                if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
6348                    if (!dryRun) {
6349                        mCachedViews.remove(i);
6350                    }
6351                    if (DEBUG) {
6352                        Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
6353                                + ") found match in cache: " + holder);
6354                    }
6355                    return holder;
6356                }
6357            }
6358            return null;
6359        }
6360
6361        ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
6362            // Look in our attached views first
6363            final int count = mAttachedScrap.size();
6364            for (int i = count - 1; i >= 0; i--) {
6365                final ViewHolder holder = mAttachedScrap.get(i);
6366                if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
6367                    if (type == holder.getItemViewType()) {
6368                        holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6369                        if (holder.isRemoved()) {
6370                            // this might be valid in two cases:
6371                            // > item is removed but we are in pre-layout pass
6372                            // >> do nothing. return as is. make sure we don't rebind
6373                            // > item is removed then added to another position and we are in
6374                            // post layout.
6375                            // >> remove removed and invalid flags, add update flag to rebind
6376                            // because item was invisible to us and we don't know what happened in
6377                            // between.
6378                            if (!mState.isPreLayout()) {
6379                                holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
6380                                        | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
6381                            }
6382                        }
6383                        return holder;
6384                    } else if (!dryRun) {
6385                        // if we are running animations, it is actually better to keep it in scrap
6386                        // but this would force layout manager to lay it out which would be bad.
6387                        // Recycle this scrap. Type mismatch.
6388                        mAttachedScrap.remove(i);
6389                        removeDetachedView(holder.itemView, false);
6390                        quickRecycleScrapView(holder.itemView);
6391                    }
6392                }
6393            }
6394
6395            // Search the first-level cache
6396            final int cacheSize = mCachedViews.size();
6397            for (int i = cacheSize - 1; i >= 0; i--) {
6398                final ViewHolder holder = mCachedViews.get(i);
6399                if (holder.getItemId() == id) {
6400                    if (type == holder.getItemViewType()) {
6401                        if (!dryRun) {
6402                            mCachedViews.remove(i);
6403                        }
6404                        return holder;
6405                    } else if (!dryRun) {
6406                        recycleCachedViewAt(i);
6407                        return null;
6408                    }
6409                }
6410            }
6411            return null;
6412        }
6413
6414        void dispatchViewRecycled(@NonNull ViewHolder holder) {
6415            if (mRecyclerListener != null) {
6416                mRecyclerListener.onViewRecycled(holder);
6417            }
6418            if (mAdapter != null) {
6419                mAdapter.onViewRecycled(holder);
6420            }
6421            if (mState != null) {
6422                mViewInfoStore.removeViewHolder(holder);
6423            }
6424            if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
6425        }
6426
6427        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
6428                boolean compatibleWithPrevious) {
6429            clear();
6430            getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
6431        }
6432
6433        void offsetPositionRecordsForMove(int from, int to) {
6434            final int start, end, inBetweenOffset;
6435            if (from < to) {
6436                start = from;
6437                end = to;
6438                inBetweenOffset = -1;
6439            } else {
6440                start = to;
6441                end = from;
6442                inBetweenOffset = 1;
6443            }
6444            final int cachedCount = mCachedViews.size();
6445            for (int i = 0; i < cachedCount; i++) {
6446                final ViewHolder holder = mCachedViews.get(i);
6447                if (holder == null || holder.mPosition < start || holder.mPosition > end) {
6448                    continue;
6449                }
6450                if (holder.mPosition == from) {
6451                    holder.offsetPosition(to - from, false);
6452                } else {
6453                    holder.offsetPosition(inBetweenOffset, false);
6454                }
6455                if (DEBUG) {
6456                    Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
6457                            + holder);
6458                }
6459            }
6460        }
6461
6462        void offsetPositionRecordsForInsert(int insertedAt, int count) {
6463            final int cachedCount = mCachedViews.size();
6464            for (int i = 0; i < cachedCount; i++) {
6465                final ViewHolder holder = mCachedViews.get(i);
6466                if (holder != null && holder.mPosition >= insertedAt) {
6467                    if (DEBUG) {
6468                        Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder "
6469                                + holder + " now at position " + (holder.mPosition + count));
6470                    }
6471                    holder.offsetPosition(count, true);
6472                }
6473            }
6474        }
6475
6476        /**
6477         * @param removedFrom Remove start index
6478         * @param count Remove count
6479         * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
6480         *                         false, they'll be applied before the second layout pass
6481         */
6482        void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
6483            final int removedEnd = removedFrom + count;
6484            final int cachedCount = mCachedViews.size();
6485            for (int i = cachedCount - 1; i >= 0; i--) {
6486                final ViewHolder holder = mCachedViews.get(i);
6487                if (holder != null) {
6488                    if (holder.mPosition >= removedEnd) {
6489                        if (DEBUG) {
6490                            Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
6491                                    + " holder " + holder + " now at position "
6492                                    + (holder.mPosition - count));
6493                        }
6494                        holder.offsetPosition(-count, applyToPreLayout);
6495                    } else if (holder.mPosition >= removedFrom) {
6496                        // Item for this view was removed. Dump it from the cache.
6497                        holder.addFlags(ViewHolder.FLAG_REMOVED);
6498                        recycleCachedViewAt(i);
6499                    }
6500                }
6501            }
6502        }
6503
6504        void setViewCacheExtension(ViewCacheExtension extension) {
6505            mViewCacheExtension = extension;
6506        }
6507
6508        void setRecycledViewPool(RecycledViewPool pool) {
6509            if (mRecyclerPool != null) {
6510                mRecyclerPool.detach();
6511            }
6512            mRecyclerPool = pool;
6513            if (mRecyclerPool != null && getAdapter() != null) {
6514                mRecyclerPool.attach();
6515            }
6516        }
6517
6518        RecycledViewPool getRecycledViewPool() {
6519            if (mRecyclerPool == null) {
6520                mRecyclerPool = new RecycledViewPool();
6521            }
6522            return mRecyclerPool;
6523        }
6524
6525        void viewRangeUpdate(int positionStart, int itemCount) {
6526            final int positionEnd = positionStart + itemCount;
6527            final int cachedCount = mCachedViews.size();
6528            for (int i = cachedCount - 1; i >= 0; i--) {
6529                final ViewHolder holder = mCachedViews.get(i);
6530                if (holder == null) {
6531                    continue;
6532                }
6533
6534                final int pos = holder.mPosition;
6535                if (pos >= positionStart && pos < positionEnd) {
6536                    holder.addFlags(ViewHolder.FLAG_UPDATE);
6537                    recycleCachedViewAt(i);
6538                    // cached views should not be flagged as changed because this will cause them
6539                    // to animate when they are returned from cache.
6540                }
6541            }
6542        }
6543
6544        void markKnownViewsInvalid() {
6545            final int cachedCount = mCachedViews.size();
6546            for (int i = 0; i < cachedCount; i++) {
6547                final ViewHolder holder = mCachedViews.get(i);
6548                if (holder != null) {
6549                    holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
6550                    holder.addChangePayload(null);
6551                }
6552            }
6553
6554            if (mAdapter == null || !mAdapter.hasStableIds()) {
6555                // we cannot re-use cached views in this case. Recycle them all
6556                recycleAndClearCachedViews();
6557            }
6558        }
6559
6560        void clearOldPositions() {
6561            final int cachedCount = mCachedViews.size();
6562            for (int i = 0; i < cachedCount; i++) {
6563                final ViewHolder holder = mCachedViews.get(i);
6564                holder.clearOldPosition();
6565            }
6566            final int scrapCount = mAttachedScrap.size();
6567            for (int i = 0; i < scrapCount; i++) {
6568                mAttachedScrap.get(i).clearOldPosition();
6569            }
6570            if (mChangedScrap != null) {
6571                final int changedScrapCount = mChangedScrap.size();
6572                for (int i = 0; i < changedScrapCount; i++) {
6573                    mChangedScrap.get(i).clearOldPosition();
6574                }
6575            }
6576        }
6577
6578        void markItemDecorInsetsDirty() {
6579            final int cachedCount = mCachedViews.size();
6580            for (int i = 0; i < cachedCount; i++) {
6581                final ViewHolder holder = mCachedViews.get(i);
6582                LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
6583                if (layoutParams != null) {
6584                    layoutParams.mInsetsDirty = true;
6585                }
6586            }
6587        }
6588    }
6589
6590    /**
6591     * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
6592     * be controlled by the developer.
6593     * <p>
6594     * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
6595     * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
6596     * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
6597     * {@link RecycledViewPool}.
6598     * <p>
6599     * Note that, Recycler never sends Views to this method to be cached. It is developers
6600     * responsibility to decide whether they want to keep their Views in this custom cache or let
6601     * the default recycling policy handle it.
6602     */
6603    public abstract static class ViewCacheExtension {
6604
6605        /**
6606         * Returns a View that can be binded to the given Adapter position.
6607         * <p>
6608         * This method should <b>not</b> create a new View. Instead, it is expected to return
6609         * an already created View that can be re-used for the given type and position.
6610         * If the View is marked as ignored, it should first call
6611         * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
6612         * <p>
6613         * RecyclerView will re-bind the returned View to the position if necessary.
6614         *
6615         * @param recycler The Recycler that can be used to bind the View
6616         * @param position The adapter position
6617         * @param type     The type of the View, defined by adapter
6618         * @return A View that is bound to the given position or NULL if there is no View to re-use
6619         * @see LayoutManager#ignoreView(View)
6620         */
6621        @Nullable
6622        public abstract View getViewForPositionAndType(@NonNull Recycler recycler, int position,
6623                int type);
6624    }
6625
6626    /**
6627     * Base class for an Adapter
6628     *
6629     * <p>Adapters provide a binding from an app-specific data set to views that are displayed
6630     * within a {@link RecyclerView}.</p>
6631     *
6632     * @param  A class that extends ViewHolder that will be used by the adapter.
6633     */
6634    public abstract static class Adapter<VH extends ViewHolder> {
6635        private final AdapterDataObservable mObservable = new AdapterDataObservable();
6636        private boolean mHasStableIds = false;
6637
6638        /**
6639         * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
6640         * an item.
6641         * <p>
6642         * This new ViewHolder should be constructed with a new View that can represent the items
6643         * of the given type. You can either create a new View manually or inflate it from an XML
6644         * layout file.
6645         * <p>
6646         * The new ViewHolder will be used to display items of the adapter using
6647         * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
6648         * different items in the data set, it is a good idea to cache references to sub views of
6649         * the View to avoid unnecessary {@link View#findViewById(int)} calls.
6650         *
6651         * @param parent The ViewGroup into which the new View will be added after it is bound to
6652         *               an adapter position.
6653         * @param viewType The view type of the new View.
6654         *
6655         * @return A new ViewHolder that holds a View of the given view type.
6656         * @see #getItemViewType(int)
6657         * @see #onBindViewHolder(ViewHolder, int)
6658         */
6659        @NonNull
6660        public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
6661
6662        /**
6663         * Called by RecyclerView to display the data at the specified position. This method should
6664         * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
6665         * position.
6666         * <p>
6667         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6668         * again if the position of the item changes in the data set unless the item itself is
6669         * invalidated or the new position cannot be determined. For this reason, you should only
6670         * use the <code>position</code> parameter while acquiring the related data item inside
6671         * this method and should not keep a copy of it. If you need the position of an item later
6672         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6673         * have the updated adapter position.
6674         *
6675         * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
6676         * handle efficient partial bind.
6677         *
6678         * @param holder The ViewHolder which should be updated to represent the contents of the
6679         *        item at the given position in the data set.
6680         * @param position The position of the item within the adapter's data set.
6681         */
6682        public abstract void onBindViewHolder(@NonNull VH holder, int position);
6683
6684        /**
6685         * Called by RecyclerView to display the data at the specified position. This method
6686         * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
6687         * the given position.
6688         * <p>
6689         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6690         * again if the position of the item changes in the data set unless the item itself is
6691         * invalidated or the new position cannot be determined. For this reason, you should only
6692         * use the <code>position</code> parameter while acquiring the related data item inside
6693         * this method and should not keep a copy of it. If you need the position of an item later
6694         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6695         * have the updated adapter position.
6696         * <p>
6697         * Partial bind vs full bind:
6698         * <p>
6699         * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
6700         * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
6701         * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
6702         * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
6703         * Adapter should not assume that the payload passed in notify methods will be received by
6704         * onBindViewHolder().  For example when the view is not attached to the screen, the
6705         * payload in notifyItemChange() will be simply dropped.
6706         *
6707         * @param holder The ViewHolder which should be updated to represent the contents of the
6708         *               item at the given position in the data set.
6709         * @param position The position of the item within the adapter's data set.
6710         * @param payloads A non-null list of merged payloads. Can be empty list if requires full
6711         *                 update.
6712         */
6713        public void onBindViewHolder(@NonNull VH holder, int position,
6714                @NonNull List<Object> payloads) {
6715            onBindViewHolder(holder, position);
6716        }
6717
6718        /**
6719         * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
6720         * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
6721         *
6722         * @see #onCreateViewHolder(ViewGroup, int)
6723         */
6724        @NonNull
6725        public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
6726            try {
6727                TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
6728                final VH holder = onCreateViewHolder(parent, viewType);
6729                if (holder.itemView.getParent() != null) {
6730                    throw new IllegalStateException("ViewHolder views must not be attached when"
6731                            + " created. Ensure that you are not passing 'true' to the attachToRoot"
6732                            + " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
6733                }
6734                holder.mItemViewType = viewType;
6735                return holder;
6736            } finally {
6737                TraceCompat.endSection();
6738            }
6739        }
6740
6741        /**
6742         * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
6743         * {@link ViewHolder} contents with the item at the given position and also sets up some
6744         * private fields to be used by RecyclerView.
6745         *
6746         * @see #onBindViewHolder(ViewHolder, int)
6747         */
6748        public final void bindViewHolder(@NonNull VH holder, int position) {
6749            holder.mPosition = position;
6750            if (hasStableIds()) {
6751                holder.mItemId = getItemId(position);
6752            }
6753            holder.setFlags(ViewHolder.FLAG_BOUND,
6754                    ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
6755                            | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
6756            TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
6757            onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
6758            holder.clearPayload();
6759            final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
6760            if (layoutParams instanceof RecyclerView.LayoutParams) {
6761                ((LayoutParams) layoutParams).mInsetsDirty = true;
6762            }
6763            TraceCompat.endSection();
6764        }
6765
6766        /**
6767         * Return the view type of the item at <code>position</code> for the purposes
6768         * of view recycling.
6769         *
6770         * <p>The default implementation of this method returns 0, making the assumption of
6771         * a single view type for the adapter. Unlike ListView adapters, types need not
6772         * be contiguous. Consider using id resources to uniquely identify item view types.
6773         *
6774         * @param position position to query
6775         * @return integer value identifying the type of the view needed to represent the item at
6776         *                 <code>position</code>. Type codes need not be contiguous.
6777         */
6778        public int getItemViewType(int position) {
6779            return 0;
6780        }
6781
6782        /**
6783         * Indicates whether each item in the data set can be represented with a unique identifier
6784         * of type {@link java.lang.Long}.
6785         *
6786         * @param hasStableIds Whether items in data set have unique identifiers or not.
6787         * @see #hasStableIds()
6788         * @see #getItemId(int)
6789         */
6790        public void setHasStableIds(boolean hasStableIds) {
6791            if (hasObservers()) {
6792                throw new IllegalStateException("Cannot change whether this adapter has "
6793                        + "stable IDs while the adapter has registered observers.");
6794            }
6795            mHasStableIds = hasStableIds;
6796        }
6797
6798        /**
6799         * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
6800         * would return false this method should return {@link #NO_ID}. The default implementation
6801         * of this method returns {@link #NO_ID}.
6802         *
6803         * @param position Adapter position to query
6804         * @return the stable ID of the item at position
6805         */
6806        public long getItemId(int position) {
6807            return NO_ID;
6808        }
6809
6810        /**
6811         * Returns the total number of items in the data set held by the adapter.
6812         *
6813         * @return The total number of items in this adapter.
6814         */
6815        public abstract int getItemCount();
6816
6817        /**
6818         * Returns true if this adapter publishes a unique <code>long</code> value that can
6819         * act as a key for the item at a given position in the data set. If that item is relocated
6820         * in the data set, the ID returned for that item should be the same.
6821         *
6822         * @return true if this adapter's items have stable IDs
6823         */
6824        public final boolean hasStableIds() {
6825            return mHasStableIds;
6826        }
6827
6828        /**
6829         * Called when a view created by this adapter has been recycled.
6830         *
6831         * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
6832         * needs to be attached to its parent {@link RecyclerView}. This can be because it has
6833         * fallen out of visibility or a set of cached views represented by views still
6834         * attached to the parent RecyclerView. If an item view has large or expensive data
6835         * bound to it such as large bitmaps, this may be a good place to release those
6836         * resources.</p>
6837         * <p>
6838         * RecyclerView calls this method right before clearing ViewHolder's internal data and
6839         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
6840         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
6841         * its adapter position.
6842         *
6843         * @param holder The ViewHolder for the view being recycled
6844         */
6845        public void onViewRecycled(@NonNull VH holder) {
6846        }
6847
6848        /**
6849         * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
6850         * due to its transient state. Upon receiving this callback, Adapter can clear the
6851         * animation(s) that effect the View's transient state and return <code>true</code> so that
6852         * the View can be recycled. Keep in mind that the View in question is already removed from
6853         * the RecyclerView.
6854         * <p>
6855         * In some cases, it is acceptable to recycle a View although it has transient state. Most
6856         * of the time, this is a case where the transient state will be cleared in
6857         * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
6858         * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
6859         * value of this method to decide whether the View should be recycled or not.
6860         * <p>
6861         * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
6862         * should never receive this callback because RecyclerView keeps those Views as children
6863         * until their animations are complete. This callback is useful when children of the item
6864         * views create animations which may not be easy to implement using an {@link ItemAnimator}.
6865         * <p>
6866         * You should <em>never</em> fix this issue by calling
6867         * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
6868         * <code>holder.itemView.setHasTransientState(true);</code>. Each
6869         * <code>View.setHasTransientState(true)</code> call must be matched by a
6870         * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
6871         * may become inconsistent. You should always prefer to end or cancel animations that are
6872         * triggering the transient state instead of handling it manually.
6873         *
6874         * @param holder The ViewHolder containing the View that could not be recycled due to its
6875         *               transient state.
6876         * @return True if the View should be recycled, false otherwise. Note that if this method
6877         * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
6878         * the View and recycle it regardless. If this method returns <code>false</code>,
6879         * RecyclerView will check the View's transient state again before giving a final decision.
6880         * Default implementation returns false.
6881         */
6882        public boolean onFailedToRecycleView(@NonNull VH holder) {
6883            return false;
6884        }
6885
6886        /**
6887         * Called when a view created by this adapter has been attached to a window.
6888         *
6889         * <p>This can be used as a reasonable signal that the view is about to be seen
6890         * by the user. If the adapter previously freed any resources in
6891         * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
6892         * those resources should be restored here.</p>
6893         *
6894         * @param holder Holder of the view being attached
6895         */
6896        public void onViewAttachedToWindow(@NonNull VH holder) {
6897        }
6898
6899        /**
6900         * Called when a view created by this adapter has been detached from its window.
6901         *
6902         * <p>Becoming detached from the window is not necessarily a permanent condition;
6903         * the consumer of an Adapter's views may choose to cache views offscreen while they
6904         * are not visible, attaching and detaching them as appropriate.</p>
6905         *
6906         * @param holder Holder of the view being detached
6907         */
6908        public void onViewDetachedFromWindow(@NonNull VH holder) {
6909        }
6910
6911        /**
6912         * Returns true if one or more observers are attached to this adapter.
6913         *
6914         * @return true if this adapter has observers
6915         */
6916        public final boolean hasObservers() {
6917            return mObservable.hasObservers();
6918        }
6919
6920        /**
6921         * Register a new observer to listen for data changes.
6922         *
6923         * <p>The adapter may publish a variety of events describing specific changes.
6924         * Not all adapters may support all change types and some may fall back to a generic
6925         * {@link RecyclerView.AdapterDataObserver#onChanged()
6926         * "something changed"} event if more specific data is not available.</p>
6927         *
6928         * <p>Components registering observers with an adapter are responsible for
6929         * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6930         * unregistering} those observers when finished.</p>
6931         *
6932         * @param observer Observer to register
6933         *
6934         * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6935         */
6936        public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
6937            mObservable.registerObserver(observer);
6938        }
6939
6940        /**
6941         * Unregister an observer currently listening for data changes.
6942         *
6943         * <p>The unregistered observer will no longer receive events about changes
6944         * to the adapter.</p>
6945         *
6946         * @param observer Observer to unregister
6947         *
6948         * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
6949         */
6950        public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {
6951            mObservable.unregisterObserver(observer);
6952        }
6953
6954        /**
6955         * Called by RecyclerView when it starts observing this Adapter.
6956         * <p>
6957         * Keep in mind that same adapter may be observed by multiple RecyclerViews.
6958         *
6959         * @param recyclerView The RecyclerView instance which started observing this adapter.
6960         * @see #onDetachedFromRecyclerView(RecyclerView)
6961         */
6962        public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
6963        }
6964
6965        /**
6966         * Called by RecyclerView when it stops observing this Adapter.
6967         *
6968         * @param recyclerView The RecyclerView instance which stopped observing this adapter.
6969         * @see #onAttachedToRecyclerView(RecyclerView)
6970         */
6971        public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
6972        }
6973
6974        /**
6975         * Notify any registered observers that the data set has changed.
6976         *
6977         * <p>There are two different classes of data change events, item changes and structural
6978         * changes. Item changes are when a single item has its data updated but no positional
6979         * changes have occurred. Structural changes are when items are inserted, removed or moved
6980         * within the data set.</p>
6981         *
6982         * <p>This event does not specify what about the data set has changed, forcing
6983         * any observers to assume that all existing items and structure may no longer be valid.
6984         * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
6985         *
6986         * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
6987         * for adapters that report that they have {@link #hasStableIds() stable IDs} when
6988         * this method is used. This can help for the purposes of animation and visual
6989         * object persistence but individual item views will still need to be rebound
6990         * and relaid out.</p>
6991         *
6992         * <p>If you are writing an adapter it will always be more efficient to use the more
6993         * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
6994         * as a last resort.</p>
6995         *
6996         * @see #notifyItemChanged(int)
6997         * @see #notifyItemInserted(int)
6998         * @see #notifyItemRemoved(int)
6999         * @see #notifyItemRangeChanged(int, int)
7000         * @see #notifyItemRangeInserted(int, int)
7001         * @see #notifyItemRangeRemoved(int, int)
7002         */
7003        public final void notifyDataSetChanged() {
7004            mObservable.notifyChanged();
7005        }
7006
7007        /**
7008         * Notify any registered observers that the item at <code>position</code> has changed.
7009         * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
7010         *
7011         * <p>This is an item change event, not a structural change event. It indicates that any
7012         * reflection of the data at <code>position</code> is out of date and should be updated.
7013         * The item at <code>position</code> retains the same identity.</p>
7014         *
7015         * @param position Position of the item that has changed
7016         *
7017         * @see #notifyItemRangeChanged(int, int)
7018         */
7019        public final void notifyItemChanged(int position) {
7020            mObservable.notifyItemRangeChanged(position, 1);
7021        }
7022
7023        /**
7024         * Notify any registered observers that the item at <code>position</code> has changed with
7025         * an optional payload object.
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.
7030         * </p>
7031         *
7032         * <p>
7033         * Client can optionally pass a payload for partial change. These payloads will be merged
7034         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
7035         * item is already represented by a ViewHolder and it will be rebound to the same
7036         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
7037         * payloads on that item and prevent future payload until
7038         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
7039         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
7040         * attached, the payload will be simply dropped.
7041         *
7042         * @param position Position of the item that has changed
7043         * @param payload Optional parameter, use null to identify a "full" update
7044         *
7045         * @see #notifyItemRangeChanged(int, int)
7046         */
7047        public final void notifyItemChanged(int position, @Nullable Object payload) {
7048            mObservable.notifyItemRangeChanged(position, 1, payload);
7049        }
7050
7051        /**
7052         * Notify any registered observers that the <code>itemCount</code> items starting at
7053         * position <code>positionStart</code> have changed.
7054         * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
7055         *
7056         * <p>This is an item change event, not a structural change event. It indicates that
7057         * any reflection of the data in the given position range is out of date and should
7058         * be updated. The items in the given range retain the same identity.</p>
7059         *
7060         * @param positionStart Position of the first item that has changed
7061         * @param itemCount Number of items that have changed
7062         *
7063         * @see #notifyItemChanged(int)
7064         */
7065        public final void notifyItemRangeChanged(int positionStart, int itemCount) {
7066            mObservable.notifyItemRangeChanged(positionStart, itemCount);
7067        }
7068
7069        /**
7070         * Notify any registered observers that the <code>itemCount</code> items starting at
7071         * position <code>positionStart</code> have changed. An optional payload can be
7072         * passed to each changed item.
7073         *
7074         * <p>This is an item change event, not a structural change event. It indicates that any
7075         * reflection of the data in the given position range is out of date and should be updated.
7076         * The items in the given range retain the same identity.
7077         * </p>
7078         *
7079         * <p>
7080         * Client can optionally pass a payload for partial change. These payloads will be merged
7081         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
7082         * item is already represented by a ViewHolder and it will be rebound to the same
7083         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
7084         * payloads on that item and prevent future payload until
7085         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
7086         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
7087         * attached, the payload will be simply dropped.
7088         *
7089         * @param positionStart Position of the first item that has changed
7090         * @param itemCount Number of items that have changed
7091         * @param payload  Optional parameter, use null to identify a "full" update
7092         *
7093         * @see #notifyItemChanged(int)
7094         */
7095        public final void notifyItemRangeChanged(int positionStart, int itemCount,
7096                @Nullable Object payload) {
7097            mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
7098        }
7099
7100        /**
7101         * Notify any registered observers that the item reflected at <code>position</code>
7102         * has been newly inserted. The item previously at <code>position</code> is now at
7103         * position <code>position + 1</code>.
7104         *
7105         * <p>This is a structural change event. Representations of other existing items in the
7106         * data set are still considered up to date and will not be rebound, though their
7107         * positions may be altered.</p>
7108         *
7109         * @param position Position of the newly inserted item in the data set
7110         *
7111         * @see #notifyItemRangeInserted(int, int)
7112         */
7113        public final void notifyItemInserted(int position) {
7114            mObservable.notifyItemRangeInserted(position, 1);
7115        }
7116
7117        /**
7118         * Notify any registered observers that the item reflected at <code>fromPosition</code>
7119         * has been moved to <code>toPosition</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 fromPosition Previous position of the item.
7126         * @param toPosition New position of the item.
7127         */
7128        public final void notifyItemMoved(int fromPosition, int toPosition) {
7129            mObservable.notifyItemMoved(fromPosition, toPosition);
7130        }
7131
7132        /**
7133         * Notify any registered observers that the currently reflected <code>itemCount</code>
7134         * items starting at <code>positionStart</code> have been newly inserted. The items
7135         * previously located at <code>positionStart</code> and beyond can now be found starting
7136         * at position <code>positionStart + itemCount</code>.
7137         *
7138         * <p>This is a structural change event. Representations of other existing items in the
7139         * data set are still considered up to date and will not be rebound, though their positions
7140         * may be altered.</p>
7141         *
7142         * @param positionStart Position of the first item that was inserted
7143         * @param itemCount Number of items inserted
7144         *
7145         * @see #notifyItemInserted(int)
7146         */
7147        public final void notifyItemRangeInserted(int positionStart, int itemCount) {
7148            mObservable.notifyItemRangeInserted(positionStart, itemCount);
7149        }
7150
7151        /**
7152         * Notify any registered observers that the item previously located at <code>position</code>
7153         * has been removed from the data set. The items previously located at and after
7154         * <code>position</code> may now be found at <code>oldPosition - 1</code>.
7155         *
7156         * <p>This is a structural change event. Representations of other existing items in the
7157         * data set are still considered up to date and will not be rebound, though their positions
7158         * may be altered.</p>
7159         *
7160         * @param position Position of the item that has now been removed
7161         *
7162         * @see #notifyItemRangeRemoved(int, int)
7163         */
7164        public final void notifyItemRemoved(int position) {
7165            mObservable.notifyItemRangeRemoved(position, 1);
7166        }
7167
7168        /**
7169         * Notify any registered observers that the <code>itemCount</code> items previously
7170         * located at <code>positionStart</code> have been removed from the data set. The items
7171         * previously located at and after <code>positionStart + itemCount</code> may now be found
7172         * at <code>oldPosition - itemCount</code>.
7173         *
7174         * <p>This is a structural change event. Representations of other existing items in the data
7175         * set are still considered up to date and will not be rebound, though their positions
7176         * may be altered.</p>
7177         *
7178         * @param positionStart Previous position of the first item that was removed
7179         * @param itemCount Number of items removed from the data set
7180         */
7181        public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
7182            mObservable.notifyItemRangeRemoved(positionStart, itemCount);
7183        }
7184    }
7185
7186    void dispatchChildDetached(View child) {
7187        final ViewHolder viewHolder = getChildViewHolderInt(child);
7188        onChildDetachedFromWindow(child);
7189        if (mAdapter != null && viewHolder != null) {
7190            mAdapter.onViewDetachedFromWindow(viewHolder);
7191        }
7192        if (mOnChildAttachStateListeners != null) {
7193            final int cnt = mOnChildAttachStateListeners.size();
7194            for (int i = cnt - 1; i >= 0; i--) {
7195                mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
7196            }
7197        }
7198    }
7199
7200    void dispatchChildAttached(View child) {
7201        final ViewHolder viewHolder = getChildViewHolderInt(child);
7202        onChildAttachedToWindow(child);
7203        if (mAdapter != null && viewHolder != null) {
7204            mAdapter.onViewAttachedToWindow(viewHolder);
7205        }
7206        if (mOnChildAttachStateListeners != null) {
7207            final int cnt = mOnChildAttachStateListeners.size();
7208            for (int i = cnt - 1; i >= 0; i--) {
7209                mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
7210            }
7211        }
7212    }
7213
7214    /**
7215     * A <code>LayoutManager</code> is responsible for measuring and positioning item views
7216     * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
7217     * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
7218     * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
7219     * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
7220     * layout managers are provided for general use.
7221     * <p/>
7222     * If the LayoutManager specifies a default constructor or one with the signature
7223     * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
7224     * instantiate and set the LayoutManager when being inflated. Most used properties can
7225     * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
7226     * a LayoutManager specifies both constructors, the non-default constructor will take
7227     * precedence.
7228     *
7229     */
7230    public abstract static class LayoutManager {
7231        ChildHelper mChildHelper;
7232        RecyclerView mRecyclerView;
7233
7234        /**
7235         * The callback used for retrieving information about a RecyclerView and its children in the
7236         * horizontal direction.
7237         */
7238        private final ViewBoundsCheck.Callback mHorizontalBoundCheckCallback =
7239                new ViewBoundsCheck.Callback() {
7240                    @Override
7241                    public int getChildCount() {
7242                        return LayoutManager.this.getChildCount();
7243                    }
7244
7245                    @Override
7246                    public View getParent() {
7247                        return mRecyclerView;
7248                    }
7249
7250                    @Override
7251                    public View getChildAt(int index) {
7252                        return LayoutManager.this.getChildAt(index);
7253                    }
7254
7255                    @Override
7256                    public int getParentStart() {
7257                        return LayoutManager.this.getPaddingLeft();
7258                    }
7259
7260                    @Override
7261                    public int getParentEnd() {
7262                        return LayoutManager.this.getWidth() - LayoutManager.this.getPaddingRight();
7263                    }
7264
7265                    @Override
7266                    public int getChildStart(View view) {
7267                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7268                                view.getLayoutParams();
7269                        return LayoutManager.this.getDecoratedLeft(view) - params.leftMargin;
7270                    }
7271
7272                    @Override
7273                    public int getChildEnd(View view) {
7274                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7275                                view.getLayoutParams();
7276                        return LayoutManager.this.getDecoratedRight(view) + params.rightMargin;
7277                    }
7278                };
7279
7280        /**
7281         * The callback used for retrieving information about a RecyclerView and its children in the
7282         * vertical direction.
7283         */
7284        private final ViewBoundsCheck.Callback mVerticalBoundCheckCallback =
7285                new ViewBoundsCheck.Callback() {
7286                    @Override
7287                    public int getChildCount() {
7288                        return LayoutManager.this.getChildCount();
7289                    }
7290
7291                    @Override
7292                    public View getParent() {
7293                        return mRecyclerView;
7294                    }
7295
7296                    @Override
7297                    public View getChildAt(int index) {
7298                        return LayoutManager.this.getChildAt(index);
7299                    }
7300
7301                    @Override
7302                    public int getParentStart() {
7303                        return LayoutManager.this.getPaddingTop();
7304                    }
7305
7306                    @Override
7307                    public int getParentEnd() {
7308                        return LayoutManager.this.getHeight()
7309                                - LayoutManager.this.getPaddingBottom();
7310                    }
7311
7312                    @Override
7313                    public int getChildStart(View view) {
7314                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7315                                view.getLayoutParams();
7316                        return LayoutManager.this.getDecoratedTop(view) - params.topMargin;
7317                    }
7318
7319                    @Override
7320                    public int getChildEnd(View view) {
7321                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7322                                view.getLayoutParams();
7323                        return LayoutManager.this.getDecoratedBottom(view) + params.bottomMargin;
7324                    }
7325                };
7326
7327        /**
7328         * Utility objects used to check the boundaries of children against their parent
7329         * RecyclerView.
7330         * @see #isViewPartiallyVisible(View, boolean, boolean),
7331         * {@link LinearLayoutManager#findOneVisibleChild(int, int, boolean, boolean)},
7332         * and {@link LinearLayoutManager#findOnePartiallyOrCompletelyInvisibleChild(int, int)}.
7333         */
7334        ViewBoundsCheck mHorizontalBoundCheck = new ViewBoundsCheck(mHorizontalBoundCheckCallback);
7335        ViewBoundsCheck mVerticalBoundCheck = new ViewBoundsCheck(mVerticalBoundCheckCallback);
7336
7337        @Nullable
7338        SmoothScroller mSmoothScroller;
7339
7340        boolean mRequestedSimpleAnimations = false;
7341
7342        boolean mIsAttachedToWindow = false;
7343
7344        /**
7345         * This field is only set via the deprecated {@link #setAutoMeasureEnabled(boolean)} and is
7346         * only accessed via {@link #isAutoMeasureEnabled()} for backwards compatability reasons.
7347         */
7348        boolean mAutoMeasure = false;
7349
7350        /**
7351         * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
7352         * if the space that will be given to it is already larger than what it has measured before.
7353         */
7354        private boolean mMeasurementCacheEnabled = true;
7355
7356        private boolean mItemPrefetchEnabled = true;
7357
7358        /**
7359         * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
7360         * requested by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)} or
7361         * {@link #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)} call.
7362         *
7363         * If expanded by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)},
7364         * will be reset upon layout to prevent initial prefetches (often large, since they're
7365         * proportional to expected child count) from expanding cache permanently.
7366         */
7367        int mPrefetchMaxCountObserved;
7368
7369        /**
7370         * If true, mPrefetchMaxCountObserved is only valid until next layout, and should be reset.
7371         */
7372        boolean mPrefetchMaxObservedInInitialPrefetch;
7373
7374        /**
7375         * These measure specs might be the measure specs that were passed into RecyclerView's
7376         * onMeasure method OR fake measure specs created by the RecyclerView.
7377         * For example, when a layout is run, RecyclerView always sets these specs to be
7378         * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
7379         * <p>
7380         * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
7381         * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
7382         * corrupt values. Older platforms have no responsibility to provide a size if they set
7383         * mode to unspecified.
7384         */
7385        private int mWidthMode, mHeightMode;
7386        private int mWidth, mHeight;
7387
7388
7389        /**
7390         * Interface for LayoutManagers to request items to be prefetched, based on position, with
7391         * specified distance from viewport, which indicates priority.
7392         *
7393         * @see LayoutManager#collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
7394         * @see LayoutManager#collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
7395         */
7396        public interface LayoutPrefetchRegistry {
7397            /**
7398             * Requests an an item to be prefetched, based on position, with a specified distance,
7399             * indicating priority.
7400             *
7401             * @param layoutPosition Position of the item to prefetch.
7402             * @param pixelDistance Distance from the current viewport to the bounds of the item,
7403             *                      must be non-negative.
7404             */
7405            void addPosition(int layoutPosition, int pixelDistance);
7406        }
7407
7408        void setRecyclerView(RecyclerView recyclerView) {
7409            if (recyclerView == null) {
7410                mRecyclerView = null;
7411                mChildHelper = null;
7412                mWidth = 0;
7413                mHeight = 0;
7414            } else {
7415                mRecyclerView = recyclerView;
7416                mChildHelper = recyclerView.mChildHelper;
7417                mWidth = recyclerView.getWidth();
7418                mHeight = recyclerView.getHeight();
7419            }
7420            mWidthMode = MeasureSpec.EXACTLY;
7421            mHeightMode = MeasureSpec.EXACTLY;
7422        }
7423
7424        void setMeasureSpecs(int wSpec, int hSpec) {
7425            mWidth = MeasureSpec.getSize(wSpec);
7426            mWidthMode = MeasureSpec.getMode(wSpec);
7427            if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
7428                mWidth = 0;
7429            }
7430
7431            mHeight = MeasureSpec.getSize(hSpec);
7432            mHeightMode = MeasureSpec.getMode(hSpec);
7433            if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
7434                mHeight = 0;
7435            }
7436        }
7437
7438        /**
7439         * Called after a layout is calculated during a measure pass when using auto-measure.
7440         * <p>
7441         * It simply traverses all children to calculate a bounding box then calls
7442         * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
7443         * if they need to handle the bounding box differently.
7444         * <p>
7445         * For example, GridLayoutManager override that method to ensure that even if a column is
7446         * empty, the GridLayoutManager still measures wide enough to include it.
7447         *
7448         * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
7449         * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
7450         */
7451        void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
7452            final int count = getChildCount();
7453            if (count == 0) {
7454                mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
7455                return;
7456            }
7457            int minX = Integer.MAX_VALUE;
7458            int minY = Integer.MAX_VALUE;
7459            int maxX = Integer.MIN_VALUE;
7460            int maxY = Integer.MIN_VALUE;
7461
7462            for (int i = 0; i < count; i++) {
7463                View child = getChildAt(i);
7464                final Rect bounds = mRecyclerView.mTempRect;
7465                getDecoratedBoundsWithMargins(child, bounds);
7466                if (bounds.left < minX) {
7467                    minX = bounds.left;
7468                }
7469                if (bounds.right > maxX) {
7470                    maxX = bounds.right;
7471                }
7472                if (bounds.top < minY) {
7473                    minY = bounds.top;
7474                }
7475                if (bounds.bottom > maxY) {
7476                    maxY = bounds.bottom;
7477                }
7478            }
7479            mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
7480            setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
7481        }
7482
7483        /**
7484         * Sets the measured dimensions from the given bounding box of the children and the
7485         * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
7486         * only called if a LayoutManager returns <code>true</code> from
7487         * {@link #isAutoMeasureEnabled()} and it is called after the RecyclerView calls
7488         * {@link LayoutManager#onLayoutChildren(Recycler, State)} in the execution of
7489         * {@link RecyclerView#onMeasure(int, int)}.
7490         * <p>
7491         * This method must call {@link #setMeasuredDimension(int, int)}.
7492         * <p>
7493         * The default implementation adds the RecyclerView's padding to the given bounding box
7494         * then caps the value to be within the given measurement specs.
7495         *
7496         * @param childrenBounds The bounding box of all children
7497         * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
7498         * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
7499         *
7500         * @see #isAutoMeasureEnabled()
7501         * @see #setMeasuredDimension(int, int)
7502         */
7503        public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
7504            int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
7505            int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
7506            int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
7507            int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
7508            setMeasuredDimension(width, height);
7509        }
7510
7511        /**
7512         * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
7513         */
7514        public void requestLayout() {
7515            if (mRecyclerView != null) {
7516                mRecyclerView.requestLayout();
7517            }
7518        }
7519
7520        /**
7521         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7522         * {@link IllegalStateException} if it <b>is not</b>.
7523         *
7524         * @param message The message for the exception. Can be null.
7525         * @see #assertNotInLayoutOrScroll(String)
7526         */
7527        public void assertInLayoutOrScroll(String message) {
7528            if (mRecyclerView != null) {
7529                mRecyclerView.assertInLayoutOrScroll(message);
7530            }
7531        }
7532
7533        /**
7534         * Chooses a size from the given specs and parameters that is closest to the desired size
7535         * and also complies with the spec.
7536         *
7537         * @param spec The measureSpec
7538         * @param desired The preferred measurement
7539         * @param min The minimum value
7540         *
7541         * @return A size that fits to the given specs
7542         */
7543        public static int chooseSize(int spec, int desired, int min) {
7544            final int mode = View.MeasureSpec.getMode(spec);
7545            final int size = View.MeasureSpec.getSize(spec);
7546            switch (mode) {
7547                case View.MeasureSpec.EXACTLY:
7548                    return size;
7549                case View.MeasureSpec.AT_MOST:
7550                    return Math.min(size, Math.max(desired, min));
7551                case View.MeasureSpec.UNSPECIFIED:
7552                default:
7553                    return Math.max(desired, min);
7554            }
7555        }
7556
7557        /**
7558         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7559         * {@link IllegalStateException} if it <b>is</b>.
7560         *
7561         * @param message The message for the exception. Can be null.
7562         * @see #assertInLayoutOrScroll(String)
7563         */
7564        public void assertNotInLayoutOrScroll(String message) {
7565            if (mRecyclerView != null) {
7566                mRecyclerView.assertNotInLayoutOrScroll(message);
7567            }
7568        }
7569
7570        /**
7571         * Defines whether the measuring pass of layout should use the AutoMeasure mechanism of
7572         * {@link RecyclerView} or if it should be done by the LayoutManager's implementation of
7573         * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
7574         *
7575         * @param enabled <code>True</code> if layout measurement should be done by the
7576         *                RecyclerView, <code>false</code> if it should be done by this
7577         *                LayoutManager.
7578         *
7579         * @see #isAutoMeasureEnabled()
7580         *
7581         * @deprecated Implementors of LayoutManager should define whether or not it uses
7582         *             AutoMeasure by overriding {@link #isAutoMeasureEnabled()}.
7583         */
7584        @Deprecated
7585        public void setAutoMeasureEnabled(boolean enabled) {
7586            mAutoMeasure = enabled;
7587        }
7588
7589        /**
7590         * Returns whether the measuring pass of layout should use the AutoMeasure mechanism of
7591         * {@link RecyclerView} or if it should be done by the LayoutManager's implementation of
7592         * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
7593         * <p>
7594         * This method returns false by default (it actually returns the value passed to the
7595         * deprecated {@link #setAutoMeasureEnabled(boolean)}) and should be overridden to return
7596         * true if a LayoutManager wants to be auto measured by the RecyclerView.
7597         * <p>
7598         * If this method is overridden to return true,
7599         * {@link LayoutManager#onMeasure(Recycler, State, int, int)} should not be overridden.
7600         * <p>
7601         * AutoMeasure is a RecyclerView mechanism that handles the measuring pass of layout in a
7602         * simple and contract satisfying way, including the wrapping of children laid out by
7603         * LayoutManager. Simply put, it handles wrapping children by calling
7604         * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a call to
7605         * {@link RecyclerView#onMeasure(int, int)}, and then calculating desired dimensions based
7606         * on children's dimensions and positions. It does this while supporting all existing
7607         * animation capabilities of the RecyclerView.
7608         * <p>
7609         * More specifically:
7610         * <ol>
7611         * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided measure
7612         * specs both have a mode of {@link View.MeasureSpec#EXACTLY}, RecyclerView will set its
7613         * measured dimensions accordingly and return, allowing layout to continue as normal
7614         * (Actually, RecyclerView will call
7615         * {@link LayoutManager#onMeasure(Recycler, State, int, int)} for backwards compatibility
7616         * reasons but it should not be overridden if AutoMeasure is being used).</li>
7617         * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
7618         * layout process. It will first process all pending Adapter updates and
7619         * then decide whether to run a predictive layout. If it decides to do so, it will first
7620         * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
7621         * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
7622         * return the width and height of the RecyclerView as of the last layout calculation.
7623         * <p>
7624         * After handling the predictive case, RecyclerView will call
7625         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7626         * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
7627         * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
7628         * {@link #getWidth()} and {@link #getWidthMode()}.</li>
7629         * <li>After the layout calculation, RecyclerView sets the measured width & height by
7630         * calculating the bounding box for the children (+ RecyclerView's padding). The
7631         * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
7632         * different values. For instance, GridLayoutManager overrides this value to handle the case
7633         * where if it is vertical and has 3 columns but only 2 items, it should still measure its
7634         * width to fit 3 items, not 2.</li>
7635         * <li>Any following calls to {@link RecyclerView#onMeasure(int, int)} will run
7636         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7637         * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
7638         * take care of which views are actually added / removed / moved / changed for animations so
7639         * that the LayoutManager should not worry about them and handle each
7640         * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.</li>
7641         * <li>When measure is complete and RecyclerView's
7642         * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
7643         * whether it already did layout calculations during the measure pass and if so, it re-uses
7644         * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
7645         * if the last measure spec was different from the final dimensions or adapter contents
7646         * have changed between the measure call and the layout call.</li>
7647         * <li>Finally, animations are calculated and run as usual.</li>
7648         * </ol>
7649         *
7650         * @return <code>True</code> if the measuring pass of layout should use the AutoMeasure
7651         * mechanism of {@link RecyclerView} or <code>False</code> if it should be done by the
7652         * LayoutManager's implementation of
7653         * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
7654         *
7655         * @see #setMeasuredDimension(Rect, int, int)
7656         * @see #onMeasure(Recycler, State, int, int)
7657         */
7658        public boolean isAutoMeasureEnabled() {
7659            return mAutoMeasure;
7660        }
7661
7662        /**
7663         * Returns whether this LayoutManager supports "predictive item animations".
7664         * <p>
7665         * "Predictive item animations" are automatically created animations that show
7666         * where items came from, and where they are going to, as items are added, removed,
7667         * or moved within a layout.
7668         * <p>
7669         * A LayoutManager wishing to support predictive item animations must override this
7670         * method to return true (the default implementation returns false) and must obey certain
7671         * behavioral contracts outlined in {@link #onLayoutChildren(Recycler, State)}.
7672         * <p>
7673         * Whether item animations actually occur in a RecyclerView is actually determined by both
7674         * the return value from this method and the
7675         * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
7676         * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
7677         * method returns false, then only "simple item animations" will be enabled in the
7678         * RecyclerView, in which views whose position are changing are simply faded in/out. If the
7679         * RecyclerView has a non-null ItemAnimator and this method returns true, then predictive
7680         * item animations will be enabled in the RecyclerView.
7681         *
7682         * @return true if this LayoutManager supports predictive item animations, false otherwise.
7683         */
7684        public boolean supportsPredictiveItemAnimations() {
7685            return false;
7686        }
7687
7688        /**
7689         * Sets whether the LayoutManager should be queried for views outside of
7690         * its viewport while the UI thread is idle between frames.
7691         *
7692         * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
7693         * view system traversals on devices running API 21 or greater. Default value is true.</p>
7694         *
7695         * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
7696         * to RenderThread and the starting up its next frame at the next VSync pulse. By
7697         * prefetching out of window views in this time period, delays from inflation and view
7698         * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
7699         *
7700         * <p>While prefetch is enabled, it will have the side effect of expanding the effective
7701         * size of the View cache to hold prefetched views.</p>
7702         *
7703         * @param enabled <code>True</code> if items should be prefetched in between traversals.
7704         *
7705         * @see #isItemPrefetchEnabled()
7706         */
7707        public final void setItemPrefetchEnabled(boolean enabled) {
7708            if (enabled != mItemPrefetchEnabled) {
7709                mItemPrefetchEnabled = enabled;
7710                mPrefetchMaxCountObserved = 0;
7711                if (mRecyclerView != null) {
7712                    mRecyclerView.mRecycler.updateViewCacheSize();
7713                }
7714            }
7715        }
7716
7717        /**
7718         * Sets whether the LayoutManager should be queried for views outside of
7719         * its viewport while the UI thread is idle between frames.
7720         *
7721         * @see #setItemPrefetchEnabled(boolean)
7722         *
7723         * @return true if item prefetch is enabled, false otherwise
7724         */
7725        public final boolean isItemPrefetchEnabled() {
7726            return mItemPrefetchEnabled;
7727        }
7728
7729        /**
7730         * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
7731         *
7732         * <p>If item prefetch is enabled, this method is called in between traversals to gather
7733         * which positions the LayoutManager will soon need, given upcoming movement in subsequent
7734         * traversals.</p>
7735         *
7736         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7737         * each item to be prepared, and these positions will have their ViewHolders created and
7738         * bound, if there is sufficient time available, in advance of being needed by a
7739         * scroll or layout.</p>
7740         *
7741         * @param dx X movement component.
7742         * @param dy Y movement component.
7743         * @param state State of RecyclerView
7744         * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7745         *
7746         * @see #isItemPrefetchEnabled()
7747         * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
7748         */
7749        public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
7750                LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7751
7752        /**
7753         * Gather all positions from the LayoutManager to be prefetched in preperation for its
7754         * RecyclerView to come on screen, due to the movement of another, containing RecyclerView.
7755         *
7756         * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
7757         *
7758         * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
7759         * LayoutManager, this method is called in between draw traversals to gather
7760         * which positions this LayoutManager will first need, once it appears on the screen.</p>
7761         *
7762         * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
7763         * vertically scrolling LayoutManager, this method would be called when the horizontal list
7764         * is about to come onscreen.</p>
7765         *
7766         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7767         * each item to be prepared, and these positions will have their ViewHolders created and
7768         * bound, if there is sufficient time available, in advance of being needed by a
7769         * scroll or layout.</p>
7770         *
7771         * @param adapterItemCount number of items in the associated adapter.
7772         * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7773         *
7774         * @see #isItemPrefetchEnabled()
7775         * @see #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
7776         */
7777        public void collectInitialPrefetchPositions(int adapterItemCount,
7778                LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7779
7780        void dispatchAttachedToWindow(RecyclerView view) {
7781            mIsAttachedToWindow = true;
7782            onAttachedToWindow(view);
7783        }
7784
7785        void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
7786            mIsAttachedToWindow = false;
7787            onDetachedFromWindow(view, recycler);
7788        }
7789
7790        /**
7791         * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
7792         * to a window.
7793         *
7794         * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
7795         * is attached to window.
7796         */
7797        public boolean isAttachedToWindow() {
7798            return mIsAttachedToWindow;
7799        }
7800
7801        /**
7802         * Causes the Runnable to execute on the next animation time step.
7803         * The runnable will be run on the user interface thread.
7804         * <p>
7805         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7806         *
7807         * @param action The Runnable that will be executed.
7808         *
7809         * @see #removeCallbacks
7810         */
7811        public void postOnAnimation(Runnable action) {
7812            if (mRecyclerView != null) {
7813                ViewCompat.postOnAnimation(mRecyclerView, action);
7814            }
7815        }
7816
7817        /**
7818         * Removes the specified Runnable from the message queue.
7819         * <p>
7820         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7821         *
7822         * @param action The Runnable to remove from the message handling queue
7823         *
7824         * @return true if RecyclerView could ask the Handler to remove the Runnable,
7825         *         false otherwise. When the returned value is true, the Runnable
7826         *         may or may not have been actually removed from the message queue
7827         *         (for instance, if the Runnable was not in the queue already.)
7828         *
7829         * @see #postOnAnimation
7830         */
7831        public boolean removeCallbacks(Runnable action) {
7832            if (mRecyclerView != null) {
7833                return mRecyclerView.removeCallbacks(action);
7834            }
7835            return false;
7836        }
7837        /**
7838         * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
7839         * is attached to a window.
7840         * <p>
7841         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7842         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7843         * not requested on the RecyclerView while it was detached.
7844         * <p>
7845         * Subclass implementations should always call through to the superclass implementation.
7846         *
7847         * @param view The RecyclerView this LayoutManager is bound to
7848         *
7849         * @see #onDetachedFromWindow(RecyclerView, Recycler)
7850         */
7851        @CallSuper
7852        public void onAttachedToWindow(RecyclerView view) {
7853        }
7854
7855        /**
7856         * @deprecated
7857         * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
7858         */
7859        @Deprecated
7860        public void onDetachedFromWindow(RecyclerView view) {
7861
7862        }
7863
7864        /**
7865         * Called when this LayoutManager is detached from its parent RecyclerView or when
7866         * its parent RecyclerView is detached from its window.
7867         * <p>
7868         * LayoutManager should clear all of its View references as another LayoutManager might be
7869         * assigned to the RecyclerView.
7870         * <p>
7871         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7872         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7873         * not requested on the RecyclerView while it was detached.
7874         * <p>
7875         * If your LayoutManager has View references that it cleans in on-detach, it should also
7876         * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
7877         * RecyclerView is re-attached.
7878         * <p>
7879         * Subclass implementations should always call through to the superclass implementation.
7880         *
7881         * @param view The RecyclerView this LayoutManager is bound to
7882         * @param recycler The recycler to use if you prefer to recycle your children instead of
7883         *                 keeping them around.
7884         *
7885         * @see #onAttachedToWindow(RecyclerView)
7886         */
7887        @CallSuper
7888        public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
7889            onDetachedFromWindow(view);
7890        }
7891
7892        /**
7893         * Check if the RecyclerView is configured to clip child views to its padding.
7894         *
7895         * @return true if this RecyclerView clips children to its padding, false otherwise
7896         */
7897        public boolean getClipToPadding() {
7898            return mRecyclerView != null && mRecyclerView.mClipToPadding;
7899        }
7900
7901        /**
7902         * Lay out all relevant child views from the given adapter.
7903         *
7904         * The LayoutManager is in charge of the behavior of item animations. By default,
7905         * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
7906         * item animations are enabled. This means that add/remove operations on the
7907         * adapter will result in animations to add new or appearing items, removed or
7908         * disappearing items, and moved items. If a LayoutManager returns false from
7909         * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
7910         * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
7911         * RecyclerView will have enough information to run those animations in a simple
7912         * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
7913         * simply fade views in and out, whether they are actually added/removed or whether
7914         * they are moved on or off the screen due to other add/remove operations.
7915         *
7916         * <p>A LayoutManager wanting a better item animation experience, where items can be
7917         * animated onto and off of the screen according to where the items exist when they
7918         * are not on screen, then the LayoutManager should return true from
7919         * {@link #supportsPredictiveItemAnimations()} and add additional logic to
7920         * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
7921         * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
7922         * once as a "pre" layout step to determine where items would have been prior to
7923         * a real layout, and again to do the "real" layout. In the pre-layout phase,
7924         * items will remember their pre-layout positions to allow them to be laid out
7925         * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
7926         * be returned from the scrap to help determine correct placement of other items.
7927         * These removed items should not be added to the child list, but should be used
7928         * to help calculate correct positioning of other views, including views that
7929         * were not previously onscreen (referred to as APPEARING views), but whose
7930         * pre-layout offscreen position can be determined given the extra
7931         * information about the pre-layout removed views.</p>
7932         *
7933         * <p>The second layout pass is the real layout in which only non-removed views
7934         * will be used. The only additional requirement during this pass is, if
7935         * {@link #supportsPredictiveItemAnimations()} returns true, to note which
7936         * views exist in the child list prior to layout and which are not there after
7937         * layout (referred to as DISAPPEARING views), and to position/layout those views
7938         * appropriately, without regard to the actual bounds of the RecyclerView. This allows
7939         * the animation system to know the location to which to animate these disappearing
7940         * views.</p>
7941         *
7942         * <p>The default LayoutManager implementations for RecyclerView handle all of these
7943         * requirements for animations already. Clients of RecyclerView can either use one
7944         * of these layout managers directly or look at their implementations of
7945         * onLayoutChildren() to see how they account for the APPEARING and
7946         * DISAPPEARING views.</p>
7947         *
7948         * @param recycler         Recycler to use for fetching potentially cached views for a
7949         *                         position
7950         * @param state            Transient state of RecyclerView
7951         */
7952        public void onLayoutChildren(Recycler recycler, State state) {
7953            Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
7954        }
7955
7956        /**
7957         * Called after a full layout calculation is finished. The layout calculation may include
7958         * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
7959         * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
7960         * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
7961         * <p>
7962         * This is a good place for the LayoutManager to do some cleanup like pending scroll
7963         * position, saved state etc.
7964         *
7965         * @param state Transient state of RecyclerView
7966         */
7967        public void onLayoutCompleted(State state) {
7968        }
7969
7970        /**
7971         * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
7972         *
7973         * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
7974         * to store extra information specific to the layout. Client code should subclass
7975         * {@link RecyclerView.LayoutParams} for this purpose.</p>
7976         *
7977         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7978         * you must also override
7979         * {@link #checkLayoutParams(LayoutParams)},
7980         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7981         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7982         *
7983         * @return A new LayoutParams for a child view
7984         */
7985        public abstract LayoutParams generateDefaultLayoutParams();
7986
7987        /**
7988         * Determines the validity of the supplied LayoutParams object.
7989         *
7990         * <p>This should check to make sure that the object is of the correct type
7991         * and all values are within acceptable ranges. The default implementation
7992         * returns <code>true</code> for non-null params.</p>
7993         *
7994         * @param lp LayoutParams object to check
7995         * @return true if this LayoutParams object is valid, false otherwise
7996         */
7997        public boolean checkLayoutParams(LayoutParams lp) {
7998            return lp != null;
7999        }
8000
8001        /**
8002         * Create a LayoutParams object suitable for this LayoutManager, copying relevant
8003         * values from the supplied LayoutParams object if possible.
8004         *
8005         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
8006         * you must also override
8007         * {@link #checkLayoutParams(LayoutParams)},
8008         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
8009         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
8010         *
8011         * @param lp Source LayoutParams object to copy values from
8012         * @return a new LayoutParams object
8013         */
8014        public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
8015            if (lp instanceof LayoutParams) {
8016                return new LayoutParams((LayoutParams) lp);
8017            } else if (lp instanceof MarginLayoutParams) {
8018                return new LayoutParams((MarginLayoutParams) lp);
8019            } else {
8020                return new LayoutParams(lp);
8021            }
8022        }
8023
8024        /**
8025         * Create a LayoutParams object suitable for this LayoutManager from
8026         * an inflated layout resource.
8027         *
8028         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
8029         * you must also override
8030         * {@link #checkLayoutParams(LayoutParams)},
8031         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
8032         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
8033         *
8034         * @param c Context for obtaining styled attributes
8035         * @param attrs AttributeSet describing the supplied arguments
8036         * @return a new LayoutParams object
8037         */
8038        public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
8039            return new LayoutParams(c, attrs);
8040        }
8041
8042        /**
8043         * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
8044         * The default implementation does nothing and returns 0.
8045         *
8046         * @param dx            distance to scroll by in pixels. X increases as scroll position
8047         *                      approaches the right.
8048         * @param recycler      Recycler to use for fetching potentially cached views for a
8049         *                      position
8050         * @param state         Transient state of RecyclerView
8051         * @return The actual distance scrolled. The return value will be negative if dx was
8052         * negative and scrolling proceeeded in that direction.
8053         * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
8054         */
8055        public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
8056            return 0;
8057        }
8058
8059        /**
8060         * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
8061         * The default implementation does nothing and returns 0.
8062         *
8063         * @param dy            distance to scroll in pixels. Y increases as scroll position
8064         *                      approaches the bottom.
8065         * @param recycler      Recycler to use for fetching potentially cached views for a
8066         *                      position
8067         * @param state         Transient state of RecyclerView
8068         * @return The actual distance scrolled. The return value will be negative if dy was
8069         * negative and scrolling proceeeded in that direction.
8070         * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
8071         */
8072        public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
8073            return 0;
8074        }
8075
8076        /**
8077         * Query if horizontal scrolling is currently supported. The default implementation
8078         * returns false.
8079         *
8080         * @return True if this LayoutManager can scroll the current contents horizontally
8081         */
8082        public boolean canScrollHorizontally() {
8083            return false;
8084        }
8085
8086        /**
8087         * Query if vertical scrolling is currently supported. The default implementation
8088         * returns false.
8089         *
8090         * @return True if this LayoutManager can scroll the current contents vertically
8091         */
8092        public boolean canScrollVertically() {
8093            return false;
8094        }
8095
8096        /**
8097         * Scroll to the specified adapter position.
8098         *
8099         * Actual position of the item on the screen depends on the LayoutManager implementation.
8100         * @param position Scroll to this adapter position.
8101         */
8102        public void scrollToPosition(int position) {
8103            if (DEBUG) {
8104                Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
8105            }
8106        }
8107
8108        /**
8109         * <p>Smooth scroll to the specified adapter position.</p>
8110         * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
8111         * instance and call {@link #startSmoothScroll(SmoothScroller)}.
8112         * </p>
8113         * @param recyclerView The RecyclerView to which this layout manager is attached
8114         * @param state    Current State of RecyclerView
8115         * @param position Scroll to this adapter position.
8116         */
8117        public void smoothScrollToPosition(RecyclerView recyclerView, State state,
8118                int position) {
8119            Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
8120        }
8121
8122        /**
8123         * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
8124         * <p>Calling this method will cancel any previous smooth scroll request.</p>
8125         * @param smoothScroller Instance which defines how smooth scroll should be animated
8126         */
8127        public void startSmoothScroll(SmoothScroller smoothScroller) {
8128            if (mSmoothScroller != null && smoothScroller != mSmoothScroller
8129                    && mSmoothScroller.isRunning()) {
8130                mSmoothScroller.stop();
8131            }
8132            mSmoothScroller = smoothScroller;
8133            mSmoothScroller.start(mRecyclerView, this);
8134        }
8135
8136        /**
8137         * @return true if RecyclerView is currently in the state of smooth scrolling.
8138         */
8139        public boolean isSmoothScrolling() {
8140            return mSmoothScroller != null && mSmoothScroller.isRunning();
8141        }
8142
8143
8144        /**
8145         * Returns the resolved layout direction for this RecyclerView.
8146         *
8147         * @return {@link androidx.core.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout
8148         * direction is RTL or returns
8149         * {@link androidx.core.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction
8150         * is not RTL.
8151         */
8152        public int getLayoutDirection() {
8153            return ViewCompat.getLayoutDirection(mRecyclerView);
8154        }
8155
8156        /**
8157         * Ends all animations on the view created by the {@link ItemAnimator}.
8158         *
8159         * @param view The View for which the animations should be ended.
8160         * @see RecyclerView.ItemAnimator#endAnimations()
8161         */
8162        public void endAnimation(View view) {
8163            if (mRecyclerView.mItemAnimator != null) {
8164                mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
8165            }
8166        }
8167
8168        /**
8169         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
8170         * to the layout that is known to be going away, either because it has been
8171         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
8172         * visible portion of the container but is being laid out in order to inform RecyclerView
8173         * in how to animate the item out of view.
8174         * <p>
8175         * Views added via this method are going to be invisible to LayoutManager after the
8176         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
8177         * or won't be included in {@link #getChildCount()} method.
8178         *
8179         * @param child View to add and then remove with animation.
8180         */
8181        public void addDisappearingView(View child) {
8182            addDisappearingView(child, -1);
8183        }
8184
8185        /**
8186         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
8187         * to the layout that is known to be going away, either because it has been
8188         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
8189         * visible portion of the container but is being laid out in order to inform RecyclerView
8190         * in how to animate the item out of view.
8191         * <p>
8192         * Views added via this method are going to be invisible to LayoutManager after the
8193         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
8194         * or won't be included in {@link #getChildCount()} method.
8195         *
8196         * @param child View to add and then remove with animation.
8197         * @param index Index of the view.
8198         */
8199        public void addDisappearingView(View child, int index) {
8200            addViewInt(child, index, true);
8201        }
8202
8203        /**
8204         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
8205         * use this method to add views obtained from a {@link Recycler} using
8206         * {@link Recycler#getViewForPosition(int)}.
8207         *
8208         * @param child View to add
8209         */
8210        public void addView(View child) {
8211            addView(child, -1);
8212        }
8213
8214        /**
8215         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
8216         * use this method to add views obtained from a {@link Recycler} using
8217         * {@link Recycler#getViewForPosition(int)}.
8218         *
8219         * @param child View to add
8220         * @param index Index to add child at
8221         */
8222        public void addView(View child, int index) {
8223            addViewInt(child, index, false);
8224        }
8225
8226        private void addViewInt(View child, int index, boolean disappearing) {
8227            final ViewHolder holder = getChildViewHolderInt(child);
8228            if (disappearing || holder.isRemoved()) {
8229                // these views will be hidden at the end of the layout pass.
8230                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
8231            } else {
8232                // This may look like unnecessary but may happen if layout manager supports
8233                // predictive layouts and adapter removed then re-added the same item.
8234                // In this case, added version will be visible in the post layout (because add is
8235                // deferred) but RV will still bind it to the same View.
8236                // So if a View re-appears in post layout pass, remove it from disappearing list.
8237                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
8238            }
8239            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8240            if (holder.wasReturnedFromScrap() || holder.isScrap()) {
8241                if (holder.isScrap()) {
8242                    holder.unScrap();
8243                } else {
8244                    holder.clearReturnedFromScrapFlag();
8245                }
8246                mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
8247                if (DISPATCH_TEMP_DETACH) {
8248                    ViewCompat.dispatchFinishTemporaryDetach(child);
8249                }
8250            } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
8251                // ensure in correct position
8252                int currentIndex = mChildHelper.indexOfChild(child);
8253                if (index == -1) {
8254                    index = mChildHelper.getChildCount();
8255                }
8256                if (currentIndex == -1) {
8257                    throw new IllegalStateException("Added View has RecyclerView as parent but"
8258                            + " view is not a real child. Unfiltered index:"
8259                            + mRecyclerView.indexOfChild(child) + mRecyclerView.exceptionLabel());
8260                }
8261                if (currentIndex != index) {
8262                    mRecyclerView.mLayout.moveView(currentIndex, index);
8263                }
8264            } else {
8265                mChildHelper.addView(child, index, false);
8266                lp.mInsetsDirty = true;
8267                if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
8268                    mSmoothScroller.onChildAttachedToWindow(child);
8269                }
8270            }
8271            if (lp.mPendingInvalidate) {
8272                if (DEBUG) {
8273                    Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
8274                }
8275                holder.itemView.invalidate();
8276                lp.mPendingInvalidate = false;
8277            }
8278        }
8279
8280        /**
8281         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
8282         * use this method to completely remove a child view that is no longer needed.
8283         * LayoutManagers should strongly consider recycling removed views using
8284         * {@link Recycler#recycleView(android.view.View)}.
8285         *
8286         * @param child View to remove
8287         */
8288        public void removeView(View child) {
8289            mChildHelper.removeView(child);
8290        }
8291
8292        /**
8293         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
8294         * use this method to completely remove a child view that is no longer needed.
8295         * LayoutManagers should strongly consider recycling removed views using
8296         * {@link Recycler#recycleView(android.view.View)}.
8297         *
8298         * @param index Index of the child view to remove
8299         */
8300        public void removeViewAt(int index) {
8301            final View child = getChildAt(index);
8302            if (child != null) {
8303                mChildHelper.removeViewAt(index);
8304            }
8305        }
8306
8307        /**
8308         * Remove all views from the currently attached RecyclerView. This will not recycle
8309         * any of the affected views; the LayoutManager is responsible for doing so if desired.
8310         */
8311        public void removeAllViews() {
8312            // Only remove non-animating views
8313            final int childCount = getChildCount();
8314            for (int i = childCount - 1; i >= 0; i--) {
8315                mChildHelper.removeViewAt(i);
8316            }
8317        }
8318
8319        /**
8320         * Returns offset of the RecyclerView's text baseline from the its top boundary.
8321         *
8322         * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
8323         * there is no baseline.
8324         */
8325        public int getBaseline() {
8326            return -1;
8327        }
8328
8329        /**
8330         * Returns the adapter position of the item represented by the given View. This does not
8331         * contain any adapter changes that might have happened after the last layout.
8332         *
8333         * @param view The view to query
8334         * @return The adapter position of the item which is rendered by this View.
8335         */
8336        public int getPosition(@NonNull View view) {
8337            return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
8338        }
8339
8340        /**
8341         * Returns the View type defined by the adapter.
8342         *
8343         * @param view The view to query
8344         * @return The type of the view assigned by the adapter.
8345         */
8346        public int getItemViewType(@NonNull View view) {
8347            return getChildViewHolderInt(view).getItemViewType();
8348        }
8349
8350        /**
8351         * Traverses the ancestors of the given view and returns the item view that contains it
8352         * and also a direct child of the LayoutManager.
8353         * <p>
8354         * Note that this method may return null if the view is a child of the RecyclerView but
8355         * not a child of the LayoutManager (e.g. running a disappear animation).
8356         *
8357         * @param view The view that is a descendant of the LayoutManager.
8358         *
8359         * @return The direct child of the LayoutManager which contains the given view or null if
8360         * the provided view is not a descendant of this LayoutManager.
8361         *
8362         * @see RecyclerView#getChildViewHolder(View)
8363         * @see RecyclerView#findContainingViewHolder(View)
8364         */
8365        @Nullable
8366        public View findContainingItemView(@NonNull View view) {
8367            if (mRecyclerView == null) {
8368                return null;
8369            }
8370            View found = mRecyclerView.findContainingItemView(view);
8371            if (found == null) {
8372                return null;
8373            }
8374            if (mChildHelper.isHidden(found)) {
8375                return null;
8376            }
8377            return found;
8378        }
8379
8380        /**
8381         * Finds the view which represents the given adapter position.
8382         * <p>
8383         * This method traverses each child since it has no information about child order.
8384         * Override this method to improve performance if your LayoutManager keeps data about
8385         * child views.
8386         * <p>
8387         * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
8388         *
8389         * @param position Position of the item in adapter
8390         * @return The child view that represents the given position or null if the position is not
8391         * laid out
8392         */
8393        @Nullable
8394        public View findViewByPosition(int position) {
8395            final int childCount = getChildCount();
8396            for (int i = 0; i < childCount; i++) {
8397                View child = getChildAt(i);
8398                ViewHolder vh = getChildViewHolderInt(child);
8399                if (vh == null) {
8400                    continue;
8401                }
8402                if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
8403                        && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
8404                    return child;
8405                }
8406            }
8407            return null;
8408        }
8409
8410        /**
8411         * Temporarily detach a child view.
8412         *
8413         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
8414         * views currently attached to the RecyclerView. Generally LayoutManager implementations
8415         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
8416         * so that the detached view may be rebound and reused.</p>
8417         *
8418         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
8419         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
8420         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
8421         * before the LayoutManager entry point method called by RecyclerView returns.</p>
8422         *
8423         * @param child Child to detach
8424         */
8425        public void detachView(@NonNull View child) {
8426            final int ind = mChildHelper.indexOfChild(child);
8427            if (ind >= 0) {
8428                detachViewInternal(ind, child);
8429            }
8430        }
8431
8432        /**
8433         * Temporarily detach a child view.
8434         *
8435         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
8436         * views currently attached to the RecyclerView. Generally LayoutManager implementations
8437         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
8438         * so that the detached view may be rebound and reused.</p>
8439         *
8440         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
8441         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
8442         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
8443         * before the LayoutManager entry point method called by RecyclerView returns.</p>
8444         *
8445         * @param index Index of the child to detach
8446         */
8447        public void detachViewAt(int index) {
8448            detachViewInternal(index, getChildAt(index));
8449        }
8450
8451        private void detachViewInternal(int index, @NonNull View view) {
8452            if (DISPATCH_TEMP_DETACH) {
8453                ViewCompat.dispatchStartTemporaryDetach(view);
8454            }
8455            mChildHelper.detachViewFromParent(index);
8456        }
8457
8458        /**
8459         * Reattach a previously {@link #detachView(android.view.View) detached} view.
8460         * This method should not be used to reattach views that were previously
8461         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8462         *
8463         * @param child Child to reattach
8464         * @param index Intended child index for child
8465         * @param lp LayoutParams for child
8466         */
8467        public void attachView(@NonNull View child, int index, LayoutParams lp) {
8468            ViewHolder vh = getChildViewHolderInt(child);
8469            if (vh.isRemoved()) {
8470                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
8471            } else {
8472                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
8473            }
8474            mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
8475            if (DISPATCH_TEMP_DETACH)  {
8476                ViewCompat.dispatchFinishTemporaryDetach(child);
8477            }
8478        }
8479
8480        /**
8481         * Reattach a previously {@link #detachView(android.view.View) detached} view.
8482         * This method should not be used to reattach views that were previously
8483         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8484         *
8485         * @param child Child to reattach
8486         * @param index Intended child index for child
8487         */
8488        public void attachView(@NonNull View child, int index) {
8489            attachView(child, index, (LayoutParams) child.getLayoutParams());
8490        }
8491
8492        /**
8493         * Reattach a previously {@link #detachView(android.view.View) detached} view.
8494         * This method should not be used to reattach views that were previously
8495         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8496         *
8497         * @param child Child to reattach
8498         */
8499        public void attachView(@NonNull View child) {
8500            attachView(child, -1);
8501        }
8502
8503        /**
8504         * Finish removing a view that was previously temporarily
8505         * {@link #detachView(android.view.View) detached}.
8506         *
8507         * @param child Detached child to remove
8508         */
8509        public void removeDetachedView(@NonNull View child) {
8510            mRecyclerView.removeDetachedView(child, false);
8511        }
8512
8513        /**
8514         * Moves a View from one position to another.
8515         *
8516         * @param fromIndex The View's initial index
8517         * @param toIndex The View's target index
8518         */
8519        public void moveView(int fromIndex, int toIndex) {
8520            View view = getChildAt(fromIndex);
8521            if (view == null) {
8522                throw new IllegalArgumentException("Cannot move a child from non-existing index:"
8523                        + fromIndex + mRecyclerView.toString());
8524            }
8525            detachViewAt(fromIndex);
8526            attachView(view, toIndex);
8527        }
8528
8529        /**
8530         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8531         *
8532         * <p>Scrapping a view allows it to be rebound and reused to show updated or
8533         * different data.</p>
8534         *
8535         * @param child Child to detach and scrap
8536         * @param recycler Recycler to deposit the new scrap view into
8537         */
8538        public void detachAndScrapView(@NonNull View child, @NonNull Recycler recycler) {
8539            int index = mChildHelper.indexOfChild(child);
8540            scrapOrRecycleView(recycler, index, child);
8541        }
8542
8543        /**
8544         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8545         *
8546         * <p>Scrapping a view allows it to be rebound and reused to show updated or
8547         * different data.</p>
8548         *
8549         * @param index Index of child to detach and scrap
8550         * @param recycler Recycler to deposit the new scrap view into
8551         */
8552        public void detachAndScrapViewAt(int index, @NonNull Recycler recycler) {
8553            final View child = getChildAt(index);
8554            scrapOrRecycleView(recycler, index, child);
8555        }
8556
8557        /**
8558         * Remove a child view and recycle it using the given Recycler.
8559         *
8560         * @param child Child to remove and recycle
8561         * @param recycler Recycler to use to recycle child
8562         */
8563        public void removeAndRecycleView(@NonNull View child, @NonNull Recycler recycler) {
8564            removeView(child);
8565            recycler.recycleView(child);
8566        }
8567
8568        /**
8569         * Remove a child view and recycle it using the given Recycler.
8570         *
8571         * @param index Index of child to remove and recycle
8572         * @param recycler Recycler to use to recycle child
8573         */
8574        public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
8575            final View view = getChildAt(index);
8576            removeViewAt(index);
8577            recycler.recycleView(view);
8578        }
8579
8580        /**
8581         * Return the current number of child views attached to the parent RecyclerView.
8582         * This does not include child views that were temporarily detached and/or scrapped.
8583         *
8584         * @return Number of attached children
8585         */
8586        public int getChildCount() {
8587            return mChildHelper != null ? mChildHelper.getChildCount() : 0;
8588        }
8589
8590        /**
8591         * Return the child view at the given index
8592         * @param index Index of child to return
8593         * @return Child view at index
8594         */
8595        @Nullable
8596        public View getChildAt(int index) {
8597            return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
8598        }
8599
8600        /**
8601         * Return the width measurement spec mode that is currently relevant to the LayoutManager.
8602         *
8603         * <p>This value is set only if the LayoutManager opts into the AutoMeasure api via
8604         * {@link #setAutoMeasureEnabled(boolean)}.
8605         *
8606         * <p>When RecyclerView is running a layout, this value is always set to
8607         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8608         *
8609         * @return Width measure spec mode
8610         *
8611         * @see View.MeasureSpec#getMode(int)
8612         */
8613        public int getWidthMode() {
8614            return mWidthMode;
8615        }
8616
8617        /**
8618         * Return the height measurement spec mode that is currently relevant to the LayoutManager.
8619         *
8620         * <p>This value is set only if the LayoutManager opts into the AutoMeasure api via
8621         * {@link #setAutoMeasureEnabled(boolean)}.
8622         *
8623         * <p>When RecyclerView is running a layout, this value is always set to
8624         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8625         *
8626         * @return Height measure spec mode
8627         *
8628         * @see View.MeasureSpec#getMode(int)
8629         */
8630        public int getHeightMode() {
8631            return mHeightMode;
8632        }
8633
8634        /**
8635         * Returns the width that is currently relevant to the LayoutManager.
8636         *
8637         * <p>This value is usually equal to the laid out width of the {@link RecyclerView} but may
8638         * reflect the current {@link android.view.View.MeasureSpec} width if the
8639         * {@link LayoutManager} is using AutoMeasure and the RecyclerView is in the process of
8640         * measuring. The LayoutManager must always use this method to retrieve the width relevant
8641         * to it at any given time.
8642         *
8643         * @return Width in pixels
8644         */
8645        @Px
8646        public int getWidth() {
8647            return mWidth;
8648        }
8649
8650        /**
8651         * Returns the height that is currently relevant to the LayoutManager.
8652         *
8653         * <p>This value is usually equal to the laid out height of the {@link RecyclerView} but may
8654         * reflect the current {@link android.view.View.MeasureSpec} height 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 height relevant
8657         * to it at any given time.
8658         *
8659         * @return Height in pixels
8660         */
8661        @Px
8662        public int getHeight() {
8663            return mHeight;
8664        }
8665
8666        /**
8667         * Return the left padding of the parent RecyclerView
8668         *
8669         * @return Padding in pixels
8670         */
8671        @Px
8672        public int getPaddingLeft() {
8673            return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
8674        }
8675
8676        /**
8677         * Return the top padding of the parent RecyclerView
8678         *
8679         * @return Padding in pixels
8680         */
8681        @Px
8682        public int getPaddingTop() {
8683            return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
8684        }
8685
8686        /**
8687         * Return the right padding of the parent RecyclerView
8688         *
8689         * @return Padding in pixels
8690         */
8691        @Px
8692        public int getPaddingRight() {
8693            return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
8694        }
8695
8696        /**
8697         * Return the bottom padding of the parent RecyclerView
8698         *
8699         * @return Padding in pixels
8700         */
8701        @Px
8702        public int getPaddingBottom() {
8703            return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
8704        }
8705
8706        /**
8707         * Return the start padding of the parent RecyclerView
8708         *
8709         * @return Padding in pixels
8710         */
8711        @Px
8712        public int getPaddingStart() {
8713            return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;
8714        }
8715
8716        /**
8717         * Return the end padding of the parent RecyclerView
8718         *
8719         * @return Padding in pixels
8720         */
8721        @Px
8722        public int getPaddingEnd() {
8723            return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;
8724        }
8725
8726        /**
8727         * Returns true if the RecyclerView this LayoutManager is bound to has focus.
8728         *
8729         * @return True if the RecyclerView has focus, false otherwise.
8730         * @see View#isFocused()
8731         */
8732        public boolean isFocused() {
8733            return mRecyclerView != null && mRecyclerView.isFocused();
8734        }
8735
8736        /**
8737         * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
8738         *
8739         * @return true if the RecyclerView has or contains focus
8740         * @see View#hasFocus()
8741         */
8742        public boolean hasFocus() {
8743            return mRecyclerView != null && mRecyclerView.hasFocus();
8744        }
8745
8746        /**
8747         * Returns the item View which has or contains focus.
8748         *
8749         * @return A direct child of RecyclerView which has focus or contains the focused child.
8750         */
8751        @Nullable
8752        public View getFocusedChild() {
8753            if (mRecyclerView == null) {
8754                return null;
8755            }
8756            final View focused = mRecyclerView.getFocusedChild();
8757            if (focused == null || mChildHelper.isHidden(focused)) {
8758                return null;
8759            }
8760            return focused;
8761        }
8762
8763        /**
8764         * Returns the number of items in the adapter bound to the parent RecyclerView.
8765         * <p>
8766         * Note that this number is not necessarily equal to
8767         * {@link State#getItemCount() State#getItemCount()}. In methods where {@link State} is
8768         * available, you should use {@link State#getItemCount() State#getItemCount()} instead.
8769         * For more details, check the documentation for
8770         * {@link State#getItemCount() State#getItemCount()}.
8771         *
8772         * @return The number of items in the bound adapter
8773         * @see State#getItemCount()
8774         */
8775        public int getItemCount() {
8776            final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
8777            return a != null ? a.getItemCount() : 0;
8778        }
8779
8780        /**
8781         * Offset all child views attached to the parent RecyclerView by dx pixels along
8782         * the horizontal axis.
8783         *
8784         * @param dx Pixels to offset by
8785         */
8786        public void offsetChildrenHorizontal(@Px int dx) {
8787            if (mRecyclerView != null) {
8788                mRecyclerView.offsetChildrenHorizontal(dx);
8789            }
8790        }
8791
8792        /**
8793         * Offset all child views attached to the parent RecyclerView by dy pixels along
8794         * the vertical axis.
8795         *
8796         * @param dy Pixels to offset by
8797         */
8798        public void offsetChildrenVertical(@Px int dy) {
8799            if (mRecyclerView != null) {
8800                mRecyclerView.offsetChildrenVertical(dy);
8801            }
8802        }
8803
8804        /**
8805         * Flags a view so that it will not be scrapped or recycled.
8806         * <p>
8807         * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
8808         * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
8809         * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
8810         * ignore the child.
8811         * <p>
8812         * Before this child can be recycled again, you have to call
8813         * {@link #stopIgnoringView(View)}.
8814         * <p>
8815         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8816         *
8817         * @param view View to ignore.
8818         * @see #stopIgnoringView(View)
8819         */
8820        public void ignoreView(@NonNull View view) {
8821            if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
8822                // checking this because calling this method on a recycled or detached view may
8823                // cause loss of state.
8824                throw new IllegalArgumentException("View should be fully attached to be ignored"
8825                        + mRecyclerView.exceptionLabel());
8826            }
8827            final ViewHolder vh = getChildViewHolderInt(view);
8828            vh.addFlags(ViewHolder.FLAG_IGNORE);
8829            mRecyclerView.mViewInfoStore.removeViewHolder(vh);
8830        }
8831
8832        /**
8833         * View can be scrapped and recycled again.
8834         * <p>
8835         * Note that calling this method removes all information in the view holder.
8836         * <p>
8837         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8838         *
8839         * @param view View to ignore.
8840         */
8841        public void stopIgnoringView(@NonNull View view) {
8842            final ViewHolder vh = getChildViewHolderInt(view);
8843            vh.stopIgnoring();
8844            vh.resetInternal();
8845            vh.addFlags(ViewHolder.FLAG_INVALID);
8846        }
8847
8848        /**
8849         * Temporarily detach and scrap all currently attached child views. Views will be scrapped
8850         * into the given Recycler. The Recycler may prefer to reuse scrap views before
8851         * other views that were previously recycled.
8852         *
8853         * @param recycler Recycler to scrap views into
8854         */
8855        public void detachAndScrapAttachedViews(@NonNull Recycler recycler) {
8856            final int childCount = getChildCount();
8857            for (int i = childCount - 1; i >= 0; i--) {
8858                final View v = getChildAt(i);
8859                scrapOrRecycleView(recycler, i, v);
8860            }
8861        }
8862
8863        private void scrapOrRecycleView(Recycler recycler, int index, View view) {
8864            final ViewHolder viewHolder = getChildViewHolderInt(view);
8865            if (viewHolder.shouldIgnore()) {
8866                if (DEBUG) {
8867                    Log.d(TAG, "ignoring view " + viewHolder);
8868                }
8869                return;
8870            }
8871            if (viewHolder.isInvalid() && !viewHolder.isRemoved()
8872                    && !mRecyclerView.mAdapter.hasStableIds()) {
8873                removeViewAt(index);
8874                recycler.recycleViewHolderInternal(viewHolder);
8875            } else {
8876                detachViewAt(index);
8877                recycler.scrapView(view);
8878                mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
8879            }
8880        }
8881
8882        /**
8883         * Recycles the scrapped views.
8884         * <p>
8885         * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
8886         * the expected behavior if scrapped views are used for animations. Otherwise, we need to
8887         * call remove and invalidate RecyclerView to ensure UI update.
8888         *
8889         * @param recycler Recycler
8890         */
8891        void removeAndRecycleScrapInt(Recycler recycler) {
8892            final int scrapCount = recycler.getScrapCount();
8893            // Loop backward, recycler might be changed by removeDetachedView()
8894            for (int i = scrapCount - 1; i >= 0; i--) {
8895                final View scrap = recycler.getScrapViewAt(i);
8896                final ViewHolder vh = getChildViewHolderInt(scrap);
8897                if (vh.shouldIgnore()) {
8898                    continue;
8899                }
8900                // If the scrap view is animating, we need to cancel them first. If we cancel it
8901                // here, ItemAnimator callback may recycle it which will cause double recycling.
8902                // To avoid this, we mark it as not recycleable before calling the item animator.
8903                // Since removeDetachedView calls a user API, a common mistake (ending animations on
8904                // the view) may recycle it too, so we guard it before we call user APIs.
8905                vh.setIsRecyclable(false);
8906                if (vh.isTmpDetached()) {
8907                    mRecyclerView.removeDetachedView(scrap, false);
8908                }
8909                if (mRecyclerView.mItemAnimator != null) {
8910                    mRecyclerView.mItemAnimator.endAnimation(vh);
8911                }
8912                vh.setIsRecyclable(true);
8913                recycler.quickRecycleScrapView(scrap);
8914            }
8915            recycler.clearScrap();
8916            if (scrapCount > 0) {
8917                mRecyclerView.invalidate();
8918            }
8919        }
8920
8921
8922        /**
8923         * Measure a child view using standard measurement policy, taking the padding
8924         * of the parent RecyclerView and any added item decorations into account.
8925         *
8926         * <p>If the RecyclerView can be scrolled in either dimension the caller may
8927         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8928         *
8929         * @param child Child view to measure
8930         * @param widthUsed Width in pixels currently consumed by other views, if relevant
8931         * @param heightUsed Height in pixels currently consumed by other views, if relevant
8932         */
8933        public void measureChild(@NonNull View child, int widthUsed, int heightUsed) {
8934            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8935
8936            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8937            widthUsed += insets.left + insets.right;
8938            heightUsed += insets.top + insets.bottom;
8939            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8940                    getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
8941                    canScrollHorizontally());
8942            final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8943                    getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
8944                    canScrollVertically());
8945            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8946                child.measure(widthSpec, heightSpec);
8947            }
8948        }
8949
8950        /**
8951         * RecyclerView internally does its own View measurement caching which should help with
8952         * WRAP_CONTENT.
8953         * <p>
8954         * Use this method if the View is already measured once in this layout pass.
8955         */
8956        boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8957            return !mMeasurementCacheEnabled
8958                    || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
8959                    || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
8960        }
8961
8962        // we may consider making this public
8963        /**
8964         * RecyclerView internally does its own View measurement caching which should help with
8965         * WRAP_CONTENT.
8966         * <p>
8967         * Use this method if the View is not yet measured and you need to decide whether to
8968         * measure this View or not.
8969         */
8970        boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8971            return child.isLayoutRequested()
8972                    || !mMeasurementCacheEnabled
8973                    || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
8974                    || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
8975        }
8976
8977        /**
8978         * In addition to the View Framework's measurement cache, RecyclerView uses its own
8979         * additional measurement cache for its children to avoid re-measuring them when not
8980         * necessary. It is on by default but it can be turned off via
8981         * {@link #setMeasurementCacheEnabled(boolean)}.
8982         *
8983         * @return True if measurement cache is enabled, false otherwise.
8984         *
8985         * @see #setMeasurementCacheEnabled(boolean)
8986         */
8987        public boolean isMeasurementCacheEnabled() {
8988            return mMeasurementCacheEnabled;
8989        }
8990
8991        /**
8992         * Sets whether RecyclerView should use its own measurement cache for the children. This is
8993         * a more aggressive cache than the framework uses.
8994         *
8995         * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
8996         *
8997         * @see #isMeasurementCacheEnabled()
8998         */
8999        public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
9000            mMeasurementCacheEnabled = measurementCacheEnabled;
9001        }
9002
9003        private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
9004            final int specMode = MeasureSpec.getMode(spec);
9005            final int specSize = MeasureSpec.getSize(spec);
9006            if (dimension > 0 && childSize != dimension) {
9007                return false;
9008            }
9009            switch (specMode) {
9010                case MeasureSpec.UNSPECIFIED:
9011                    return true;
9012                case MeasureSpec.AT_MOST:
9013                    return specSize >= childSize;
9014                case MeasureSpec.EXACTLY:
9015                    return  specSize == childSize;
9016            }
9017            return false;
9018        }
9019
9020        /**
9021         * Measure a child view using standard measurement policy, taking the padding
9022         * of the parent RecyclerView, any added item decorations and the child margins
9023         * into account.
9024         *
9025         * <p>If the RecyclerView can be scrolled in either dimension the caller may
9026         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
9027         *
9028         * @param child Child view to measure
9029         * @param widthUsed Width in pixels currently consumed by other views, if relevant
9030         * @param heightUsed Height in pixels currently consumed by other views, if relevant
9031         */
9032        public void measureChildWithMargins(@NonNull View child, int widthUsed, int heightUsed) {
9033            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
9034
9035            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
9036            widthUsed += insets.left + insets.right;
9037            heightUsed += insets.top + insets.bottom;
9038
9039            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
9040                    getPaddingLeft() + getPaddingRight()
9041                            + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
9042                    canScrollHorizontally());
9043            final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
9044                    getPaddingTop() + getPaddingBottom()
9045                            + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
9046                    canScrollVertically());
9047            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
9048                child.measure(widthSpec, heightSpec);
9049            }
9050        }
9051
9052        /**
9053         * Calculate a MeasureSpec value for measuring a child view in one dimension.
9054         *
9055         * @param parentSize Size of the parent view where the child will be placed
9056         * @param padding Total space currently consumed by other elements of the parent
9057         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
9058         *                       Generally obtained from the child view's LayoutParams
9059         * @param canScroll true if the parent RecyclerView can scroll in this dimension
9060         *
9061         * @return a MeasureSpec value for the child view
9062         * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
9063         */
9064        @Deprecated
9065        public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
9066                boolean canScroll) {
9067            int size = Math.max(0, parentSize - padding);
9068            int resultSize = 0;
9069            int resultMode = 0;
9070            if (canScroll) {
9071                if (childDimension >= 0) {
9072                    resultSize = childDimension;
9073                    resultMode = MeasureSpec.EXACTLY;
9074                } else {
9075                    // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
9076                    // instead using UNSPECIFIED.
9077                    resultSize = 0;
9078                    resultMode = MeasureSpec.UNSPECIFIED;
9079                }
9080            } else {
9081                if (childDimension >= 0) {
9082                    resultSize = childDimension;
9083                    resultMode = MeasureSpec.EXACTLY;
9084                } else if (childDimension == LayoutParams.MATCH_PARENT) {
9085                    resultSize = size;
9086                    // TODO this should be my spec.
9087                    resultMode = MeasureSpec.EXACTLY;
9088                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
9089                    resultSize = size;
9090                    resultMode = MeasureSpec.AT_MOST;
9091                }
9092            }
9093            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
9094        }
9095
9096        /**
9097         * Calculate a MeasureSpec value for measuring a child view in one dimension.
9098         *
9099         * @param parentSize Size of the parent view where the child will be placed
9100         * @param parentMode The measurement spec mode of the parent
9101         * @param padding Total space currently consumed by other elements of parent
9102         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
9103         *                       Generally obtained from the child view's LayoutParams
9104         * @param canScroll true if the parent RecyclerView can scroll in this dimension
9105         *
9106         * @return a MeasureSpec value for the child view
9107         */
9108        public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
9109                int childDimension, boolean canScroll) {
9110            int size = Math.max(0, parentSize - padding);
9111            int resultSize = 0;
9112            int resultMode = 0;
9113            if (canScroll) {
9114                if (childDimension >= 0) {
9115                    resultSize = childDimension;
9116                    resultMode = MeasureSpec.EXACTLY;
9117                } else if (childDimension == LayoutParams.MATCH_PARENT) {
9118                    switch (parentMode) {
9119                        case MeasureSpec.AT_MOST:
9120                        case MeasureSpec.EXACTLY:
9121                            resultSize = size;
9122                            resultMode = parentMode;
9123                            break;
9124                        case MeasureSpec.UNSPECIFIED:
9125                            resultSize = 0;
9126                            resultMode = MeasureSpec.UNSPECIFIED;
9127                            break;
9128                    }
9129                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
9130                    resultSize = 0;
9131                    resultMode = MeasureSpec.UNSPECIFIED;
9132                }
9133            } else {
9134                if (childDimension >= 0) {
9135                    resultSize = childDimension;
9136                    resultMode = MeasureSpec.EXACTLY;
9137                } else if (childDimension == LayoutParams.MATCH_PARENT) {
9138                    resultSize = size;
9139                    resultMode = parentMode;
9140                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
9141                    resultSize = size;
9142                    if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
9143                        resultMode = MeasureSpec.AT_MOST;
9144                    } else {
9145                        resultMode = MeasureSpec.UNSPECIFIED;
9146                    }
9147
9148                }
9149            }
9150            //noinspection WrongConstant
9151            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
9152        }
9153
9154        /**
9155         * Returns the measured width of the given child, plus the additional size of
9156         * any insets applied by {@link ItemDecoration ItemDecorations}.
9157         *
9158         * @param child Child view to query
9159         * @return child's measured width plus <code>ItemDecoration</code> insets
9160         *
9161         * @see View#getMeasuredWidth()
9162         */
9163        public int getDecoratedMeasuredWidth(@NonNull View child) {
9164            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9165            return child.getMeasuredWidth() + insets.left + insets.right;
9166        }
9167
9168        /**
9169         * Returns the measured height of the given child, plus the additional size of
9170         * any insets applied by {@link ItemDecoration ItemDecorations}.
9171         *
9172         * @param child Child view to query
9173         * @return child's measured height plus <code>ItemDecoration</code> insets
9174         *
9175         * @see View#getMeasuredHeight()
9176         */
9177        public int getDecoratedMeasuredHeight(@NonNull View child) {
9178            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9179            return child.getMeasuredHeight() + insets.top + insets.bottom;
9180        }
9181
9182        /**
9183         * Lay out the given child view within the RecyclerView using coordinates that
9184         * include any current {@link ItemDecoration ItemDecorations}.
9185         *
9186         * <p>LayoutManagers should prefer working in sizes and coordinates that include
9187         * item decoration insets whenever possible. This allows the LayoutManager to effectively
9188         * ignore decoration insets within measurement and layout code. See the following
9189         * methods:</p>
9190         * <ul>
9191         *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
9192         *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
9193         *     <li>{@link #measureChild(View, int, int)}</li>
9194         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
9195         *     <li>{@link #getDecoratedLeft(View)}</li>
9196         *     <li>{@link #getDecoratedTop(View)}</li>
9197         *     <li>{@link #getDecoratedRight(View)}</li>
9198         *     <li>{@link #getDecoratedBottom(View)}</li>
9199         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
9200         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
9201         * </ul>
9202         *
9203         * @param child Child to lay out
9204         * @param left Left edge, with item decoration insets included
9205         * @param top Top edge, with item decoration insets included
9206         * @param right Right edge, with item decoration insets included
9207         * @param bottom Bottom edge, with item decoration insets included
9208         *
9209         * @see View#layout(int, int, int, int)
9210         * @see #layoutDecoratedWithMargins(View, int, int, int, int)
9211         */
9212        public void layoutDecorated(@NonNull View child, int left, int top, int right, int bottom) {
9213            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9214            child.layout(left + insets.left, top + insets.top, right - insets.right,
9215                    bottom - insets.bottom);
9216        }
9217
9218        /**
9219         * Lay out the given child view within the RecyclerView using coordinates that
9220         * include any current {@link ItemDecoration ItemDecorations} and margins.
9221         *
9222         * <p>LayoutManagers should prefer working in sizes and coordinates that include
9223         * item decoration insets whenever possible. This allows the LayoutManager to effectively
9224         * ignore decoration insets within measurement and layout code. See the following
9225         * methods:</p>
9226         * <ul>
9227         *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
9228         *     <li>{@link #measureChild(View, int, int)}</li>
9229         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
9230         *     <li>{@link #getDecoratedLeft(View)}</li>
9231         *     <li>{@link #getDecoratedTop(View)}</li>
9232         *     <li>{@link #getDecoratedRight(View)}</li>
9233         *     <li>{@link #getDecoratedBottom(View)}</li>
9234         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
9235         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
9236         * </ul>
9237         *
9238         * @param child Child to lay out
9239         * @param left Left edge, with item decoration insets and left margin included
9240         * @param top Top edge, with item decoration insets and top margin included
9241         * @param right Right edge, with item decoration insets and right margin included
9242         * @param bottom Bottom edge, with item decoration insets and bottom margin included
9243         *
9244         * @see View#layout(int, int, int, int)
9245         * @see #layoutDecorated(View, int, int, int, int)
9246         */
9247        public void layoutDecoratedWithMargins(@NonNull View child, int left, int top, int right,
9248                int bottom) {
9249            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
9250            final Rect insets = lp.mDecorInsets;
9251            child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
9252                    right - insets.right - lp.rightMargin,
9253                    bottom - insets.bottom - lp.bottomMargin);
9254        }
9255
9256        /**
9257         * Calculates the bounding box of the View while taking into account its matrix changes
9258         * (translation, scale etc) with respect to the RecyclerView.
9259         * <p>
9260         * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
9261         * the View's matrix so that the decor offsets also go through the same transformation.
9262         *
9263         * @param child The ItemView whose bounding box should be calculated.
9264         * @param includeDecorInsets True if the decor insets should be included in the bounding box
9265         * @param out The rectangle into which the output will be written.
9266         */
9267        public void getTransformedBoundingBox(@NonNull View child, boolean includeDecorInsets,
9268                @NonNull Rect out) {
9269            if (includeDecorInsets) {
9270                Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9271                out.set(-insets.left, -insets.top,
9272                        child.getWidth() + insets.right, child.getHeight() + insets.bottom);
9273            } else {
9274                out.set(0, 0, child.getWidth(), child.getHeight());
9275            }
9276
9277            if (mRecyclerView != null) {
9278                final Matrix childMatrix = child.getMatrix();
9279                if (childMatrix != null && !childMatrix.isIdentity()) {
9280                    final RectF tempRectF = mRecyclerView.mTempRectF;
9281                    tempRectF.set(out);
9282                    childMatrix.mapRect(tempRectF);
9283                    out.set(
9284                            (int) Math.floor(tempRectF.left),
9285                            (int) Math.floor(tempRectF.top),
9286                            (int) Math.ceil(tempRectF.right),
9287                            (int) Math.ceil(tempRectF.bottom)
9288                    );
9289                }
9290            }
9291            out.offset(child.getLeft(), child.getTop());
9292        }
9293
9294        /**
9295         * Returns the bounds of the view including its decoration and margins.
9296         *
9297         * @param view The view element to check
9298         * @param outBounds A rect that will receive the bounds of the element including its
9299         *                  decoration and margins.
9300         */
9301        public void getDecoratedBoundsWithMargins(@NonNull View view, @NonNull Rect outBounds) {
9302            RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
9303        }
9304
9305        /**
9306         * Returns the left edge of the given child view within its parent, offset by any applied
9307         * {@link ItemDecoration ItemDecorations}.
9308         *
9309         * @param child Child to query
9310         * @return Child left edge with offsets applied
9311         * @see #getLeftDecorationWidth(View)
9312         */
9313        public int getDecoratedLeft(@NonNull View child) {
9314            return child.getLeft() - getLeftDecorationWidth(child);
9315        }
9316
9317        /**
9318         * Returns the top edge of the given child view within its parent, offset by any applied
9319         * {@link ItemDecoration ItemDecorations}.
9320         *
9321         * @param child Child to query
9322         * @return Child top edge with offsets applied
9323         * @see #getTopDecorationHeight(View)
9324         */
9325        public int getDecoratedTop(@NonNull View child) {
9326            return child.getTop() - getTopDecorationHeight(child);
9327        }
9328
9329        /**
9330         * Returns the right edge of the given child view within its parent, offset by any applied
9331         * {@link ItemDecoration ItemDecorations}.
9332         *
9333         * @param child Child to query
9334         * @return Child right edge with offsets applied
9335         * @see #getRightDecorationWidth(View)
9336         */
9337        public int getDecoratedRight(@NonNull View child) {
9338            return child.getRight() + getRightDecorationWidth(child);
9339        }
9340
9341        /**
9342         * Returns the bottom edge of the given child view within its parent, offset by any applied
9343         * {@link ItemDecoration ItemDecorations}.
9344         *
9345         * @param child Child to query
9346         * @return Child bottom edge with offsets applied
9347         * @see #getBottomDecorationHeight(View)
9348         */
9349        public int getDecoratedBottom(@NonNull View child) {
9350            return child.getBottom() + getBottomDecorationHeight(child);
9351        }
9352
9353        /**
9354         * Calculates the item decor insets applied to the given child and updates the provided
9355         * Rect instance with the inset values.
9356         * <ul>
9357         *     <li>The Rect's left is set to the total width of left decorations.</li>
9358         *     <li>The Rect's top is set to the total height of top decorations.</li>
9359         *     <li>The Rect's right is set to the total width of right decorations.</li>
9360         *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
9361         * </ul>
9362         * <p>
9363         * Note that item decorations are automatically calculated when one of the LayoutManager's
9364         * measure child methods is called. If you need to measure the child with custom specs via
9365         * {@link View#measure(int, int)}, you can use this method to get decorations.
9366         *
9367         * @param child The child view whose decorations should be calculated
9368         * @param outRect The Rect to hold result values
9369         */
9370        public void calculateItemDecorationsForChild(@NonNull View child, @NonNull Rect outRect) {
9371            if (mRecyclerView == null) {
9372                outRect.set(0, 0, 0, 0);
9373                return;
9374            }
9375            Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
9376            outRect.set(insets);
9377        }
9378
9379        /**
9380         * Returns the total height of item decorations applied to child's top.
9381         * <p>
9382         * Note that this value is not updated until the View is measured or
9383         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9384         *
9385         * @param child Child to query
9386         * @return The total height of item decorations applied to the child's top.
9387         * @see #getDecoratedTop(View)
9388         * @see #calculateItemDecorationsForChild(View, Rect)
9389         */
9390        public int getTopDecorationHeight(@NonNull View child) {
9391            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
9392        }
9393
9394        /**
9395         * Returns the total height of item decorations applied to child's bottom.
9396         * <p>
9397         * Note that this value is not updated until the View is measured or
9398         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9399         *
9400         * @param child Child to query
9401         * @return The total height of item decorations applied to the child's bottom.
9402         * @see #getDecoratedBottom(View)
9403         * @see #calculateItemDecorationsForChild(View, Rect)
9404         */
9405        public int getBottomDecorationHeight(@NonNull View child) {
9406            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
9407        }
9408
9409        /**
9410         * Returns the total width of item decorations applied to child's left.
9411         * <p>
9412         * Note that this value is not updated until the View is measured or
9413         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9414         *
9415         * @param child Child to query
9416         * @return The total width of item decorations applied to the child's left.
9417         * @see #getDecoratedLeft(View)
9418         * @see #calculateItemDecorationsForChild(View, Rect)
9419         */
9420        public int getLeftDecorationWidth(@NonNull View child) {
9421            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
9422        }
9423
9424        /**
9425         * Returns the total width of item decorations applied to child's right.
9426         * <p>
9427         * Note that this value is not updated until the View is measured or
9428         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9429         *
9430         * @param child Child to query
9431         * @return The total width of item decorations applied to the child's right.
9432         * @see #getDecoratedRight(View)
9433         * @see #calculateItemDecorationsForChild(View, Rect)
9434         */
9435        public int getRightDecorationWidth(@NonNull View child) {
9436            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
9437        }
9438
9439        /**
9440         * Called when searching for a focusable view in the given direction has failed
9441         * for the current content of the RecyclerView.
9442         *
9443         * <p>This is the LayoutManager's opportunity to populate views in the given direction
9444         * to fulfill the request if it can. The LayoutManager should attach and return
9445         * the view to be focused, if a focusable view in the given direction is found.
9446         * Otherwise, if all the existing (or the newly populated views) are unfocusable, it returns
9447         * the next unfocusable view to become visible on the screen. This unfocusable view is
9448         * typically the first view that's either partially or fully out of RV's padded bounded
9449         * area in the given direction. The default implementation returns null.</p>
9450         *
9451         * @param focused   The currently focused view
9452         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9453         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9454         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9455         *                  or 0 for not applicable
9456         * @param recycler  The recycler to use for obtaining views for currently offscreen items
9457         * @param state     Transient state of RecyclerView
9458         * @return The chosen view to be focused if a focusable view is found, otherwise an
9459         * unfocusable view to become visible onto the screen, else null.
9460         */
9461        @Nullable
9462        public View onFocusSearchFailed(@NonNull View focused, int direction,
9463                @NonNull Recycler recycler, @NonNull State state) {
9464            return null;
9465        }
9466
9467        /**
9468         * This method gives a LayoutManager an opportunity to intercept the initial focus search
9469         * before the default behavior of {@link FocusFinder} is used. If this method returns
9470         * null FocusFinder will attempt to find a focusable child view. If it fails
9471         * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
9472         * will be called to give the LayoutManager an opportunity to add new views for items
9473         * that did not have attached views representing them. The LayoutManager should not add
9474         * or remove views from this method.
9475         *
9476         * @param focused The currently focused view
9477         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9478         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9479         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9480         * @return A descendant view to focus or null to fall back to default behavior.
9481         *         The default implementation returns null.
9482         */
9483        @Nullable
9484        public View onInterceptFocusSearch(@NonNull View focused, int direction) {
9485            return null;
9486        }
9487
9488        /**
9489         * Returns the scroll amount that brings the given rect in child's coordinate system within
9490         * the padded area of RecyclerView.
9491         * @param parent The parent RecyclerView.
9492         * @param child The direct child making the request.
9493         * @param rect The rectangle in the child's coordinates the child
9494         *             wishes to be on the screen.
9495         * @param immediate True to forbid animated or delayed scrolling,
9496         *                  false otherwise
9497         * @return The array containing the scroll amount in x and y directions that brings the
9498         * given rect into RV's padded area.
9499         */
9500        private int[] getChildRectangleOnScreenScrollAmount(RecyclerView parent, View child,
9501                Rect rect, boolean immediate) {
9502            int[] out = new int[2];
9503            final int parentLeft = getPaddingLeft();
9504            final int parentTop = getPaddingTop();
9505            final int parentRight = getWidth() - getPaddingRight();
9506            final int parentBottom = getHeight() - getPaddingBottom();
9507            final int childLeft = child.getLeft() + rect.left - child.getScrollX();
9508            final int childTop = child.getTop() + rect.top - child.getScrollY();
9509            final int childRight = childLeft + rect.width();
9510            final int childBottom = childTop + rect.height();
9511
9512            final int offScreenLeft = Math.min(0, childLeft - parentLeft);
9513            final int offScreenTop = Math.min(0, childTop - parentTop);
9514            final int offScreenRight = Math.max(0, childRight - parentRight);
9515            final int offScreenBottom = Math.max(0, childBottom - parentBottom);
9516
9517            // Favor the "start" layout direction over the end when bringing one side or the other
9518            // of a large rect into view. If we decide to bring in end because start is already
9519            // visible, limit the scroll such that start won't go out of bounds.
9520            final int dx;
9521            if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) {
9522                dx = offScreenRight != 0 ? offScreenRight
9523                        : Math.max(offScreenLeft, childRight - parentRight);
9524            } else {
9525                dx = offScreenLeft != 0 ? offScreenLeft
9526                        : Math.min(childLeft - parentLeft, offScreenRight);
9527            }
9528
9529            // Favor bringing the top into view over the bottom. If top is already visible and
9530            // we should scroll to make bottom visible, make sure top does not go out of bounds.
9531            final int dy = offScreenTop != 0 ? offScreenTop
9532                    : Math.min(childTop - parentTop, offScreenBottom);
9533            out[0] = dx;
9534            out[1] = dy;
9535            return out;
9536        }
9537        /**
9538         * Called when a child of the RecyclerView wants a particular rectangle to be positioned
9539         * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
9540         * android.graphics.Rect, boolean)} for more details.
9541         *
9542         * <p>The base implementation will attempt to perform a standard programmatic scroll
9543         * to bring the given rect into view, within the padded area of the RecyclerView.</p>
9544         *
9545         * @param child The direct child making the request.
9546         * @param rect  The rectangle in the child's coordinates the child
9547         *              wishes to be on the screen.
9548         * @param immediate True to forbid animated or delayed scrolling,
9549         *                  false otherwise
9550         * @return Whether the group scrolled to handle the operation
9551         */
9552        public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
9553                @NonNull View child, @NonNull Rect rect, boolean immediate) {
9554            return requestChildRectangleOnScreen(parent, child, rect, immediate, false);
9555        }
9556
9557        /**
9558         * Requests that the given child of the RecyclerView be positioned onto the screen. This
9559         * method can be called for both unfocusable and focusable child views. For unfocusable
9560         * child views, focusedChildVisible is typically true in which case, layout manager
9561         * makes the child view visible only if the currently focused child stays in-bounds of RV.
9562         * @param parent The parent RecyclerView.
9563         * @param child The direct child making the request.
9564         * @param rect The rectangle in the child's coordinates the child
9565         *              wishes to be on the screen.
9566         * @param immediate True to forbid animated or delayed scrolling,
9567         *                  false otherwise
9568         * @param focusedChildVisible Whether the currently focused view must stay visible.
9569         * @return Whether the group scrolled to handle the operation
9570         */
9571        public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
9572                @NonNull View child, @NonNull Rect rect, boolean immediate,
9573                boolean focusedChildVisible) {
9574            int[] scrollAmount = getChildRectangleOnScreenScrollAmount(parent, child, rect,
9575                    immediate);
9576            int dx = scrollAmount[0];
9577            int dy = scrollAmount[1];
9578            if (!focusedChildVisible || isFocusedChildVisibleAfterScrolling(parent, dx, dy)) {
9579                if (dx != 0 || dy != 0) {
9580                    if (immediate) {
9581                        parent.scrollBy(dx, dy);
9582                    } else {
9583                        parent.smoothScrollBy(dx, dy);
9584                    }
9585                    return true;
9586                }
9587            }
9588            return false;
9589        }
9590
9591        /**
9592         * Returns whether the given child view is partially or fully visible within the padded
9593         * bounded area of RecyclerView, depending on the input parameters.
9594         * A view is partially visible if it has non-zero overlap with RV's padded bounded area.
9595         * If acceptEndPointInclusion flag is set to true, it's also considered partially
9596         * visible if it's located outside RV's bounds and it's hitting either RV's start or end
9597         * bounds.
9598         *
9599         * @param child The child view to be examined.
9600         * @param completelyVisible If true, the method returns true if and only if the child is
9601         *                          completely visible. If false, the method returns true if and
9602         *                          only if the child is only partially visible (that is it will
9603         *                          return false if the child is either completely visible or out
9604         *                          of RV's bounds).
9605         * @param acceptEndPointInclusion If the view's endpoint intersection with RV's start of end
9606         *                                bounds is enough to consider it partially visible,
9607         *                                false otherwise.
9608         * @return True if the given child is partially or fully visible, false otherwise.
9609         */
9610        public boolean isViewPartiallyVisible(@NonNull View child, boolean completelyVisible,
9611                boolean acceptEndPointInclusion) {
9612            int boundsFlag = (ViewBoundsCheck.FLAG_CVS_GT_PVS | ViewBoundsCheck.FLAG_CVS_EQ_PVS
9613                    | ViewBoundsCheck.FLAG_CVE_LT_PVE | ViewBoundsCheck.FLAG_CVE_EQ_PVE);
9614            boolean isViewFullyVisible = mHorizontalBoundCheck.isViewWithinBoundFlags(child,
9615                    boundsFlag)
9616                    && mVerticalBoundCheck.isViewWithinBoundFlags(child, boundsFlag);
9617            if (completelyVisible) {
9618                return isViewFullyVisible;
9619            } else {
9620                return !isViewFullyVisible;
9621            }
9622        }
9623
9624        /**
9625         * Returns whether the currently focused child stays within RV's bounds with the given
9626         * amount of scrolling.
9627         * @param parent The parent RecyclerView.
9628         * @param dx The scrolling in x-axis direction to be performed.
9629         * @param dy The scrolling in y-axis direction to be performed.
9630         * @return {@code false} if the focused child is not at least partially visible after
9631         *         scrolling or no focused child exists, {@code true} otherwise.
9632         */
9633        private boolean isFocusedChildVisibleAfterScrolling(RecyclerView parent, int dx, int dy) {
9634            final View focusedChild = parent.getFocusedChild();
9635            if (focusedChild == null) {
9636                return false;
9637            }
9638            final int parentLeft = getPaddingLeft();
9639            final int parentTop = getPaddingTop();
9640            final int parentRight = getWidth() - getPaddingRight();
9641            final int parentBottom = getHeight() - getPaddingBottom();
9642            final Rect bounds = mRecyclerView.mTempRect;
9643            getDecoratedBoundsWithMargins(focusedChild, bounds);
9644
9645            if (bounds.left - dx >= parentRight || bounds.right - dx <= parentLeft
9646                    || bounds.top - dy >= parentBottom || bounds.bottom - dy <= parentTop) {
9647                return false;
9648            }
9649            return true;
9650        }
9651
9652        /**
9653         * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
9654         */
9655        @Deprecated
9656        public boolean onRequestChildFocus(@NonNull RecyclerView parent, @NonNull View child,
9657                @Nullable View focused) {
9658            // eat the request if we are in the middle of a scroll or layout
9659            return isSmoothScrolling() || parent.isComputingLayout();
9660        }
9661
9662        /**
9663         * Called when a descendant view of the RecyclerView requests focus.
9664         *
9665         * <p>A LayoutManager wishing to keep focused views aligned in a specific
9666         * portion of the view may implement that behavior in an override of this method.</p>
9667         *
9668         * <p>If the LayoutManager executes different behavior that should override the default
9669         * behavior of scrolling the focused child on screen instead of running alongside it,
9670         * this method should return true.</p>
9671         *
9672         * @param parent  The RecyclerView hosting this LayoutManager
9673         * @param state   Current state of RecyclerView
9674         * @param child   Direct child of the RecyclerView containing the newly focused view
9675         * @param focused The newly focused view. This may be the same view as child or it may be
9676         *                null
9677         * @return true if the default scroll behavior should be suppressed
9678         */
9679        public boolean onRequestChildFocus(@NonNull RecyclerView parent, @NonNull State state,
9680                @NonNull View child, @Nullable View focused) {
9681            return onRequestChildFocus(parent, child, focused);
9682        }
9683
9684        /**
9685         * Called if the RecyclerView this LayoutManager is bound to has a different adapter set via
9686         * {@link RecyclerView#setAdapter(Adapter)} or
9687         * {@link RecyclerView#swapAdapter(Adapter, boolean)}. The LayoutManager may use this
9688         * opportunity to clear caches and configure state such that it can relayout appropriately
9689         * with the new data and potentially new view types.
9690         *
9691         * <p>The default implementation removes all currently attached views.</p>
9692         *
9693         * @param oldAdapter The previous adapter instance. Will be null if there was previously no
9694         *                   adapter.
9695         * @param newAdapter The new adapter instance. Might be null if
9696         *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
9697         */
9698        public void onAdapterChanged(@Nullable Adapter oldAdapter, @Nullable Adapter newAdapter) {
9699        }
9700
9701        /**
9702         * Called to populate focusable views within the RecyclerView.
9703         *
9704         * <p>The LayoutManager implementation should return <code>true</code> if the default
9705         * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
9706         * suppressed.</p>
9707         *
9708         * <p>The default implementation returns <code>false</code> to trigger RecyclerView
9709         * to fall back to the default ViewGroup behavior.</p>
9710         *
9711         * @param recyclerView The RecyclerView hosting this LayoutManager
9712         * @param views List of output views. This method should add valid focusable views
9713         *              to this list.
9714         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9715         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9716         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9717         * @param focusableMode The type of focusables to be added.
9718         *
9719         * @return true to suppress the default behavior, false to add default focusables after
9720         *         this method returns.
9721         *
9722         * @see #FOCUSABLES_ALL
9723         * @see #FOCUSABLES_TOUCH_MODE
9724         */
9725        public boolean onAddFocusables(@NonNull RecyclerView recyclerView,
9726                @NonNull ArrayList<View> views, int direction, int focusableMode) {
9727            return false;
9728        }
9729
9730        /**
9731         * Called in response to a call to {@link Adapter#notifyDataSetChanged()} or
9732         * {@link RecyclerView#swapAdapter(Adapter, boolean)} ()} and signals that the the entire
9733         * data set has changed.
9734         *
9735         * @param recyclerView
9736         */
9737        public void onItemsChanged(@NonNull RecyclerView recyclerView) {
9738        }
9739
9740        /**
9741         * Called when items have been added to the adapter. The LayoutManager may choose to
9742         * requestLayout if the inserted items would require refreshing the currently visible set
9743         * of child views. (e.g. currently empty space would be filled by appended items, etc.)
9744         *
9745         * @param recyclerView
9746         * @param positionStart
9747         * @param itemCount
9748         */
9749        public void onItemsAdded(@NonNull RecyclerView recyclerView, int positionStart,
9750                int itemCount) {
9751        }
9752
9753        /**
9754         * Called when items have been removed from the adapter.
9755         *
9756         * @param recyclerView
9757         * @param positionStart
9758         * @param itemCount
9759         */
9760        public void onItemsRemoved(@NonNull RecyclerView recyclerView, int positionStart,
9761                int itemCount) {
9762        }
9763
9764        /**
9765         * Called when items have been changed in the adapter.
9766         * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
9767         * instead, then this callback will not be invoked.
9768         *
9769         * @param recyclerView
9770         * @param positionStart
9771         * @param itemCount
9772         */
9773        public void onItemsUpdated(@NonNull RecyclerView recyclerView, int positionStart,
9774                int itemCount) {
9775        }
9776
9777        /**
9778         * Called when items have been changed in the adapter and with optional payload.
9779         * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
9780         *
9781         * @param recyclerView
9782         * @param positionStart
9783         * @param itemCount
9784         * @param payload
9785         */
9786        public void onItemsUpdated(@NonNull RecyclerView recyclerView, int positionStart,
9787                int itemCount, @Nullable Object payload) {
9788            onItemsUpdated(recyclerView, positionStart, itemCount);
9789        }
9790
9791        /**
9792         * Called when an item is moved withing the adapter.
9793         * <p>
9794         * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
9795         * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
9796         * is called.
9797         *
9798         * @param recyclerView
9799         * @param from
9800         * @param to
9801         * @param itemCount
9802         */
9803        public void onItemsMoved(@NonNull RecyclerView recyclerView, int from, int to,
9804                int itemCount) {
9805
9806        }
9807
9808
9809        /**
9810         * <p>Override this method if you want to support scroll bars.</p>
9811         *
9812         * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
9813         *
9814         * <p>Default implementation returns 0.</p>
9815         *
9816         * @param state Current state of RecyclerView
9817         * @return The horizontal extent of the scrollbar's thumb
9818         * @see RecyclerView#computeHorizontalScrollExtent()
9819         */
9820        public int computeHorizontalScrollExtent(@NonNull State state) {
9821            return 0;
9822        }
9823
9824        /**
9825         * <p>Override this method if you want to support scroll bars.</p>
9826         *
9827         * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
9828         *
9829         * <p>Default implementation returns 0.</p>
9830         *
9831         * @param state Current State of RecyclerView where you can find total item count
9832         * @return The horizontal offset of the scrollbar's thumb
9833         * @see RecyclerView#computeHorizontalScrollOffset()
9834         */
9835        public int computeHorizontalScrollOffset(@NonNull State state) {
9836            return 0;
9837        }
9838
9839        /**
9840         * <p>Override this method if you want to support scroll bars.</p>
9841         *
9842         * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
9843         *
9844         * <p>Default implementation returns 0.</p>
9845         *
9846         * @param state Current State of RecyclerView where you can find total item count
9847         * @return The total horizontal range represented by the vertical scrollbar
9848         * @see RecyclerView#computeHorizontalScrollRange()
9849         */
9850        public int computeHorizontalScrollRange(@NonNull State state) {
9851            return 0;
9852        }
9853
9854        /**
9855         * <p>Override this method if you want to support scroll bars.</p>
9856         *
9857         * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
9858         *
9859         * <p>Default implementation returns 0.</p>
9860         *
9861         * @param state Current state of RecyclerView
9862         * @return The vertical extent of the scrollbar's thumb
9863         * @see RecyclerView#computeVerticalScrollExtent()
9864         */
9865        public int computeVerticalScrollExtent(@NonNull State state) {
9866            return 0;
9867        }
9868
9869        /**
9870         * <p>Override this method if you want to support scroll bars.</p>
9871         *
9872         * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
9873         *
9874         * <p>Default implementation returns 0.</p>
9875         *
9876         * @param state Current State of RecyclerView where you can find total item count
9877         * @return The vertical offset of the scrollbar's thumb
9878         * @see RecyclerView#computeVerticalScrollOffset()
9879         */
9880        public int computeVerticalScrollOffset(@NonNull State state) {
9881            return 0;
9882        }
9883
9884        /**
9885         * <p>Override this method if you want to support scroll bars.</p>
9886         *
9887         * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
9888         *
9889         * <p>Default implementation returns 0.</p>
9890         *
9891         * @param state Current State of RecyclerView where you can find total item count
9892         * @return The total vertical range represented by the vertical scrollbar
9893         * @see RecyclerView#computeVerticalScrollRange()
9894         */
9895        public int computeVerticalScrollRange(@NonNull State state) {
9896            return 0;
9897        }
9898
9899        /**
9900         * Measure the attached RecyclerView. Implementations must call
9901         * {@link #setMeasuredDimension(int, int)} before returning.
9902         * <p>
9903         * It is strongly advised to use the AutoMeasure mechanism by overriding
9904         * {@link #isAutoMeasureEnabled()} to return true as AutoMeasure handles all the standard
9905         * measure cases including when the RecyclerView's layout_width or layout_height have been
9906         * set to wrap_content.  If {@link #isAutoMeasureEnabled()} is overridden to return true,
9907         * this method should not be overridden.
9908         * <p>
9909         * The default implementation will handle EXACTLY measurements and respect
9910         * the minimum width and height properties of the host RecyclerView if measured
9911         * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
9912         * will consume all available space.
9913         *
9914         * @param recycler Recycler
9915         * @param state Transient state of RecyclerView
9916         * @param widthSpec Width {@link android.view.View.MeasureSpec}
9917         * @param heightSpec Height {@link android.view.View.MeasureSpec}
9918         *
9919         * @see #isAutoMeasureEnabled()
9920         * @see #setMeasuredDimension(int, int)
9921         */
9922        public void onMeasure(@NonNull Recycler recycler, @NonNull State state, int widthSpec,
9923                int heightSpec) {
9924            mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
9925        }
9926
9927        /**
9928         * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
9929         * host RecyclerView.
9930         *
9931         * @param widthSize Measured width
9932         * @param heightSize Measured height
9933         */
9934        public void setMeasuredDimension(int widthSize, int heightSize) {
9935            mRecyclerView.setMeasuredDimension(widthSize, heightSize);
9936        }
9937
9938        /**
9939         * @return The host RecyclerView's {@link View#getMinimumWidth()}
9940         */
9941        @Px
9942        public int getMinimumWidth() {
9943            return ViewCompat.getMinimumWidth(mRecyclerView);
9944        }
9945
9946        /**
9947         * @return The host RecyclerView's {@link View#getMinimumHeight()}
9948         */
9949        @Px
9950        public int getMinimumHeight() {
9951            return ViewCompat.getMinimumHeight(mRecyclerView);
9952        }
9953        /**
9954         * <p>Called when the LayoutManager should save its state. This is a good time to save your
9955         * scroll position, configuration and anything else that may be required to restore the same
9956         * layout state if the LayoutManager is recreated.</p>
9957         * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
9958         * restore. This will let you share information between your LayoutManagers but it is also
9959         * your responsibility to make sure they use the same parcelable class.</p>
9960         *
9961         * @return Necessary information for LayoutManager to be able to restore its state
9962         */
9963        @Nullable
9964        public Parcelable onSaveInstanceState() {
9965            return null;
9966        }
9967
9968
9969        public void onRestoreInstanceState(Parcelable state) {
9970
9971        }
9972
9973        void stopSmoothScroller() {
9974            if (mSmoothScroller != null) {
9975                mSmoothScroller.stop();
9976            }
9977        }
9978
9979        private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
9980            if (mSmoothScroller == smoothScroller) {
9981                mSmoothScroller = null;
9982            }
9983        }
9984
9985        /**
9986         * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
9987         *
9988         * @param state The new scroll state for RecyclerView
9989         */
9990        public void onScrollStateChanged(int state) {
9991        }
9992
9993        /**
9994         * Removes all views and recycles them using the given recycler.
9995         * <p>
9996         * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
9997         * <p>
9998         * If a View is marked as "ignored", it is not removed nor recycled.
9999         *
10000         * @param recycler Recycler to use to recycle children
10001         * @see #removeAndRecycleView(View, Recycler)
10002         * @see #removeAndRecycleViewAt(int, Recycler)
10003         * @see #ignoreView(View)
10004         */
10005        public void removeAndRecycleAllViews(@NonNull Recycler recycler) {
10006            for (int i = getChildCount() - 1; i >= 0; i--) {
10007                final View view = getChildAt(i);
10008                if (!getChildViewHolderInt(view).shouldIgnore()) {
10009                    removeAndRecycleViewAt(i, recycler);
10010                }
10011            }
10012        }
10013
10014        // called by accessibility delegate
10015        void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
10016            onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
10017        }
10018
10019        /**
10020         * Called by the AccessibilityDelegate when the information about the current layout should
10021         * be populated.
10022         * <p>
10023         * Default implementation adds a {@link
10024         * androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}.
10025         * <p>
10026         * You should override
10027         * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
10028         * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
10029         * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
10030         * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
10031         * more accurate accessibility information.
10032         *
10033         * @param recycler The Recycler that can be used to convert view positions into adapter
10034         *                 positions
10035         * @param state    The current state of RecyclerView
10036         * @param info     The info that should be filled by the LayoutManager
10037         * @see View#onInitializeAccessibilityNodeInfo(
10038         *android.view.accessibility.AccessibilityNodeInfo)
10039         * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
10040         * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
10041         * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
10042         * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
10043         */
10044        public void onInitializeAccessibilityNodeInfo(@NonNull Recycler recycler,
10045                @NonNull State state, @NonNull AccessibilityNodeInfoCompat info) {
10046            if (mRecyclerView.canScrollVertically(-1) || mRecyclerView.canScrollHorizontally(-1)) {
10047                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
10048                info.setScrollable(true);
10049            }
10050            if (mRecyclerView.canScrollVertically(1) || mRecyclerView.canScrollHorizontally(1)) {
10051                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
10052                info.setScrollable(true);
10053            }
10054            final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo =
10055                    AccessibilityNodeInfoCompat.CollectionInfoCompat
10056                            .obtain(getRowCountForAccessibility(recycler, state),
10057                                    getColumnCountForAccessibility(recycler, state),
10058                                    isLayoutHierarchical(recycler, state),
10059                                    getSelectionModeForAccessibility(recycler, state));
10060            info.setCollectionInfo(collectionInfo);
10061        }
10062
10063        // called by accessibility delegate
10064        public void onInitializeAccessibilityEvent(@NonNull AccessibilityEvent event) {
10065            onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
10066        }
10067
10068        /**
10069         * Called by the accessibility delegate to initialize an accessibility event.
10070         * <p>
10071         * Default implementation adds item count and scroll information to the event.
10072         *
10073         * @param recycler The Recycler that can be used to convert view positions into adapter
10074         *                 positions
10075         * @param state    The current state of RecyclerView
10076         * @param event    The event instance to initialize
10077         * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
10078         */
10079        public void onInitializeAccessibilityEvent(@NonNull Recycler recycler, @NonNull State state,
10080                @NonNull AccessibilityEvent event) {
10081            if (mRecyclerView == null || event == null) {
10082                return;
10083            }
10084            event.setScrollable(mRecyclerView.canScrollVertically(1)
10085                    || mRecyclerView.canScrollVertically(-1)
10086                    || mRecyclerView.canScrollHorizontally(-1)
10087                    || mRecyclerView.canScrollHorizontally(1));
10088
10089            if (mRecyclerView.mAdapter != null) {
10090                event.setItemCount(mRecyclerView.mAdapter.getItemCount());
10091            }
10092        }
10093
10094        // called by accessibility delegate
10095        void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) {
10096            final ViewHolder vh = getChildViewHolderInt(host);
10097            // avoid trying to create accessibility node info for removed children
10098            if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
10099                onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
10100                        mRecyclerView.mState, host, info);
10101            }
10102        }
10103
10104        /**
10105         * Called by the AccessibilityDelegate when the accessibility information for a specific
10106         * item should be populated.
10107         * <p>
10108         * Default implementation adds basic positioning information about the item.
10109         *
10110         * @param recycler The Recycler that can be used to convert view positions into adapter
10111         *                 positions
10112         * @param state    The current state of RecyclerView
10113         * @param host     The child for which accessibility node info should be populated
10114         * @param info     The info to fill out about the item
10115         * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
10116         * android.view.accessibility.AccessibilityNodeInfo)
10117         */
10118        public void onInitializeAccessibilityNodeInfoForItem(@NonNull Recycler recycler,
10119                @NonNull State state, @NonNull View host,
10120                @NonNull AccessibilityNodeInfoCompat info) {
10121            int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
10122            int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
10123            final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo =
10124                    AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
10125                            columnIndexGuess, 1, false, false);
10126            info.setCollectionItemInfo(itemInfo);
10127        }
10128
10129        /**
10130         * A LayoutManager can call this method to force RecyclerView to run simple animations in
10131         * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
10132         * change).
10133         * <p>
10134         * Note that, calling this method will not guarantee that RecyclerView will run animations
10135         * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
10136         * not run any animations but will still clear this flag after the layout is complete.
10137         *
10138         */
10139        public void requestSimpleAnimationsInNextLayout() {
10140            mRequestedSimpleAnimations = true;
10141        }
10142
10143        /**
10144         * Returns the selection mode for accessibility. Should be
10145         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE},
10146         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or
10147         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}.
10148         * <p>
10149         * Default implementation returns
10150         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
10151         *
10152         * @param recycler The Recycler that can be used to convert view positions into adapter
10153         *                 positions
10154         * @param state    The current state of RecyclerView
10155         * @return Selection mode for accessibility. Default implementation returns
10156         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
10157         */
10158        public int getSelectionModeForAccessibility(@NonNull Recycler recycler,
10159                @NonNull State state) {
10160            return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
10161        }
10162
10163        /**
10164         * Returns the number of rows for accessibility.
10165         * <p>
10166         * Default implementation returns the number of items in the adapter if LayoutManager
10167         * supports vertical scrolling or 1 if LayoutManager does not support vertical
10168         * scrolling.
10169         *
10170         * @param recycler The Recycler that can be used to convert view positions into adapter
10171         *                 positions
10172         * @param state    The current state of RecyclerView
10173         * @return The number of rows in LayoutManager for accessibility.
10174         */
10175        public int getRowCountForAccessibility(@NonNull Recycler recycler, @NonNull State state) {
10176            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
10177                return 1;
10178            }
10179            return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
10180        }
10181
10182        /**
10183         * Returns the number of columns for accessibility.
10184         * <p>
10185         * Default implementation returns the number of items in the adapter if LayoutManager
10186         * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
10187         * scrolling.
10188         *
10189         * @param recycler The Recycler that can be used to convert view positions into adapter
10190         *                 positions
10191         * @param state    The current state of RecyclerView
10192         * @return The number of rows in LayoutManager for accessibility.
10193         */
10194        public int getColumnCountForAccessibility(@NonNull Recycler recycler,
10195                @NonNull State state) {
10196            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
10197                return 1;
10198            }
10199            return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
10200        }
10201
10202        /**
10203         * Returns whether layout is hierarchical or not to be used for accessibility.
10204         * <p>
10205         * Default implementation returns false.
10206         *
10207         * @param recycler The Recycler that can be used to convert view positions into adapter
10208         *                 positions
10209         * @param state    The current state of RecyclerView
10210         * @return True if layout is hierarchical.
10211         */
10212        public boolean isLayoutHierarchical(@NonNull Recycler recycler, @NonNull State state) {
10213            return false;
10214        }
10215
10216        // called by accessibility delegate
10217        boolean performAccessibilityAction(int action, @Nullable Bundle args) {
10218            return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
10219                    action, args);
10220        }
10221
10222        /**
10223         * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
10224         *
10225         * @param recycler  The Recycler that can be used to convert view positions into adapter
10226         *                  positions
10227         * @param state     The current state of RecyclerView
10228         * @param action    The action to perform
10229         * @param args      Optional action arguments
10230         * @see View#performAccessibilityAction(int, android.os.Bundle)
10231         */
10232        public boolean performAccessibilityAction(@NonNull Recycler recycler, @NonNull State state,
10233                int action, @Nullable Bundle args) {
10234            if (mRecyclerView == null) {
10235                return false;
10236            }
10237            int vScroll = 0, hScroll = 0;
10238            switch (action) {
10239                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
10240                    if (mRecyclerView.canScrollVertically(-1)) {
10241                        vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
10242                    }
10243                    if (mRecyclerView.canScrollHorizontally(-1)) {
10244                        hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
10245                    }
10246                    break;
10247                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
10248                    if (mRecyclerView.canScrollVertically(1)) {
10249                        vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
10250                    }
10251                    if (mRecyclerView.canScrollHorizontally(1)) {
10252                        hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
10253                    }
10254                    break;
10255            }
10256            if (vScroll == 0 && hScroll == 0) {
10257                return false;
10258            }
10259            mRecyclerView.smoothScrollBy(hScroll, vScroll);
10260            return true;
10261        }
10262
10263        // called by accessibility delegate
10264        boolean performAccessibilityActionForItem(@NonNull View view, int action,
10265                @Nullable Bundle args) {
10266            return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
10267                    view, action, args);
10268        }
10269
10270        /**
10271         * Called by AccessibilityDelegate when an accessibility action is requested on one of the
10272         * children of LayoutManager.
10273         * <p>
10274         * Default implementation does not do anything.
10275         *
10276         * @param recycler The Recycler that can be used to convert view positions into adapter
10277         *                 positions
10278         * @param state    The current state of RecyclerView
10279         * @param view     The child view on which the action is performed
10280         * @param action   The action to perform
10281         * @param args     Optional action arguments
10282         * @return true if action is handled
10283         * @see View#performAccessibilityAction(int, android.os.Bundle)
10284         */
10285        public boolean performAccessibilityActionForItem(@NonNull Recycler recycler,
10286                @NonNull State state, @NonNull View view, int action, @Nullable Bundle args) {
10287            return false;
10288        }
10289
10290        /**
10291         * Parse the xml attributes to get the most common properties used by layout managers.
10292         *
10293         * @attr ref androidx.recyclerview.R.styleable#RecyclerView_android_orientation
10294         * @attr ref androidx.recyclerview.R.styleable#RecyclerView_spanCount
10295         * @attr ref androidx.recyclerview.R.styleable#RecyclerView_reverseLayout
10296         * @attr ref androidx.recyclerview.R.styleable#RecyclerView_stackFromEnd
10297         *
10298         * @return an object containing the properties as specified in the attrs.
10299         */
10300        public static Properties getProperties(@NonNull Context context,
10301                @Nullable AttributeSet attrs,
10302                int defStyleAttr, int defStyleRes) {
10303            Properties properties = new Properties();
10304            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
10305                    defStyleAttr, defStyleRes);
10306            properties.orientation = a.getInt(R.styleable.RecyclerView_android_orientation,
10307                    DEFAULT_ORIENTATION);
10308            properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
10309            properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
10310            properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
10311            a.recycle();
10312            return properties;
10313        }
10314
10315        void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
10316            setMeasureSpecs(
10317                    MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
10318                    MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
10319            );
10320        }
10321
10322        /**
10323         * Internal API to allow LayoutManagers to be measured twice.
10324         * <p>
10325         * This is not public because LayoutManagers should be able to handle their layouts in one
10326         * pass but it is very convenient to make existing LayoutManagers support wrapping content
10327         * when both orientations are undefined.
10328         * <p>
10329         * This API will be removed after default LayoutManagers properly implement wrap content in
10330         * non-scroll orientation.
10331         */
10332        boolean shouldMeasureTwice() {
10333            return false;
10334        }
10335
10336        boolean hasFlexibleChildInBothOrientations() {
10337            final int childCount = getChildCount();
10338            for (int i = 0; i < childCount; i++) {
10339                final View child = getChildAt(i);
10340                final ViewGroup.LayoutParams lp = child.getLayoutParams();
10341                if (lp.width < 0 && lp.height < 0) {
10342                    return true;
10343                }
10344            }
10345            return false;
10346        }
10347
10348        /**
10349         * Some general properties that a LayoutManager may want to use.
10350         */
10351        public static class Properties {
10352            /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_android_orientation */
10353            public int orientation;
10354            /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_spanCount */
10355            public int spanCount;
10356            /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_reverseLayout */
10357            public boolean reverseLayout;
10358            /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_stackFromEnd */
10359            public boolean stackFromEnd;
10360        }
10361    }
10362
10363    /**
10364     * An ItemDecoration allows the application to add a special drawing and layout offset
10365     * to specific item views from the adapter's data set. This can be useful for drawing dividers
10366     * between items, highlights, visual grouping boundaries and more.
10367     *
10368     * <p>All ItemDecorations are drawn in the order they were added, before the item
10369     * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
10370     * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
10371     * RecyclerView.State)}.</p>
10372     */
10373    public abstract static class ItemDecoration {
10374        /**
10375         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
10376         * Any content drawn by this method will be drawn before the item views are drawn,
10377         * and will thus appear underneath the views.
10378         *
10379         * @param c Canvas to draw into
10380         * @param parent RecyclerView this ItemDecoration is drawing into
10381         * @param state The current state of RecyclerView
10382         */
10383        public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull State state) {
10384            onDraw(c, parent);
10385        }
10386
10387        /**
10388         * @deprecated
10389         * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
10390         */
10391        @Deprecated
10392        public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent) {
10393        }
10394
10395        /**
10396         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
10397         * Any content drawn by this method will be drawn after the item views are drawn
10398         * and will thus appear over the views.
10399         *
10400         * @param c Canvas to draw into
10401         * @param parent RecyclerView this ItemDecoration is drawing into
10402         * @param state The current state of RecyclerView.
10403         */
10404        public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent,
10405                @NonNull State state) {
10406            onDrawOver(c, parent);
10407        }
10408
10409        /**
10410         * @deprecated
10411         * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
10412         */
10413        @Deprecated
10414        public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent) {
10415        }
10416
10417
10418        /**
10419         * @deprecated
10420         * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
10421         */
10422        @Deprecated
10423        public void getItemOffsets(@NonNull Rect outRect, int itemPosition,
10424                @NonNull RecyclerView parent) {
10425            outRect.set(0, 0, 0, 0);
10426        }
10427
10428        /**
10429         * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
10430         * the number of pixels that the item view should be inset by, similar to padding or margin.
10431         * The default implementation sets the bounds of outRect to 0 and returns.
10432         *
10433         * <p>
10434         * If this ItemDecoration does not affect the positioning of item views, it should set
10435         * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
10436         * before returning.
10437         *
10438         * <p>
10439         * If you need to access Adapter for additional data, you can call
10440         * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
10441         * View.
10442         *
10443         * @param outRect Rect to receive the output.
10444         * @param view    The child view to decorate
10445         * @param parent  RecyclerView this ItemDecoration is decorating
10446         * @param state   The current state of RecyclerView.
10447         */
10448        public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
10449                @NonNull RecyclerView parent, @NonNull State state) {
10450            getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
10451                    parent);
10452        }
10453    }
10454
10455    /**
10456     * An OnItemTouchListener allows the application to intercept touch events in progress at the
10457     * view hierarchy level of the RecyclerView before those touch events are considered for
10458     * RecyclerView's own scrolling behavior.
10459     *
10460     * <p>This can be useful for applications that wish to implement various forms of gestural
10461     * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
10462     * a touch interaction already in progress even if the RecyclerView is already handling that
10463     * gesture stream itself for the purposes of scrolling.</p>
10464     *
10465     * @see SimpleOnItemTouchListener
10466     */
10467    public interface OnItemTouchListener {
10468        /**
10469         * Silently observe and/or take over touch events sent to the RecyclerView
10470         * before they are handled by either the RecyclerView itself or its child views.
10471         *
10472         * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
10473         * in the order in which each listener was added, before any other touch processing
10474         * by the RecyclerView itself or child views occurs.</p>
10475         *
10476         * @param e MotionEvent describing the touch event. All coordinates are in
10477         *          the RecyclerView's coordinate system.
10478         * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
10479         *         to continue with the current behavior and continue observing future events in
10480         *         the gesture.
10481         */
10482        boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
10483
10484        /**
10485         * Process a touch event as part of a gesture that was claimed by returning true from
10486         * a previous call to {@link #onInterceptTouchEvent}.
10487         *
10488         * @param e MotionEvent describing the touch event. All coordinates are in
10489         *          the RecyclerView's coordinate system.
10490         */
10491        void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
10492
10493        /**
10494         * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
10495         * intercept touch events with
10496         * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
10497         *
10498         * @param disallowIntercept True if the child does not want the parent to
10499         *            intercept touch events.
10500         * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
10501         */
10502        void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
10503    }
10504
10505    /**
10506     * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies
10507     * and default return values.
10508     * <p>
10509     * You may prefer to extend this class if you don't need to override all methods. Another
10510     * benefit of using this class is future compatibility. As the interface may change, we'll
10511     * always provide a default implementation on this class so that your code won't break when
10512     * you update to a new version of the support library.
10513     */
10514    public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
10515        @Override
10516        public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
10517            return false;
10518        }
10519
10520        @Override
10521        public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
10522        }
10523
10524        @Override
10525        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
10526        }
10527    }
10528
10529
10530    /**
10531     * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
10532     * has occurred on that RecyclerView.
10533     * <p>
10534     * @see RecyclerView#addOnScrollListener(OnScrollListener)
10535     * @see RecyclerView#clearOnChildAttachStateChangeListeners()
10536     *
10537     */
10538    public abstract static class OnScrollListener {
10539        /**
10540         * Callback method to be invoked when RecyclerView's scroll state changes.
10541         *
10542         * @param recyclerView The RecyclerView whose scroll state has changed.
10543         * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
10544         *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
10545         */
10546        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState){}
10547
10548        /**
10549         * Callback method to be invoked when the RecyclerView has been scrolled. This will be
10550         * called after the scroll has completed.
10551         * <p>
10552         * This callback will also be called if visible item range changes after a layout
10553         * calculation. In that case, dx and dy will be 0.
10554         *
10555         * @param recyclerView The RecyclerView which scrolled.
10556         * @param dx The amount of horizontal scroll.
10557         * @param dy The amount of vertical scroll.
10558         */
10559        public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){}
10560    }
10561
10562    /**
10563     * A RecyclerListener can be set on a RecyclerView to receive messages whenever
10564     * a view is recycled.
10565     *
10566     * @see RecyclerView#setRecyclerListener(RecyclerListener)
10567     */
10568    public interface RecyclerListener {
10569
10570        /**
10571         * This method is called whenever the view in the ViewHolder is recycled.
10572         *
10573         * RecyclerView calls this method right before clearing ViewHolder's internal data and
10574         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
10575         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
10576         * its adapter position.
10577         *
10578         * @param holder The ViewHolder containing the view that was recycled
10579         */
10580        void onViewRecycled(@NonNull ViewHolder holder);
10581    }
10582
10583    /**
10584     * A Listener interface that can be attached to a RecylcerView to get notified
10585     * whenever a ViewHolder is attached to or detached from RecyclerView.
10586     */
10587    public interface OnChildAttachStateChangeListener {
10588
10589        /**
10590         * Called when a view is attached to the RecyclerView.
10591         *
10592         * @param view The View which is attached to the RecyclerView
10593         */
10594        void onChildViewAttachedToWindow(@NonNull View view);
10595
10596        /**
10597         * Called when a view is detached from RecyclerView.
10598         *
10599         * @param view The View which is being detached from the RecyclerView
10600         */
10601        void onChildViewDetachedFromWindow(@NonNull View view);
10602    }
10603
10604    /**
10605     * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
10606     *
10607     * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
10608     * potentially expensive {@link View#findViewById(int)} results.</p>
10609     *
10610     * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
10611     * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
10612     * their own custom ViewHolder implementations to store data that makes binding view contents
10613     * easier. Implementations should assume that individual item views will hold strong references
10614     * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
10615     * strong references to extra off-screen item views for caching purposes</p>
10616     */
10617    public abstract static class ViewHolder {
10618        @NonNull
10619        public final View itemView;
10620        WeakReference<RecyclerView> mNestedRecyclerView;
10621        int mPosition = NO_POSITION;
10622        int mOldPosition = NO_POSITION;
10623        long mItemId = NO_ID;
10624        int mItemViewType = INVALID_TYPE;
10625        int mPreLayoutPosition = NO_POSITION;
10626
10627        // The item that this holder is shadowing during an item change event/animation
10628        ViewHolder mShadowedHolder = null;
10629        // The item that is shadowing this holder during an item change event/animation
10630        ViewHolder mShadowingHolder = null;
10631
10632        /**
10633         * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
10634         * are all valid.
10635         */
10636        static final int FLAG_BOUND = 1 << 0;
10637
10638        /**
10639         * The data this ViewHolder's view reflects is stale and needs to be rebound
10640         * by the adapter. mPosition and mItemId are consistent.
10641         */
10642        static final int FLAG_UPDATE = 1 << 1;
10643
10644        /**
10645         * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
10646         * are not to be trusted and may no longer match the item view type.
10647         * This ViewHolder must be fully rebound to different data.
10648         */
10649        static final int FLAG_INVALID = 1 << 2;
10650
10651        /**
10652         * This ViewHolder points at data that represents an item previously removed from the
10653         * data set. Its view may still be used for things like outgoing animations.
10654         */
10655        static final int FLAG_REMOVED = 1 << 3;
10656
10657        /**
10658         * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
10659         * and is intended to keep views around during animations.
10660         */
10661        static final int FLAG_NOT_RECYCLABLE = 1 << 4;
10662
10663        /**
10664         * This ViewHolder is returned from scrap which means we are expecting an addView call
10665         * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
10666         * the end of the layout pass and then recycled by RecyclerView if it is not added back to
10667         * the RecyclerView.
10668         */
10669        static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
10670
10671        /**
10672         * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
10673         * it unless LayoutManager is replaced.
10674         * It is still fully visible to the LayoutManager.
10675         */
10676        static final int FLAG_IGNORE = 1 << 7;
10677
10678        /**
10679         * When the View is detached form the parent, we set this flag so that we can take correct
10680         * action when we need to remove it or add it back.
10681         */
10682        static final int FLAG_TMP_DETACHED = 1 << 8;
10683
10684        /**
10685         * Set when we can no longer determine the adapter position of this ViewHolder until it is
10686         * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
10687         * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
10688         * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
10689         * re-calculated.
10690         */
10691        static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
10692
10693        /**
10694         * Set when a addChangePayload(null) is called
10695         */
10696        static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
10697
10698        /**
10699         * Used by ItemAnimator when a ViewHolder's position changes
10700         */
10701        static final int FLAG_MOVED = 1 << 11;
10702
10703        /**
10704         * Used by ItemAnimator when a ViewHolder appears in pre-layout
10705         */
10706        static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
10707
10708        static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
10709
10710        /**
10711         * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
10712         * hidden list (as if it was scrap) without being recycled in between.
10713         *
10714         * When a ViewHolder is hidden, there are 2 paths it can be re-used:
10715         *   a) Animation ends, view is recycled and used from the recycle pool.
10716         *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
10717         *
10718         * This flag is used to represent "case b" where the ViewHolder is reused without being
10719         * recycled (thus "bounced" from the hidden list). This state requires special handling
10720         * because the ViewHolder must be added to pre layout maps for animations as if it was
10721         * already there.
10722         */
10723        static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
10724
10725        /**
10726         * Flags that RecyclerView assigned {@link RecyclerViewAccessibilityDelegate
10727         * #getItemDelegate()} in onBindView when app does not provide a delegate.
10728         */
10729        static final int FLAG_SET_A11Y_ITEM_DELEGATE = 1 << 14;
10730
10731        private int mFlags;
10732
10733        private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
10734
10735        List<Object> mPayloads = null;
10736        List<Object> mUnmodifiedPayloads = null;
10737
10738        private int mIsRecyclableCount = 0;
10739
10740        // If non-null, view is currently considered scrap and may be reused for other data by the
10741        // scrap container.
10742        private Recycler mScrapContainer = null;
10743        // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
10744        private boolean mInChangeScrap = false;
10745
10746        // Saves isImportantForAccessibility value for the view item while it's in hidden state and
10747        // marked as unimportant for accessibility.
10748        private int mWasImportantForAccessibilityBeforeHidden =
10749                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10750        // set if we defer the accessibility state change of the view holder
10751        @VisibleForTesting
10752        int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
10753
10754        /**
10755         * Is set when VH is bound from the adapter and cleaned right before it is sent to
10756         * {@link RecycledViewPool}.
10757         */
10758        RecyclerView mOwnerRecyclerView;
10759
10760        public ViewHolder(@NonNull View itemView) {
10761            if (itemView == null) {
10762                throw new IllegalArgumentException("itemView may not be null");
10763            }
10764            this.itemView = itemView;
10765        }
10766
10767        void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
10768            addFlags(ViewHolder.FLAG_REMOVED);
10769            offsetPosition(offset, applyToPreLayout);
10770            mPosition = mNewPosition;
10771        }
10772
10773        void offsetPosition(int offset, boolean applyToPreLayout) {
10774            if (mOldPosition == NO_POSITION) {
10775                mOldPosition = mPosition;
10776            }
10777            if (mPreLayoutPosition == NO_POSITION) {
10778                mPreLayoutPosition = mPosition;
10779            }
10780            if (applyToPreLayout) {
10781                mPreLayoutPosition += offset;
10782            }
10783            mPosition += offset;
10784            if (itemView.getLayoutParams() != null) {
10785                ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
10786            }
10787        }
10788
10789        void clearOldPosition() {
10790            mOldPosition = NO_POSITION;
10791            mPreLayoutPosition = NO_POSITION;
10792        }
10793
10794        void saveOldPosition() {
10795            if (mOldPosition == NO_POSITION) {
10796                mOldPosition = mPosition;
10797            }
10798        }
10799
10800        boolean shouldIgnore() {
10801            return (mFlags & FLAG_IGNORE) != 0;
10802        }
10803
10804        /**
10805         * @deprecated This method is deprecated because its meaning is ambiguous due to the async
10806         * handling of adapter updates. Please use {@link #getLayoutPosition()} or
10807         * {@link #getAdapterPosition()} depending on your use case.
10808         *
10809         * @see #getLayoutPosition()
10810         * @see #getAdapterPosition()
10811         */
10812        @Deprecated
10813        public final int getPosition() {
10814            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10815        }
10816
10817        /**
10818         * Returns the position of the ViewHolder in terms of the latest layout pass.
10819         * <p>
10820         * This position is mostly used by RecyclerView components to be consistent while
10821         * RecyclerView lazily processes adapter updates.
10822         * <p>
10823         * For performance and animation reasons, RecyclerView batches all adapter updates until the
10824         * next layout pass. This may cause mismatches between the Adapter position of the item and
10825         * the position it had in the latest layout calculations.
10826         * <p>
10827         * LayoutManagers should always call this method while doing calculations based on item
10828         * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
10829         * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
10830         * of the item.
10831         * <p>
10832         * If LayoutManager needs to call an external method that requires the adapter position of
10833         * the item, it can use {@link #getAdapterPosition()} or
10834         * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
10835         *
10836         * @return Returns the adapter position of the ViewHolder in the latest layout pass.
10837         * @see #getAdapterPosition()
10838         */
10839        public final int getLayoutPosition() {
10840            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10841        }
10842
10843        /**
10844         * Returns the Adapter position of the item represented by this ViewHolder.
10845         * <p>
10846         * Note that this might be different than the {@link #getLayoutPosition()} if there are
10847         * pending adapter updates but a new layout pass has not happened yet.
10848         * <p>
10849         * RecyclerView does not handle any adapter updates until the next layout traversal. This
10850         * may create temporary inconsistencies between what user sees on the screen and what
10851         * adapter contents have. This inconsistency is not important since it will be less than
10852         * 16ms but it might be a problem if you want to use ViewHolder position to access the
10853         * adapter. Sometimes, you may need to get the exact adapter position to do
10854         * some actions in response to user events. In that case, you should use this method which
10855         * will calculate the Adapter position of the ViewHolder.
10856         * <p>
10857         * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
10858         * next layout pass, the return value of this method will be {@link #NO_POSITION}.
10859         *
10860         * @return The adapter position of the item if it still exists in the adapter.
10861         * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
10862         * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
10863         * layout pass or the ViewHolder has already been recycled.
10864         */
10865        public final int getAdapterPosition() {
10866            if (mOwnerRecyclerView == null) {
10867                return NO_POSITION;
10868            }
10869            return mOwnerRecyclerView.getAdapterPositionFor(this);
10870        }
10871
10872        /**
10873         * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
10874         * to perform animations.
10875         * <p>
10876         * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
10877         * adapter index in the previous layout.
10878         *
10879         * @return The previous adapter index of the Item represented by this ViewHolder or
10880         * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
10881         * complete).
10882         */
10883        public final int getOldPosition() {
10884            return mOldPosition;
10885        }
10886
10887        /**
10888         * Returns The itemId represented by this ViewHolder.
10889         *
10890         * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
10891         * otherwise
10892         */
10893        public final long getItemId() {
10894            return mItemId;
10895        }
10896
10897        /**
10898         * @return The view type of this ViewHolder.
10899         */
10900        public final int getItemViewType() {
10901            return mItemViewType;
10902        }
10903
10904        boolean isScrap() {
10905            return mScrapContainer != null;
10906        }
10907
10908        void unScrap() {
10909            mScrapContainer.unscrapView(this);
10910        }
10911
10912        boolean wasReturnedFromScrap() {
10913            return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
10914        }
10915
10916        void clearReturnedFromScrapFlag() {
10917            mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
10918        }
10919
10920        void clearTmpDetachFlag() {
10921            mFlags = mFlags & ~FLAG_TMP_DETACHED;
10922        }
10923
10924        void stopIgnoring() {
10925            mFlags = mFlags & ~FLAG_IGNORE;
10926        }
10927
10928        void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
10929            mScrapContainer = recycler;
10930            mInChangeScrap = isChangeScrap;
10931        }
10932
10933        boolean isInvalid() {
10934            return (mFlags & FLAG_INVALID) != 0;
10935        }
10936
10937        boolean needsUpdate() {
10938            return (mFlags & FLAG_UPDATE) != 0;
10939        }
10940
10941        boolean isBound() {
10942            return (mFlags & FLAG_BOUND) != 0;
10943        }
10944
10945        boolean isRemoved() {
10946            return (mFlags & FLAG_REMOVED) != 0;
10947        }
10948
10949        boolean hasAnyOfTheFlags(int flags) {
10950            return (mFlags & flags) != 0;
10951        }
10952
10953        boolean isTmpDetached() {
10954            return (mFlags & FLAG_TMP_DETACHED) != 0;
10955        }
10956
10957        boolean isAdapterPositionUnknown() {
10958            return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
10959        }
10960
10961        void setFlags(int flags, int mask) {
10962            mFlags = (mFlags & ~mask) | (flags & mask);
10963        }
10964
10965        void addFlags(int flags) {
10966            mFlags |= flags;
10967        }
10968
10969        void addChangePayload(Object payload) {
10970            if (payload == null) {
10971                addFlags(FLAG_ADAPTER_FULLUPDATE);
10972            } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10973                createPayloadsIfNeeded();
10974                mPayloads.add(payload);
10975            }
10976        }
10977
10978        private void createPayloadsIfNeeded() {
10979            if (mPayloads == null) {
10980                mPayloads = new ArrayList<Object>();
10981                mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
10982            }
10983        }
10984
10985        void clearPayload() {
10986            if (mPayloads != null) {
10987                mPayloads.clear();
10988            }
10989            mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
10990        }
10991
10992        List<Object> getUnmodifiedPayloads() {
10993            if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10994                if (mPayloads == null || mPayloads.size() == 0) {
10995                    // Initial state,  no update being called.
10996                    return FULLUPDATE_PAYLOADS;
10997                }
10998                // there are none-null payloads
10999                return mUnmodifiedPayloads;
11000            } else {
11001                // a full update has been called.
11002                return FULLUPDATE_PAYLOADS;
11003            }
11004        }
11005
11006        void resetInternal() {
11007            mFlags = 0;
11008            mPosition = NO_POSITION;
11009            mOldPosition = NO_POSITION;
11010            mItemId = NO_ID;
11011            mPreLayoutPosition = NO_POSITION;
11012            mIsRecyclableCount = 0;
11013            mShadowedHolder = null;
11014            mShadowingHolder = null;
11015            clearPayload();
11016            mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
11017            mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
11018            clearNestedRecyclerViewIfNotNested(this);
11019        }
11020
11021        /**
11022         * Called when the child view enters the hidden state
11023         */
11024        private void onEnteredHiddenState(RecyclerView parent) {
11025            // While the view item is in hidden state, make it invisible for the accessibility.
11026            if (mPendingAccessibilityState != PENDING_ACCESSIBILITY_STATE_NOT_SET) {
11027                mWasImportantForAccessibilityBeforeHidden = mPendingAccessibilityState;
11028            } else {
11029                mWasImportantForAccessibilityBeforeHidden =
11030                        ViewCompat.getImportantForAccessibility(itemView);
11031            }
11032            parent.setChildImportantForAccessibilityInternal(this,
11033                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
11034        }
11035
11036        /**
11037         * Called when the child view leaves the hidden state
11038         */
11039        private void onLeftHiddenState(RecyclerView parent) {
11040            parent.setChildImportantForAccessibilityInternal(this,
11041                    mWasImportantForAccessibilityBeforeHidden);
11042            mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
11043        }
11044
11045        @Override
11046        public String toString() {
11047            final StringBuilder sb = new StringBuilder("ViewHolder{"
11048                    + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
11049                    + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
11050            if (isScrap()) {
11051                sb.append(" scrap ")
11052                        .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
11053            }
11054            if (isInvalid()) sb.append(" invalid");
11055            if (!isBound()) sb.append(" unbound");
11056            if (needsUpdate()) sb.append(" update");
11057            if (isRemoved()) sb.append(" removed");
11058            if (shouldIgnore()) sb.append(" ignored");
11059            if (isTmpDetached()) sb.append(" tmpDetached");
11060            if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
11061            if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
11062
11063            if (itemView.getParent() == null) sb.append(" no parent");
11064            sb.append("}");
11065            return sb.toString();
11066        }
11067
11068        /**
11069         * Informs the recycler whether this item can be recycled. Views which are not
11070         * recyclable will not be reused for other items until setIsRecyclable() is
11071         * later set to true. Calls to setIsRecyclable() should always be paired (one
11072         * call to setIsRecyclabe(false) should always be matched with a later call to
11073         * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
11074         * reference-counted.
11075         *
11076         * @param recyclable Whether this item is available to be recycled. Default value
11077         * is true.
11078         *
11079         * @see #isRecyclable()
11080         */
11081        public final void setIsRecyclable(boolean recyclable) {
11082            mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
11083            if (mIsRecyclableCount < 0) {
11084                mIsRecyclableCount = 0;
11085                if (DEBUG) {
11086                    throw new RuntimeException("isRecyclable decremented below 0: "
11087                            + "unmatched pair of setIsRecyable() calls for " + this);
11088                }
11089                Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: "
11090                        + "unmatched pair of setIsRecyable() calls for " + this);
11091            } else if (!recyclable && mIsRecyclableCount == 1) {
11092                mFlags |= FLAG_NOT_RECYCLABLE;
11093            } else if (recyclable && mIsRecyclableCount == 0) {
11094                mFlags &= ~FLAG_NOT_RECYCLABLE;
11095            }
11096            if (DEBUG) {
11097                Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
11098            }
11099        }
11100
11101        /**
11102         * @return true if this item is available to be recycled, false otherwise.
11103         *
11104         * @see #setIsRecyclable(boolean)
11105         */
11106        public final boolean isRecyclable() {
11107            return (mFlags & FLAG_NOT_RECYCLABLE) == 0
11108                    && !ViewCompat.hasTransientState(itemView);
11109        }
11110
11111        /**
11112         * Returns whether we have animations referring to this view holder or not.
11113         * This is similar to isRecyclable flag but does not check transient state.
11114         */
11115        private boolean shouldBeKeptAsChild() {
11116            return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
11117        }
11118
11119        /**
11120         * @return True if ViewHolder is not referenced by RecyclerView animations but has
11121         * transient state which will prevent it from being recycled.
11122         */
11123        private boolean doesTransientStatePreventRecycling() {
11124            return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView);
11125        }
11126
11127        boolean isUpdated() {
11128            return (mFlags & FLAG_UPDATE) != 0;
11129        }
11130    }
11131
11132    /**
11133     * This method is here so that we can control the important for a11y changes and test it.
11134     */
11135    @VisibleForTesting
11136    boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
11137            int importantForAccessibility) {
11138        if (isComputingLayout()) {
11139            viewHolder.mPendingAccessibilityState = importantForAccessibility;
11140            mPendingAccessibilityImportanceChange.add(viewHolder);
11141            return false;
11142        }
11143        ViewCompat.setImportantForAccessibility(viewHolder.itemView, importantForAccessibility);
11144        return true;
11145    }
11146
11147    void dispatchPendingImportantForAccessibilityChanges() {
11148        for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
11149            ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
11150            if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
11151                continue;
11152            }
11153            int state = viewHolder.mPendingAccessibilityState;
11154            if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
11155                //noinspection WrongConstant
11156                ViewCompat.setImportantForAccessibility(viewHolder.itemView, state);
11157                viewHolder.mPendingAccessibilityState =
11158                        ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
11159            }
11160        }
11161        mPendingAccessibilityImportanceChange.clear();
11162    }
11163
11164    int getAdapterPositionFor(ViewHolder viewHolder) {
11165        if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
11166                | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
11167                || !viewHolder.isBound()) {
11168            return RecyclerView.NO_POSITION;
11169        }
11170        return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
11171    }
11172
11173    @VisibleForTesting
11174    void initFastScroller(StateListDrawable verticalThumbDrawable,
11175            Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable,
11176            Drawable horizontalTrackDrawable) {
11177        if (verticalThumbDrawable == null || verticalTrackDrawable == null
11178                || horizontalThumbDrawable == null || horizontalTrackDrawable == null) {
11179            throw new IllegalArgumentException(
11180                "Trying to set fast scroller without both required drawables." + exceptionLabel());
11181        }
11182
11183        Resources resources = getContext().getResources();
11184        new FastScroller(this, verticalThumbDrawable, verticalTrackDrawable,
11185                horizontalThumbDrawable, horizontalTrackDrawable,
11186                resources.getDimensionPixelSize(R.dimen.fastscroll_default_thickness),
11187                resources.getDimensionPixelSize(R.dimen.fastscroll_minimum_range),
11188                resources.getDimensionPixelOffset(R.dimen.fastscroll_margin));
11189    }
11190
11191    // NestedScrollingChild
11192
11193    @Override
11194    public void setNestedScrollingEnabled(boolean enabled) {
11195        getScrollingChildHelper().setNestedScrollingEnabled(enabled);
11196    }
11197
11198    @Override
11199    public boolean isNestedScrollingEnabled() {
11200        return getScrollingChildHelper().isNestedScrollingEnabled();
11201    }
11202
11203    @Override
11204    public boolean startNestedScroll(int axes) {
11205        return getScrollingChildHelper().startNestedScroll(axes);
11206    }
11207
11208    @Override
11209    public boolean startNestedScroll(int axes, int type) {
11210        return getScrollingChildHelper().startNestedScroll(axes, type);
11211    }
11212
11213    @Override
11214    public void stopNestedScroll() {
11215        getScrollingChildHelper().stopNestedScroll();
11216    }
11217
11218    @Override
11219    public void stopNestedScroll(int type) {
11220        getScrollingChildHelper().stopNestedScroll(type);
11221    }
11222
11223    @Override
11224    public boolean hasNestedScrollingParent() {
11225        return getScrollingChildHelper().hasNestedScrollingParent();
11226    }
11227
11228    @Override
11229    public boolean hasNestedScrollingParent(int type) {
11230        return getScrollingChildHelper().hasNestedScrollingParent(type);
11231    }
11232
11233    @Override
11234    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
11235            int dyUnconsumed, int[] offsetInWindow) {
11236        return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
11237                dxUnconsumed, dyUnconsumed, offsetInWindow);
11238    }
11239
11240    @Override
11241    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
11242            int dyUnconsumed, int[] offsetInWindow, int type) {
11243        return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
11244                dxUnconsumed, dyUnconsumed, offsetInWindow, type);
11245    }
11246
11247    @Override
11248    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
11249        return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
11250    }
11251
11252    @Override
11253    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
11254            int type) {
11255        return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow,
11256                type);
11257    }
11258
11259    @Override
11260    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
11261        return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
11262    }
11263
11264    @Override
11265    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
11266        return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
11267    }
11268
11269    /**
11270     * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
11271     * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
11272     * to create their own subclass of this <code>LayoutParams</code> class
11273     * to store any additional required per-child view metadata about the layout.
11274     */
11275    public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
11276        ViewHolder mViewHolder;
11277        final Rect mDecorInsets = new Rect();
11278        boolean mInsetsDirty = true;
11279        // Flag is set to true if the view is bound while it is detached from RV.
11280        // In this case, we need to manually call invalidate after view is added to guarantee that
11281        // invalidation is populated through the View hierarchy
11282        boolean mPendingInvalidate = false;
11283
11284        public LayoutParams(Context c, AttributeSet attrs) {
11285            super(c, attrs);
11286        }
11287
11288        public LayoutParams(int width, int height) {
11289            super(width, height);
11290        }
11291
11292        public LayoutParams(MarginLayoutParams source) {
11293            super(source);
11294        }
11295
11296        public LayoutParams(ViewGroup.LayoutParams source) {
11297            super(source);
11298        }
11299
11300        public LayoutParams(LayoutParams source) {
11301            super((ViewGroup.LayoutParams) source);
11302        }
11303
11304        /**
11305         * Returns true if the view this LayoutParams is attached to needs to have its content
11306         * updated from the corresponding adapter.
11307         *
11308         * @return true if the view should have its content updated
11309         */
11310        public boolean viewNeedsUpdate() {
11311            return mViewHolder.needsUpdate();
11312        }
11313
11314        /**
11315         * Returns true if the view this LayoutParams is attached to is now representing
11316         * potentially invalid data. A LayoutManager should scrap/recycle it.
11317         *
11318         * @return true if the view is invalid
11319         */
11320        public boolean isViewInvalid() {
11321            return mViewHolder.isInvalid();
11322        }
11323
11324        /**
11325         * Returns true if the adapter data item corresponding to the view this LayoutParams
11326         * is attached to has been removed from the data set. A LayoutManager may choose to
11327         * treat it differently in order to animate its outgoing or disappearing state.
11328         *
11329         * @return true if the item the view corresponds to was removed from the data set
11330         */
11331        public boolean isItemRemoved() {
11332            return mViewHolder.isRemoved();
11333        }
11334
11335        /**
11336         * Returns true if the adapter data item corresponding to the view this LayoutParams
11337         * is attached to has been changed in the data set. A LayoutManager may choose to
11338         * treat it differently in order to animate its changing state.
11339         *
11340         * @return true if the item the view corresponds to was changed in the data set
11341         */
11342        public boolean isItemChanged() {
11343            return mViewHolder.isUpdated();
11344        }
11345
11346        /**
11347         * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
11348         */
11349        @Deprecated
11350        public int getViewPosition() {
11351            return mViewHolder.getPosition();
11352        }
11353
11354        /**
11355         * Returns the adapter position that the view this LayoutParams is attached to corresponds
11356         * to as of latest layout calculation.
11357         *
11358         * @return the adapter position this view as of latest layout pass
11359         */
11360        public int getViewLayoutPosition() {
11361            return mViewHolder.getLayoutPosition();
11362        }
11363
11364        /**
11365         * Returns the up-to-date adapter position that the view this LayoutParams is attached to
11366         * corresponds to.
11367         *
11368         * @return the up-to-date adapter position this view. It may return
11369         * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
11370         * its up-to-date position cannot be calculated.
11371         */
11372        public int getViewAdapterPosition() {
11373            return mViewHolder.getAdapterPosition();
11374        }
11375    }
11376
11377    /**
11378     * Observer base class for watching changes to an {@link Adapter}.
11379     * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
11380     */
11381    public abstract static class AdapterDataObserver {
11382        public void onChanged() {
11383            // Do nothing
11384        }
11385
11386        public void onItemRangeChanged(int positionStart, int itemCount) {
11387            // do nothing
11388        }
11389
11390        public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
11391            // fallback to onItemRangeChanged(positionStart, itemCount) if app
11392            // does not override this method.
11393            onItemRangeChanged(positionStart, itemCount);
11394        }
11395
11396        public void onItemRangeInserted(int positionStart, int itemCount) {
11397            // do nothing
11398        }
11399
11400        public void onItemRangeRemoved(int positionStart, int itemCount) {
11401            // do nothing
11402        }
11403
11404        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
11405            // do nothing
11406        }
11407    }
11408
11409    /**
11410     * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
11411     * provides methods to trigger a programmatic scroll.</p>
11412     *
11413     * @see LinearSmoothScroller
11414     */
11415    public abstract static class SmoothScroller {
11416
11417        private int mTargetPosition = RecyclerView.NO_POSITION;
11418
11419        private RecyclerView mRecyclerView;
11420
11421        private LayoutManager mLayoutManager;
11422
11423        private boolean mPendingInitialRun;
11424
11425        private boolean mRunning;
11426
11427        private View mTargetView;
11428
11429        private final Action mRecyclingAction;
11430
11431        public SmoothScroller() {
11432            mRecyclingAction = new Action(0, 0);
11433        }
11434
11435        /**
11436         * Starts a smooth scroll for the given target position.
11437         * <p>In each animation step, {@link RecyclerView} will check
11438         * for the target view and call either
11439         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
11440         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
11441         * SmoothScroller is stopped.</p>
11442         *
11443         * <p>Note that if RecyclerView finds the target view, it will automatically stop the
11444         * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
11445         * stop calling SmoothScroller in each animation step.</p>
11446         */
11447        void start(RecyclerView recyclerView, LayoutManager layoutManager) {
11448            mRecyclerView = recyclerView;
11449            mLayoutManager = layoutManager;
11450            if (mTargetPosition == RecyclerView.NO_POSITION) {
11451                throw new IllegalArgumentException("Invalid target position");
11452            }
11453            mRecyclerView.mState.mTargetPosition = mTargetPosition;
11454            mRunning = true;
11455            mPendingInitialRun = true;
11456            mTargetView = findViewByPosition(getTargetPosition());
11457            onStart();
11458            mRecyclerView.mViewFlinger.postOnAnimation();
11459        }
11460
11461        public void setTargetPosition(int targetPosition) {
11462            mTargetPosition = targetPosition;
11463        }
11464
11465        /**
11466         * Compute the scroll vector for a given target position.
11467         * <p>
11468         * This method can return null if the layout manager cannot calculate a scroll vector
11469         * for the given position (e.g. it has no current scroll position).
11470         *
11471         * @param targetPosition the position to which the scroller is scrolling
11472         *
11473         * @return the scroll vector for a given target position
11474         */
11475        @Nullable
11476        public PointF computeScrollVectorForPosition(int targetPosition) {
11477            LayoutManager layoutManager = getLayoutManager();
11478            if (layoutManager instanceof ScrollVectorProvider) {
11479                return ((ScrollVectorProvider) layoutManager)
11480                        .computeScrollVectorForPosition(targetPosition);
11481            }
11482            Log.w(TAG, "You should override computeScrollVectorForPosition when the LayoutManager"
11483                    + " does not implement " + ScrollVectorProvider.class.getCanonicalName());
11484            return null;
11485        }
11486
11487        /**
11488         * @return The LayoutManager to which this SmoothScroller is attached. Will return
11489         * <code>null</code> after the SmoothScroller is stopped.
11490         */
11491        @Nullable
11492        public LayoutManager getLayoutManager() {
11493            return mLayoutManager;
11494        }
11495
11496        /**
11497         * Stops running the SmoothScroller in each animation callback. Note that this does not
11498         * cancel any existing {@link Action} updated by
11499         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
11500         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
11501         */
11502        protected final void stop() {
11503            if (!mRunning) {
11504                return;
11505            }
11506            mRunning = false;
11507            onStop();
11508            mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
11509            mTargetView = null;
11510            mTargetPosition = RecyclerView.NO_POSITION;
11511            mPendingInitialRun = false;
11512            // trigger a cleanup
11513            mLayoutManager.onSmoothScrollerStopped(this);
11514            // clear references to avoid any potential leak by a custom smooth scroller
11515            mLayoutManager = null;
11516            mRecyclerView = null;
11517        }
11518
11519        /**
11520         * Returns true if SmoothScroller has been started but has not received the first
11521         * animation
11522         * callback yet.
11523         *
11524         * @return True if this SmoothScroller is waiting to start
11525         */
11526        public boolean isPendingInitialRun() {
11527            return mPendingInitialRun;
11528        }
11529
11530
11531        /**
11532         * @return True if SmoothScroller is currently active
11533         */
11534        public boolean isRunning() {
11535            return mRunning;
11536        }
11537
11538        /**
11539         * Returns the adapter position of the target item
11540         *
11541         * @return Adapter position of the target item or
11542         * {@link RecyclerView#NO_POSITION} if no target view is set.
11543         */
11544        public int getTargetPosition() {
11545            return mTargetPosition;
11546        }
11547
11548        private void onAnimation(int dx, int dy) {
11549            // TODO(b/72745539): If mRunning is false, we call stop, which is a no op if mRunning
11550            // is false. Also, if recyclerView is null, we call stop, and stop assumes recyclerView
11551            // is not null (as does the code following this block).  This should be cleaned up.
11552            final RecyclerView recyclerView = mRecyclerView;
11553            if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
11554                stop();
11555            }
11556
11557            // The following if block exists to have the LayoutManager scroll 1 pixel in the correct
11558            // direction in order to cause the LayoutManager to draw two pages worth of views so
11559            // that the target view may be found before scrolling any further.  This is done to
11560            // prevent an initial scroll distance from scrolling past the view, which causes a
11561            // jittery looking animation. (This block also necessarily sets mPendingInitialRun to
11562            // false if it was true).
11563            if (mPendingInitialRun && mTargetView == null && mLayoutManager != null) {
11564                PointF pointF = computeScrollVectorForPosition(mTargetPosition);
11565                if (pointF != null && (pointF.x != 0 || pointF.y != 0)) {
11566                    recyclerView.scrollStep(
11567                            (int) Math.signum(pointF.x),
11568                            (int) Math.signum(pointF.y),
11569                            null);
11570                }
11571            }
11572
11573            mPendingInitialRun = false;
11574
11575            if (mTargetView != null) {
11576                // verify target position
11577                if (getChildPosition(mTargetView) == mTargetPosition) {
11578                    onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
11579                    mRecyclingAction.runIfNecessary(recyclerView);
11580                    stop();
11581                } else {
11582                    Log.e(TAG, "Passed over target position while smooth scrolling.");
11583                    mTargetView = null;
11584                }
11585            }
11586            if (mRunning) {
11587                onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
11588                boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
11589                mRecyclingAction.runIfNecessary(recyclerView);
11590                if (hadJumpTarget) {
11591                    // It is not stopped so needs to be restarted
11592                    if (mRunning) {
11593                        mPendingInitialRun = true;
11594                        recyclerView.mViewFlinger.postOnAnimation();
11595                    } else {
11596                        // TODO(b/72745539): stop() is a no-op if mRunning is false, so this can be
11597                        // removed.
11598                        stop(); // done
11599                    }
11600                }
11601            }
11602        }
11603
11604        /**
11605         * @see RecyclerView#getChildLayoutPosition(android.view.View)
11606         */
11607        public int getChildPosition(View view) {
11608            return mRecyclerView.getChildLayoutPosition(view);
11609        }
11610
11611        /**
11612         * @see RecyclerView.LayoutManager#getChildCount()
11613         */
11614        public int getChildCount() {
11615            return mRecyclerView.mLayout.getChildCount();
11616        }
11617
11618        /**
11619         * @see RecyclerView.LayoutManager#findViewByPosition(int)
11620         */
11621        public View findViewByPosition(int position) {
11622            return mRecyclerView.mLayout.findViewByPosition(position);
11623        }
11624
11625        /**
11626         * @see RecyclerView#scrollToPosition(int)
11627         * @deprecated Use {@link Action#jumpTo(int)}.
11628         */
11629        @Deprecated
11630        public void instantScrollToPosition(int position) {
11631            mRecyclerView.scrollToPosition(position);
11632        }
11633
11634        protected void onChildAttachedToWindow(View child) {
11635            if (getChildPosition(child) == getTargetPosition()) {
11636                mTargetView = child;
11637                if (DEBUG) {
11638                    Log.d(TAG, "smooth scroll target view has been attached");
11639                }
11640            }
11641        }
11642
11643        /**
11644         * Normalizes the vector.
11645         * @param scrollVector The vector that points to the target scroll position
11646         */
11647        protected void normalize(@NonNull PointF scrollVector) {
11648            final float magnitude = (float) Math.sqrt(scrollVector.x * scrollVector.x
11649                    + scrollVector.y * scrollVector.y);
11650            scrollVector.x /= magnitude;
11651            scrollVector.y /= magnitude;
11652        }
11653
11654        /**
11655         * Called when smooth scroll is started. This might be a good time to do setup.
11656         */
11657        protected abstract void onStart();
11658
11659        /**
11660         * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
11661         * @see #stop()
11662         */
11663        protected abstract void onStop();
11664
11665        /**
11666         * <p>RecyclerView will call this method each time it scrolls until it can find the target
11667         * position in the layout.</p>
11668         * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
11669         * provided {@link Action} to define the next scroll.</p>
11670         *
11671         * @param dx        Last scroll amount horizontally
11672         * @param dy        Last scroll amount vertically
11673         * @param state     Transient state of RecyclerView
11674         * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
11675         *                  update this object.
11676         */
11677        protected abstract void onSeekTargetStep(@Px int dx, @Px int dy, @NonNull State state,
11678                @NonNull Action action);
11679
11680        /**
11681         * Called when the target position is laid out. This is the last callback SmoothScroller
11682         * will receive and it should update the provided {@link Action} to define the scroll
11683         * details towards the target view.
11684         * @param targetView    The view element which render the target position.
11685         * @param state         Transient state of RecyclerView
11686         * @param action        Action instance that you should update to define final scroll action
11687         *                      towards the targetView
11688         */
11689        protected abstract void onTargetFound(@NonNull View targetView, @NonNull State state,
11690                @NonNull Action action);
11691
11692        /**
11693         * Holds information about a smooth scroll request by a {@link SmoothScroller}.
11694         */
11695        public static class Action {
11696
11697            public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
11698
11699            private int mDx;
11700
11701            private int mDy;
11702
11703            private int mDuration;
11704
11705            private int mJumpToPosition = NO_POSITION;
11706
11707            private Interpolator mInterpolator;
11708
11709            private boolean mChanged = false;
11710
11711            // we track this variable to inform custom implementer if they are updating the action
11712            // in every animation callback
11713            private int mConsecutiveUpdates = 0;
11714
11715            /**
11716             * @param dx Pixels to scroll horizontally
11717             * @param dy Pixels to scroll vertically
11718             */
11719            public Action(@Px int dx, @Px int dy) {
11720                this(dx, dy, UNDEFINED_DURATION, null);
11721            }
11722
11723            /**
11724             * @param dx       Pixels to scroll horizontally
11725             * @param dy       Pixels to scroll vertically
11726             * @param duration Duration of the animation in milliseconds
11727             */
11728            public Action(@Px int dx, @Px int dy, int duration) {
11729                this(dx, dy, duration, null);
11730            }
11731
11732            /**
11733             * @param dx           Pixels to scroll horizontally
11734             * @param dy           Pixels to scroll vertically
11735             * @param duration     Duration of the animation in milliseconds
11736             * @param interpolator Interpolator to be used when calculating scroll position in each
11737             *                     animation step
11738             */
11739            public Action(@Px int dx, @Px int dy, int duration,
11740                    @Nullable Interpolator interpolator) {
11741                mDx = dx;
11742                mDy = dy;
11743                mDuration = duration;
11744                mInterpolator = interpolator;
11745            }
11746
11747            /**
11748             * Instead of specifying pixels to scroll, use the target position to jump using
11749             * {@link RecyclerView#scrollToPosition(int)}.
11750             * <p>
11751             * You may prefer using this method if scroll target is really far away and you prefer
11752             * to jump to a location and smooth scroll afterwards.
11753             * <p>
11754             * Note that calling this method takes priority over other update methods such as
11755             * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
11756             * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
11757             * {@link #jumpTo(int)}, the other changes will not be considered for this animation
11758             * frame.
11759             *
11760             * @param targetPosition The target item position to scroll to using instant scrolling.
11761             */
11762            public void jumpTo(int targetPosition) {
11763                mJumpToPosition = targetPosition;
11764            }
11765
11766            boolean hasJumpTarget() {
11767                return mJumpToPosition >= 0;
11768            }
11769
11770            void runIfNecessary(RecyclerView recyclerView) {
11771                if (mJumpToPosition >= 0) {
11772                    final int position = mJumpToPosition;
11773                    mJumpToPosition = NO_POSITION;
11774                    recyclerView.jumpToPositionForSmoothScroller(position);
11775                    mChanged = false;
11776                    return;
11777                }
11778                if (mChanged) {
11779                    validate();
11780                    if (mInterpolator == null) {
11781                        if (mDuration == UNDEFINED_DURATION) {
11782                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
11783                        } else {
11784                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
11785                        }
11786                    } else {
11787                        recyclerView.mViewFlinger.smoothScrollBy(
11788                                mDx, mDy, mDuration, mInterpolator);
11789                    }
11790                    mConsecutiveUpdates++;
11791                    if (mConsecutiveUpdates > 10) {
11792                        // A new action is being set in every animation step. This looks like a bad
11793                        // implementation. Inform developer.
11794                        Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
11795                                + " you are not changing it unless necessary");
11796                    }
11797                    mChanged = false;
11798                } else {
11799                    mConsecutiveUpdates = 0;
11800                }
11801            }
11802
11803            private void validate() {
11804                if (mInterpolator != null && mDuration < 1) {
11805                    throw new IllegalStateException("If you provide an interpolator, you must"
11806                            + " set a positive duration");
11807                } else if (mDuration < 1) {
11808                    throw new IllegalStateException("Scroll duration must be a positive number");
11809                }
11810            }
11811
11812            @Px
11813            public int getDx() {
11814                return mDx;
11815            }
11816
11817            public void setDx(@Px int dx) {
11818                mChanged = true;
11819                mDx = dx;
11820            }
11821
11822            @Px
11823            public int getDy() {
11824                return mDy;
11825            }
11826
11827            public void setDy(@Px int dy) {
11828                mChanged = true;
11829                mDy = dy;
11830            }
11831
11832            public int getDuration() {
11833                return mDuration;
11834            }
11835
11836            public void setDuration(int duration) {
11837                mChanged = true;
11838                mDuration = duration;
11839            }
11840
11841            @Nullable
11842            public Interpolator getInterpolator() {
11843                return mInterpolator;
11844            }
11845
11846            /**
11847             * Sets the interpolator to calculate scroll steps
11848             * @param interpolator The interpolator to use. If you specify an interpolator, you must
11849             *                     also set the duration.
11850             * @see #setDuration(int)
11851             */
11852            public void setInterpolator(@Nullable Interpolator interpolator) {
11853                mChanged = true;
11854                mInterpolator = interpolator;
11855            }
11856
11857            /**
11858             * Updates the action with given parameters.
11859             * @param dx Pixels to scroll horizontally
11860             * @param dy Pixels to scroll vertically
11861             * @param duration Duration of the animation in milliseconds
11862             * @param interpolator Interpolator to be used when calculating scroll position in each
11863             *                     animation step
11864             */
11865            public void update(@Px int dx, @Px int dy, int duration,
11866                    @Nullable Interpolator interpolator) {
11867                mDx = dx;
11868                mDy = dy;
11869                mDuration = duration;
11870                mInterpolator = interpolator;
11871                mChanged = true;
11872            }
11873        }
11874
11875        /**
11876         * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
11877         * to provide a hint to a {@link SmoothScroller} about the location of the target position.
11878         */
11879        public interface ScrollVectorProvider {
11880            /**
11881             * Should calculate the vector that points to the direction where the target position
11882             * can be found.
11883             * <p>
11884             * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
11885             * the target position.
11886             * <p>
11887             * The magnitude of the vector is not important. It is always normalized before being
11888             * used by the {@link LinearSmoothScroller}.
11889             * <p>
11890             * LayoutManager should not check whether the position exists in the adapter or not.
11891             *
11892             * @param targetPosition the target position to which the returned vector should point
11893             *
11894             * @return the scroll vector for a given position.
11895             */
11896            @Nullable
11897            PointF computeScrollVectorForPosition(int targetPosition);
11898        }
11899    }
11900
11901    static class AdapterDataObservable extends Observable<AdapterDataObserver> {
11902        public boolean hasObservers() {
11903            return !mObservers.isEmpty();
11904        }
11905
11906        public void notifyChanged() {
11907            // since onChanged() is implemented by the app, it could do anything, including
11908            // removing itself from {@link mObservers} - and that could cause problems if
11909            // an iterator is used on the ArrayList {@link mObservers}.
11910            // to avoid such problems, just march thru the list in the reverse order.
11911            for (int i = mObservers.size() - 1; i >= 0; i--) {
11912                mObservers.get(i).onChanged();
11913            }
11914        }
11915
11916        public void notifyItemRangeChanged(int positionStart, int itemCount) {
11917            notifyItemRangeChanged(positionStart, itemCount, null);
11918        }
11919
11920        public void notifyItemRangeChanged(int positionStart, int itemCount,
11921                @Nullable Object payload) {
11922            // since onItemRangeChanged() is implemented by the app, it could do anything, including
11923            // removing itself from {@link mObservers} - and that could cause problems if
11924            // an iterator is used on the ArrayList {@link mObservers}.
11925            // to avoid such problems, just march thru the list in the reverse order.
11926            for (int i = mObservers.size() - 1; i >= 0; i--) {
11927                mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
11928            }
11929        }
11930
11931        public void notifyItemRangeInserted(int positionStart, int itemCount) {
11932            // since onItemRangeInserted() is implemented by the app, it could do anything,
11933            // including removing itself from {@link mObservers} - and that could cause problems if
11934            // an iterator is used on the ArrayList {@link mObservers}.
11935            // to avoid such problems, just march thru the list in the reverse order.
11936            for (int i = mObservers.size() - 1; i >= 0; i--) {
11937                mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
11938            }
11939        }
11940
11941        public void notifyItemRangeRemoved(int positionStart, int itemCount) {
11942            // since onItemRangeRemoved() is implemented by the app, it could do anything, including
11943            // removing itself from {@link mObservers} - and that could cause problems if
11944            // an iterator is used on the ArrayList {@link mObservers}.
11945            // to avoid such problems, just march thru the list in the reverse order.
11946            for (int i = mObservers.size() - 1; i >= 0; i--) {
11947                mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
11948            }
11949        }
11950
11951        public void notifyItemMoved(int fromPosition, int toPosition) {
11952            for (int i = mObservers.size() - 1; i >= 0; i--) {
11953                mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
11954            }
11955        }
11956    }
11957
11958    /**
11959     * This is public so that the CREATOR can be accessed on cold launch.
11960     * @hide
11961     */
11962    @RestrictTo(LIBRARY_GROUP)
11963    public static class SavedState extends AbsSavedState {
11964
11965        Parcelable mLayoutState;
11966
11967        /**
11968         * called by CREATOR
11969         */
11970        SavedState(Parcel in, ClassLoader loader) {
11971            super(in, loader);
11972            mLayoutState = in.readParcelable(
11973                    loader != null ? loader : LayoutManager.class.getClassLoader());
11974        }
11975
11976        /**
11977         * Called by onSaveInstanceState
11978         */
11979        SavedState(Parcelable superState) {
11980            super(superState);
11981        }
11982
11983        @Override
11984        public void writeToParcel(Parcel dest, int flags) {
11985            super.writeToParcel(dest, flags);
11986            dest.writeParcelable(mLayoutState, 0);
11987        }
11988
11989        void copyFrom(SavedState other) {
11990            mLayoutState = other.mLayoutState;
11991        }
11992
11993        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
11994            @Override
11995            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
11996                return new SavedState(in, loader);
11997            }
11998
11999            @Override
12000            public SavedState createFromParcel(Parcel in) {
12001                return new SavedState(in, null);
12002            }
12003
12004            @Override
12005            public SavedState[] newArray(int size) {
12006                return new SavedState[size];
12007            }
12008        };
12009    }
12010
12011    /**
12012     * <p>Contains useful information about the current RecyclerView state like target scroll
12013     * position or view focus. State object can also keep arbitrary data, identified by resource
12014     * ids.</p>
12015     * <p>Often times, RecyclerView components will need to pass information between each other.
12016     * To provide a well defined data bus between components, RecyclerView passes the same State
12017     * object to component callbacks and these components can use it to exchange data.</p>
12018     * <p>If you implement custom components, you can use State's put/get/remove methods to pass
12019     * data between your components without needing to manage their lifecycles.</p>
12020     */
12021    public static class State {
12022        static final int STEP_START = 1;
12023        static final int STEP_LAYOUT = 1 << 1;
12024        static final int STEP_ANIMATIONS = 1 << 2;
12025
12026        void assertLayoutStep(int accepted) {
12027            if ((accepted & mLayoutStep) == 0) {
12028                throw new IllegalStateException("Layout state should be one of "
12029                        + Integer.toBinaryString(accepted) + " but it is "
12030                        + Integer.toBinaryString(mLayoutStep));
12031            }
12032        }
12033
12034
12035        /** Owned by SmoothScroller */
12036        private int mTargetPosition = RecyclerView.NO_POSITION;
12037
12038        private SparseArray<Object> mData;
12039
12040        ////////////////////////////////////////////////////////////////////////////////////////////
12041        // Fields below are carried from one layout pass to the next
12042        ////////////////////////////////////////////////////////////////////////////////////////////
12043
12044        /**
12045         * Number of items adapter had in the previous layout.
12046         */
12047        int mPreviousLayoutItemCount = 0;
12048
12049        /**
12050         * Number of items that were NOT laid out but has been deleted from the adapter after the
12051         * previous layout.
12052         */
12053        int mDeletedInvisibleItemCountSincePreviousLayout = 0;
12054
12055        ////////////////////////////////////////////////////////////////////////////////////////////
12056        // Fields below must be updated or cleared before they are used (generally before a pass)
12057        ////////////////////////////////////////////////////////////////////////////////////////////
12058
12059        @IntDef(flag = true, value = {
12060                STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
12061        })
12062        @Retention(RetentionPolicy.SOURCE)
12063        @interface LayoutState {}
12064
12065        @LayoutState
12066        int mLayoutStep = STEP_START;
12067
12068        /**
12069         * Number of items adapter has.
12070         */
12071        int mItemCount = 0;
12072
12073        boolean mStructureChanged = false;
12074
12075        /**
12076         * True if the associated {@link RecyclerView} is in the pre-layout step where it is having
12077         * its {@link LayoutManager} layout items where they will be at the beginning of a set of
12078         * predictive item animations.
12079         */
12080        boolean mInPreLayout = false;
12081
12082        boolean mTrackOldChangeHolders = false;
12083
12084        boolean mIsMeasuring = false;
12085
12086        ////////////////////////////////////////////////////////////////////////////////////////////
12087        // Fields below are always reset outside of the pass (or passes) that use them
12088        ////////////////////////////////////////////////////////////////////////////////////////////
12089
12090        boolean mRunSimpleAnimations = false;
12091
12092        boolean mRunPredictiveAnimations = false;
12093
12094        /**
12095         * This data is saved before a layout calculation happens. After the layout is finished,
12096         * if the previously focused view has been replaced with another view for the same item, we
12097         * move the focus to the new item automatically.
12098         */
12099        int mFocusedItemPosition;
12100        long mFocusedItemId;
12101        // when a sub child has focus, record its id and see if we can directly request focus on
12102        // that one instead
12103        int mFocusedSubChildId;
12104
12105        int mRemainingScrollHorizontal;
12106        int mRemainingScrollVertical;
12107
12108        ////////////////////////////////////////////////////////////////////////////////////////////
12109
12110        State reset() {
12111            mTargetPosition = RecyclerView.NO_POSITION;
12112            if (mData != null) {
12113                mData.clear();
12114            }
12115            mItemCount = 0;
12116            mStructureChanged = false;
12117            mIsMeasuring = false;
12118            return this;
12119        }
12120
12121        /**
12122         * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
12123         * prior to any layout passes.
12124         *
12125         * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
12126         * that Recycler#getViewForPosition() can function safely.</p>
12127         */
12128        void prepareForNestedPrefetch(Adapter adapter) {
12129            mLayoutStep = STEP_START;
12130            mItemCount = adapter.getItemCount();
12131            mInPreLayout = false;
12132            mTrackOldChangeHolders = false;
12133            mIsMeasuring = false;
12134        }
12135
12136        /**
12137         * Returns true if the RecyclerView is currently measuring the layout. This value is
12138         * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
12139         * has non-exact measurement specs.
12140         * <p>
12141         * Note that if the LayoutManager supports predictive animations and it is calculating the
12142         * pre-layout step, this value will be {@code false} even if the RecyclerView is in
12143         * {@code onMeasure} call. This is because pre-layout means the previous state of the
12144         * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
12145         * LayoutManager is always guaranteed to receive another call to
12146         * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
12147         *
12148         * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
12149         */
12150        public boolean isMeasuring() {
12151            return mIsMeasuring;
12152        }
12153
12154        /**
12155         * Returns true if the {@link RecyclerView} is in the pre-layout step where it is having its
12156         * {@link LayoutManager} layout items where they will be at the beginning of a set of
12157         * predictive item animations.
12158         */
12159        public boolean isPreLayout() {
12160            return mInPreLayout;
12161        }
12162
12163        /**
12164         * Returns whether RecyclerView will run predictive animations in this layout pass
12165         * or not.
12166         *
12167         * @return true if RecyclerView is calculating predictive animations to be run at the end
12168         *         of the layout pass.
12169         */
12170        public boolean willRunPredictiveAnimations() {
12171            return mRunPredictiveAnimations;
12172        }
12173
12174        /**
12175         * Returns whether RecyclerView will run simple animations in this layout pass
12176         * or not.
12177         *
12178         * @return true if RecyclerView is calculating simple animations to be run at the end of
12179         *         the layout pass.
12180         */
12181        public boolean willRunSimpleAnimations() {
12182            return mRunSimpleAnimations;
12183        }
12184
12185        /**
12186         * Removes the mapping from the specified id, if there was any.
12187         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
12188         *                   preserve cross functionality and avoid conflicts.
12189         */
12190        public void remove(int resourceId) {
12191            if (mData == null) {
12192                return;
12193            }
12194            mData.remove(resourceId);
12195        }
12196
12197        /**
12198         * Gets the Object mapped from the specified id, or <code>null</code>
12199         * if no such data exists.
12200         *
12201         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
12202         *                   to
12203         *                   preserve cross functionality and avoid conflicts.
12204         */
12205        @SuppressWarnings("TypeParameterUnusedInFormals")
12206        public <T> T get(int resourceId) {
12207            if (mData == null) {
12208                return null;
12209            }
12210            return (T) mData.get(resourceId);
12211        }
12212
12213        /**
12214         * Adds a mapping from the specified id to the specified value, replacing the previous
12215         * mapping from the specified key if there was one.
12216         *
12217         * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
12218         *                   preserve cross functionality and avoid conflicts.
12219         * @param data       The data you want to associate with the resourceId.
12220         */
12221        public void put(int resourceId, Object data) {
12222            if (mData == null) {
12223                mData = new SparseArray<Object>();
12224            }
12225            mData.put(resourceId, data);
12226        }
12227
12228        /**
12229         * If scroll is triggered to make a certain item visible, this value will return the
12230         * adapter index of that item.
12231         * @return Adapter index of the target item or
12232         * {@link RecyclerView#NO_POSITION} if there is no target
12233         * position.
12234         */
12235        public int getTargetScrollPosition() {
12236            return mTargetPosition;
12237        }
12238
12239        /**
12240         * Returns if current scroll has a target position.
12241         * @return true if scroll is being triggered to make a certain position visible
12242         * @see #getTargetScrollPosition()
12243         */
12244        public boolean hasTargetScrollPosition() {
12245            return mTargetPosition != RecyclerView.NO_POSITION;
12246        }
12247
12248        /**
12249         * @return true if the structure of the data set has changed since the last call to
12250         *         onLayoutChildren, false otherwise
12251         */
12252        public boolean didStructureChange() {
12253            return mStructureChanged;
12254        }
12255
12256        /**
12257         * Returns the total number of items that can be laid out. Note that this number is not
12258         * necessarily equal to the number of items in the adapter, so you should always use this
12259         * number for your position calculations and never access the adapter directly.
12260         * <p>
12261         * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
12262         * data changes on existing Views. These calculations are used to decide which animations
12263         * should be run.
12264         * <p>
12265         * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
12266         * present the correct state to LayoutManager in pre-layout pass.
12267         * <p>
12268         * For example, a newly added item is not included in pre-layout item count because
12269         * pre-layout reflects the contents of the adapter before the item is added. Behind the
12270         * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
12271         * LayoutManager does not know about the new item's existence in pre-layout. The item will
12272         * be available in second layout pass and will be included in the item count. Similar
12273         * adjustments are made for moved and removed items as well.
12274         * <p>
12275         * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
12276         *
12277         * @return The number of items currently available
12278         * @see LayoutManager#getItemCount()
12279         */
12280        public int getItemCount() {
12281            return mInPreLayout
12282                    ? (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout)
12283                    : mItemCount;
12284        }
12285
12286        /**
12287         * Returns remaining horizontal scroll distance of an ongoing scroll animation(fling/
12288         * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
12289         * other than {@link #SCROLL_STATE_SETTLING}.
12290         *
12291         * @return Remaining horizontal scroll distance
12292         */
12293        public int getRemainingScrollHorizontal() {
12294            return mRemainingScrollHorizontal;
12295        }
12296
12297        /**
12298         * Returns remaining vertical scroll distance of an ongoing scroll animation(fling/
12299         * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
12300         * other than {@link #SCROLL_STATE_SETTLING}.
12301         *
12302         * @return Remaining vertical scroll distance
12303         */
12304        public int getRemainingScrollVertical() {
12305            return mRemainingScrollVertical;
12306        }
12307
12308        @Override
12309        public String toString() {
12310            return "State{"
12311                    + "mTargetPosition=" + mTargetPosition
12312                    + ", mData=" + mData
12313                    + ", mItemCount=" + mItemCount
12314                    + ", mIsMeasuring=" + mIsMeasuring
12315                    + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
12316                    + ", mDeletedInvisibleItemCountSincePreviousLayout="
12317                    + mDeletedInvisibleItemCountSincePreviousLayout
12318                    + ", mStructureChanged=" + mStructureChanged
12319                    + ", mInPreLayout=" + mInPreLayout
12320                    + ", mRunSimpleAnimations=" + mRunSimpleAnimations
12321                    + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
12322                    + '}';
12323        }
12324    }
12325
12326    /**
12327     * This class defines the behavior of fling if the developer wishes to handle it.
12328     * <p>
12329     * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
12330     *
12331     * @see #setOnFlingListener(OnFlingListener)
12332     */
12333    public abstract static class OnFlingListener {
12334
12335        /**
12336         * Override this to handle a fling given the velocities in both x and y directions.
12337         * Note that this method will only be called if the associated {@link LayoutManager}
12338         * supports scrolling and the fling is not handled by nested scrolls first.
12339         *
12340         * @param velocityX the fling velocity on the X axis
12341         * @param velocityY the fling velocity on the Y axis
12342         *
12343         * @return true if the fling was handled, false otherwise.
12344         */
12345        public abstract boolean onFling(int velocityX, int velocityY);
12346    }
12347
12348    /**
12349     * Internal listener that manages items after animations finish. This is how items are
12350     * retained (not recycled) during animations, but allowed to be recycled afterwards.
12351     * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
12352     * method on the animator's listener when it is done animating any item.
12353     */
12354    private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
12355
12356        ItemAnimatorRestoreListener() {
12357        }
12358
12359        @Override
12360        public void onAnimationFinished(ViewHolder item) {
12361            item.setIsRecyclable(true);
12362            if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
12363                item.mShadowedHolder = null;
12364            }
12365            // always null this because an OldViewHolder can never become NewViewHolder w/o being
12366            // recycled.
12367            item.mShadowingHolder = null;
12368            if (!item.shouldBeKeptAsChild()) {
12369                if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
12370                    removeDetachedView(item.itemView, false);
12371                }
12372            }
12373        }
12374    }
12375
12376    /**
12377     * This class defines the animations that take place on items as changes are made
12378     * to the adapter.
12379     *
12380     * Subclasses of ItemAnimator can be used to implement custom animations for actions on
12381     * ViewHolder items. The RecyclerView will manage retaining these items while they
12382     * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
12383     * when a ViewHolder's animation is finished. In other words, there must be a matching
12384     * {@link #dispatchAnimationFinished(ViewHolder)} call for each
12385     * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
12386     * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12387     * animateChange()}
12388     * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
12389     * and
12390     * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12391     * animateDisappearance()} call.
12392     *
12393     * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
12394     *
12395     * @see #setItemAnimator(ItemAnimator)
12396     */
12397    @SuppressWarnings("UnusedParameters")
12398    public abstract static class ItemAnimator {
12399
12400        /**
12401         * The Item represented by this ViewHolder is updated.
12402         * <p>
12403         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12404         */
12405        public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
12406
12407        /**
12408         * The Item represented by this ViewHolder is removed from the adapter.
12409         * <p>
12410         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12411         */
12412        public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
12413
12414        /**
12415         * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
12416         * represented by this ViewHolder is invalid.
12417         * <p>
12418         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12419         */
12420        public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
12421
12422        /**
12423         * The position of the Item represented by this ViewHolder has been changed. This flag is
12424         * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
12425         * any adapter change that may have a side effect on this item. (e.g. The item before this
12426         * one has been removed from the Adapter).
12427         * <p>
12428         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12429         */
12430        public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
12431
12432        /**
12433         * This ViewHolder was not laid out but has been added to the layout in pre-layout state
12434         * by the {@link LayoutManager}. This means that the item was already in the Adapter but
12435         * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
12436         * to add new items in pre-layout to specify their virtual location when they are invisible
12437         * (e.g. to specify the item should <i>animate in</i> from below the visible area).
12438         * <p>
12439         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12440         */
12441        public static final int FLAG_APPEARED_IN_PRE_LAYOUT =
12442                ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
12443
12444        /**
12445         * The set of flags that might be passed to
12446         * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12447         */
12448        @IntDef(flag = true, value = {
12449                FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
12450                FLAG_APPEARED_IN_PRE_LAYOUT
12451        })
12452        @Retention(RetentionPolicy.SOURCE)
12453        public @interface AdapterChanges {}
12454        private ItemAnimatorListener mListener = null;
12455        private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
12456                new ArrayList<ItemAnimatorFinishedListener>();
12457
12458        private long mAddDuration = 120;
12459        private long mRemoveDuration = 120;
12460        private long mMoveDuration = 250;
12461        private long mChangeDuration = 250;
12462
12463        /**
12464         * Gets the current duration for which all move animations will run.
12465         *
12466         * @return The current move duration
12467         */
12468        public long getMoveDuration() {
12469            return mMoveDuration;
12470        }
12471
12472        /**
12473         * Sets the duration for which all move animations will run.
12474         *
12475         * @param moveDuration The move duration
12476         */
12477        public void setMoveDuration(long moveDuration) {
12478            mMoveDuration = moveDuration;
12479        }
12480
12481        /**
12482         * Gets the current duration for which all add animations will run.
12483         *
12484         * @return The current add duration
12485         */
12486        public long getAddDuration() {
12487            return mAddDuration;
12488        }
12489
12490        /**
12491         * Sets the duration for which all add animations will run.
12492         *
12493         * @param addDuration The add duration
12494         */
12495        public void setAddDuration(long addDuration) {
12496            mAddDuration = addDuration;
12497        }
12498
12499        /**
12500         * Gets the current duration for which all remove animations will run.
12501         *
12502         * @return The current remove duration
12503         */
12504        public long getRemoveDuration() {
12505            return mRemoveDuration;
12506        }
12507
12508        /**
12509         * Sets the duration for which all remove animations will run.
12510         *
12511         * @param removeDuration The remove duration
12512         */
12513        public void setRemoveDuration(long removeDuration) {
12514            mRemoveDuration = removeDuration;
12515        }
12516
12517        /**
12518         * Gets the current duration for which all change animations will run.
12519         *
12520         * @return The current change duration
12521         */
12522        public long getChangeDuration() {
12523            return mChangeDuration;
12524        }
12525
12526        /**
12527         * Sets the duration for which all change animations will run.
12528         *
12529         * @param changeDuration The change duration
12530         */
12531        public void setChangeDuration(long changeDuration) {
12532            mChangeDuration = changeDuration;
12533        }
12534
12535        /**
12536         * Internal only:
12537         * Sets the listener that must be called when the animator is finished
12538         * animating the item (or immediately if no animation happens). This is set
12539         * internally and is not intended to be set by external code.
12540         *
12541         * @param listener The listener that must be called.
12542         */
12543        void setListener(ItemAnimatorListener listener) {
12544            mListener = listener;
12545        }
12546
12547        /**
12548         * Called by the RecyclerView before the layout begins. Item animator should record
12549         * necessary information about the View before it is potentially rebound, moved or removed.
12550         * <p>
12551         * The data returned from this method will be passed to the related <code>animate**</code>
12552         * methods.
12553         * <p>
12554         * Note that this method may be called after pre-layout phase if LayoutManager adds new
12555         * Views to the layout in pre-layout pass.
12556         * <p>
12557         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
12558         * the View and the adapter change flags.
12559         *
12560         * @param state       The current State of RecyclerView which includes some useful data
12561         *                    about the layout that will be calculated.
12562         * @param viewHolder  The ViewHolder whose information should be recorded.
12563         * @param changeFlags Additional information about what changes happened in the Adapter
12564         *                    about the Item represented by this ViewHolder. For instance, if
12565         *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
12566         * @param payloads    The payload list that was previously passed to
12567         *                    {@link Adapter#notifyItemChanged(int, Object)} or
12568         *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
12569         *
12570         * @return An ItemHolderInfo instance that preserves necessary information about the
12571         * ViewHolder. This object will be passed back to related <code>animate**</code> methods
12572         * after layout is complete.
12573         *
12574         * @see #recordPostLayoutInformation(State, ViewHolder)
12575         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12576         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12577         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12578         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12579         */
12580        public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
12581                @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
12582                @NonNull List<Object> payloads) {
12583            return obtainHolderInfo().setFrom(viewHolder);
12584        }
12585
12586        /**
12587         * Called by the RecyclerView after the layout is complete. Item animator should record
12588         * necessary information about the View's final state.
12589         * <p>
12590         * The data returned from this method will be passed to the related <code>animate**</code>
12591         * methods.
12592         * <p>
12593         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
12594         * the View.
12595         *
12596         * @param state      The current State of RecyclerView which includes some useful data about
12597         *                   the layout that will be calculated.
12598         * @param viewHolder The ViewHolder whose information should be recorded.
12599         *
12600         * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
12601         * This object will be passed back to related <code>animate**</code> methods when
12602         * RecyclerView decides how items should be animated.
12603         *
12604         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12605         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12606         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12607         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12608         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12609         */
12610        public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
12611                @NonNull ViewHolder viewHolder) {
12612            return obtainHolderInfo().setFrom(viewHolder);
12613        }
12614
12615        /**
12616         * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
12617         * <p>
12618         * This means that the View was a child of the LayoutManager when layout started but has
12619         * been removed by the LayoutManager. It might have been removed from the adapter or simply
12620         * become invisible due to other factors. You can distinguish these two cases by checking
12621         * the change flags that were passed to
12622         * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12623         * <p>
12624         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
12625         * animation callback method which will be called by the RecyclerView depends on the
12626         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
12627         * LayoutManager's decision whether to layout the changed version of a disappearing
12628         * ViewHolder or not. RecyclerView will call
12629         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12630         * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
12631         * returns {@code false} from
12632         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
12633         * LayoutManager lays out a new disappearing view that holds the updated information.
12634         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
12635         * <p>
12636         * If LayoutManager supports predictive animations, it might provide a target disappear
12637         * location for the View by laying it out in that location. When that happens,
12638         * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
12639         * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
12640         * <p>
12641         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12642         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12643         * decides not to animate the view).
12644         *
12645         * @param viewHolder    The ViewHolder which should be animated
12646         * @param preLayoutInfo The information that was returned from
12647         *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12648         * @param postLayoutInfo The information that was returned from
12649         *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
12650         *                       null if the LayoutManager did not layout the item.
12651         *
12652         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12653         * false otherwise.
12654         */
12655        public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
12656                @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
12657
12658        /**
12659         * Called by the RecyclerView when a ViewHolder is added to the layout.
12660         * <p>
12661         * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
12662         * but has  been added by the LayoutManager. It might be newly added to the adapter or
12663         * simply become visible due to other factors.
12664         * <p>
12665         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12666         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12667         * decides not to animate the view).
12668         *
12669         * @param viewHolder     The ViewHolder which should be animated
12670         * @param preLayoutInfo  The information that was returned from
12671         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12672         *                       Might be null if Item was just added to the adapter or
12673         *                       LayoutManager does not support predictive animations or it could
12674         *                       not predict that this ViewHolder will become visible.
12675         * @param postLayoutInfo The information that was returned from {@link
12676         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12677         *
12678         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12679         * false otherwise.
12680         */
12681        public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
12682                @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12683
12684        /**
12685         * Called by the RecyclerView when a ViewHolder is present in both before and after the
12686         * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
12687         * for it or a {@link Adapter#notifyDataSetChanged()} call.
12688         * <p>
12689         * This ViewHolder still represents the same data that it was representing when the layout
12690         * started but its position / size may be changed by the LayoutManager.
12691         * <p>
12692         * If the Item's layout position didn't change, RecyclerView still calls this method because
12693         * it does not track this information (or does not necessarily know that an animation is
12694         * not required). Your ItemAnimator should handle this case and if there is nothing to
12695         * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
12696         * <code>false</code>.
12697         * <p>
12698         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12699         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12700         * decides not to animate the view).
12701         *
12702         * @param viewHolder     The ViewHolder which should be animated
12703         * @param preLayoutInfo  The information that was returned from
12704         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12705         * @param postLayoutInfo The information that was returned from {@link
12706         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12707         *
12708         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12709         * false otherwise.
12710         */
12711        public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
12712                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12713
12714        /**
12715         * Called by the RecyclerView when an adapter item is present both before and after the
12716         * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
12717         * for it. This method may also be called when
12718         * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
12719         * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
12720         * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
12721         * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
12722         * called for the new ViewHolder and the old one will be recycled.
12723         * <p>
12724         * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
12725         * a good possibility that item contents didn't really change but it is rebound from the
12726         * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
12727         * screen didn't change and your animator should handle this case as well and avoid creating
12728         * unnecessary animations.
12729         * <p>
12730         * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
12731         * previous presentation of the item as-is and supply a new ViewHolder for the updated
12732         * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
12733         * This is useful if you don't know the contents of the Item and would like
12734         * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
12735         * <p>
12736         * When you are writing a custom item animator for your layout, it might be more performant
12737         * and elegant to re-use the same ViewHolder and animate the content changes manually.
12738         * <p>
12739         * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
12740         * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
12741         * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
12742         * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
12743         * which represent the same Item. In that case, only the new ViewHolder is visible
12744         * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
12745         * <p>
12746         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
12747         * ViewHolder when their animation is complete
12748         * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
12749         * animate the view).
12750         * <p>
12751         *  If oldHolder and newHolder are the same instance, you should call
12752         * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
12753         * <p>
12754         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
12755         * animation callback method which will be called by the RecyclerView depends on the
12756         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
12757         * LayoutManager's decision whether to layout the changed version of a disappearing
12758         * ViewHolder or not. RecyclerView will call
12759         * {@code animateChange} instead of
12760         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12761         * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
12762         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
12763         * LayoutManager lays out a new disappearing view that holds the updated information.
12764         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
12765         *
12766         * @param oldHolder     The ViewHolder before the layout is started, might be the same
12767         *                      instance with newHolder.
12768         * @param newHolder     The ViewHolder after the layout is finished, might be the same
12769         *                      instance with oldHolder.
12770         * @param preLayoutInfo  The information that was returned from
12771         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12772         * @param postLayoutInfo The information that was returned from {@link
12773         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12774         *
12775         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12776         * false otherwise.
12777         */
12778        public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
12779                @NonNull ViewHolder newHolder,
12780                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12781
12782        @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
12783            int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
12784            if (viewHolder.isInvalid()) {
12785                return FLAG_INVALIDATED;
12786            }
12787            if ((flags & FLAG_INVALIDATED) == 0) {
12788                final int oldPos = viewHolder.getOldPosition();
12789                final int pos = viewHolder.getAdapterPosition();
12790                if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
12791                    flags |= FLAG_MOVED;
12792                }
12793            }
12794            return flags;
12795        }
12796
12797        /**
12798         * Called when there are pending animations waiting to be started. This state
12799         * is governed by the return values from
12800         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12801         * animateAppearance()},
12802         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12803         * animateChange()}
12804         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12805         * animatePersistence()}, and
12806         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12807         * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
12808         * called later to start the associated animations. runPendingAnimations() will be scheduled
12809         * to be run on the next frame.
12810         */
12811        public abstract void runPendingAnimations();
12812
12813        /**
12814         * Method called when an animation on a view should be ended immediately.
12815         * This could happen when other events, like scrolling, occur, so that
12816         * animating views can be quickly put into their proper end locations.
12817         * Implementations should ensure that any animations running on the item
12818         * are canceled and affected properties are set to their end values.
12819         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
12820         * animation since the animations are effectively done when this method is called.
12821         *
12822         * @param item The item for which an animation should be stopped.
12823         */
12824        public abstract void endAnimation(@NonNull ViewHolder item);
12825
12826        /**
12827         * Method called when all item animations should be ended immediately.
12828         * This could happen when other events, like scrolling, occur, so that
12829         * animating views can be quickly put into their proper end locations.
12830         * Implementations should ensure that any animations running on any items
12831         * are canceled and affected properties are set to their end values.
12832         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
12833         * animation since the animations are effectively done when this method is called.
12834         */
12835        public abstract void endAnimations();
12836
12837        /**
12838         * Method which returns whether there are any item animations currently running.
12839         * This method can be used to determine whether to delay other actions until
12840         * animations end.
12841         *
12842         * @return true if there are any item animations currently running, false otherwise.
12843         */
12844        public abstract boolean isRunning();
12845
12846        /**
12847         * Method to be called by subclasses when an animation is finished.
12848         * <p>
12849         * For each call RecyclerView makes to
12850         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12851         * animateAppearance()},
12852         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12853         * animatePersistence()}, or
12854         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12855         * animateDisappearance()}, there
12856         * should
12857         * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
12858         * <p>
12859         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12860         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
12861         * and <code>newHolder</code>  (if they are not the same instance).
12862         *
12863         * @param viewHolder The ViewHolder whose animation is finished.
12864         * @see #onAnimationFinished(ViewHolder)
12865         */
12866        public final void dispatchAnimationFinished(@NonNull ViewHolder viewHolder) {
12867            onAnimationFinished(viewHolder);
12868            if (mListener != null) {
12869                mListener.onAnimationFinished(viewHolder);
12870            }
12871        }
12872
12873        /**
12874         * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
12875         * ItemAnimator.
12876         *
12877         * @param viewHolder The ViewHolder whose animation is finished. There might still be other
12878         *                   animations running on this ViewHolder.
12879         * @see #dispatchAnimationFinished(ViewHolder)
12880         */
12881        public void onAnimationFinished(@NonNull ViewHolder viewHolder) {
12882        }
12883
12884        /**
12885         * Method to be called by subclasses when an animation is started.
12886         * <p>
12887         * For each call RecyclerView makes to
12888         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12889         * animateAppearance()},
12890         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12891         * animatePersistence()}, or
12892         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12893         * animateDisappearance()}, there should be a matching
12894         * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
12895         * <p>
12896         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12897         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
12898         * and <code>newHolder</code> (if they are not the same instance).
12899         * <p>
12900         * If your ItemAnimator decides not to animate a ViewHolder, it should call
12901         * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
12902         * {@link #dispatchAnimationStarted(ViewHolder)}.
12903         *
12904         * @param viewHolder The ViewHolder whose animation is starting.
12905         * @see #onAnimationStarted(ViewHolder)
12906         */
12907        public final void dispatchAnimationStarted(@NonNull ViewHolder viewHolder) {
12908            onAnimationStarted(viewHolder);
12909        }
12910
12911        /**
12912         * Called when a new animation is started on the given ViewHolder.
12913         *
12914         * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
12915         *                   might already be animating and this might be another animation.
12916         * @see #dispatchAnimationStarted(ViewHolder)
12917         */
12918        public void onAnimationStarted(@NonNull ViewHolder viewHolder) {
12919
12920        }
12921
12922        /**
12923         * Like {@link #isRunning()}, this method returns whether there are any item
12924         * animations currently running. Additionally, the listener passed in will be called
12925         * when there are no item animations running, either immediately (before the method
12926         * returns) if no animations are currently running, or when the currently running
12927         * animations are {@link #dispatchAnimationsFinished() finished}.
12928         *
12929         * <p>Note that the listener is transient - it is either called immediately and not
12930         * stored at all, or stored only until it is called when running animations
12931         * are finished sometime later.</p>
12932         *
12933         * @param listener A listener to be called immediately if no animations are running
12934         * or later when currently-running animations have finished. A null listener is
12935         * equivalent to calling {@link #isRunning()}.
12936         * @return true if there are any item animations currently running, false otherwise.
12937         */
12938        public final boolean isRunning(@Nullable ItemAnimatorFinishedListener listener) {
12939            boolean running = isRunning();
12940            if (listener != null) {
12941                if (!running) {
12942                    listener.onAnimationsFinished();
12943                } else {
12944                    mFinishedListeners.add(listener);
12945                }
12946            }
12947            return running;
12948        }
12949
12950        /**
12951         * When an item is changed, ItemAnimator can decide whether it wants to re-use
12952         * the same ViewHolder for animations or RecyclerView should create a copy of the
12953         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12954         * <p>
12955         * Note that this method will only be called if the {@link ViewHolder} still has the same
12956         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12957         * both {@link ViewHolder}s in the
12958         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12959         * <p>
12960         * If your application is using change payloads, you can override
12961         * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
12962         *
12963         * @param viewHolder The ViewHolder which represents the changed item's old content.
12964         *
12965         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
12966         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
12967         *         ItemAnimator to animate. Default implementation returns <code>true</code>.
12968         *
12969         * @see #canReuseUpdatedViewHolder(ViewHolder, List)
12970         */
12971        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
12972            return true;
12973        }
12974
12975        /**
12976         * When an item is changed, ItemAnimator can decide whether it wants to re-use
12977         * the same ViewHolder for animations or RecyclerView should create a copy of the
12978         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12979         * <p>
12980         * Note that this method will only be called if the {@link ViewHolder} still has the same
12981         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12982         * both {@link ViewHolder}s in the
12983         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12984         *
12985         * @param viewHolder The ViewHolder which represents the changed item's old content.
12986         * @param payloads A non-null list of merged payloads that were sent with change
12987         *                 notifications. Can be empty if the adapter is invalidated via
12988         *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
12989         *                 payloads will be passed into
12990         *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
12991         *                 method <b>if</b> this method returns <code>true</code>.
12992         *
12993         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
12994         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
12995         *         ItemAnimator to animate. Default implementation calls
12996         *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
12997         *
12998         * @see #canReuseUpdatedViewHolder(ViewHolder)
12999         */
13000        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
13001                @NonNull List<Object> payloads) {
13002            return canReuseUpdatedViewHolder(viewHolder);
13003        }
13004
13005        /**
13006         * This method should be called by ItemAnimator implementations to notify
13007         * any listeners that all pending and active item animations are finished.
13008         */
13009        public final void dispatchAnimationsFinished() {
13010            final int count = mFinishedListeners.size();
13011            for (int i = 0; i < count; ++i) {
13012                mFinishedListeners.get(i).onAnimationsFinished();
13013            }
13014            mFinishedListeners.clear();
13015        }
13016
13017        /**
13018         * Returns a new {@link ItemHolderInfo} which will be used to store information about the
13019         * ViewHolder. This information will later be passed into <code>animate**</code> methods.
13020         * <p>
13021         * You can override this method if you want to extend {@link ItemHolderInfo} and provide
13022         * your own instances.
13023         *
13024         * @return A new {@link ItemHolderInfo}.
13025         */
13026        @NonNull
13027        public ItemHolderInfo obtainHolderInfo() {
13028            return new ItemHolderInfo();
13029        }
13030
13031        /**
13032         * The interface to be implemented by listeners to animation events from this
13033         * ItemAnimator. This is used internally and is not intended for developers to
13034         * create directly.
13035         */
13036        interface ItemAnimatorListener {
13037            void onAnimationFinished(@NonNull ViewHolder item);
13038        }
13039
13040        /**
13041         * This interface is used to inform listeners when all pending or running animations
13042         * in an ItemAnimator are finished. This can be used, for example, to delay an action
13043         * in a data set until currently-running animations are complete.
13044         *
13045         * @see #isRunning(ItemAnimatorFinishedListener)
13046         */
13047        public interface ItemAnimatorFinishedListener {
13048            /**
13049             * Notifies when all pending or running animations in an ItemAnimator are finished.
13050             */
13051            void onAnimationsFinished();
13052        }
13053
13054        /**
13055         * A simple data structure that holds information about an item's bounds.
13056         * This information is used in calculating item animations. Default implementation of
13057         * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
13058         * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
13059         * structure. You can extend this class if you would like to keep more information about
13060         * the Views.
13061         * <p>
13062         * If you want to provide your own implementation but still use `super` methods to record
13063         * basic information, you can override {@link #obtainHolderInfo()} to provide your own
13064         * instances.
13065         */
13066        public static class ItemHolderInfo {
13067
13068            /**
13069             * The left edge of the View (excluding decorations)
13070             */
13071            public int left;
13072
13073            /**
13074             * The top edge of the View (excluding decorations)
13075             */
13076            public int top;
13077
13078            /**
13079             * The right edge of the View (excluding decorations)
13080             */
13081            public int right;
13082
13083            /**
13084             * The bottom edge of the View (excluding decorations)
13085             */
13086            public int bottom;
13087
13088            /**
13089             * The change flags that were passed to
13090             * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
13091             */
13092            @AdapterChanges
13093            public int changeFlags;
13094
13095            public ItemHolderInfo() {
13096            }
13097
13098            /**
13099             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
13100             * the given ViewHolder. Clears all {@link #changeFlags}.
13101             *
13102             * @param holder The ViewHolder whose bounds should be copied.
13103             * @return This {@link ItemHolderInfo}
13104             */
13105            @NonNull
13106            public ItemHolderInfo setFrom(@NonNull RecyclerView.ViewHolder holder) {
13107                return setFrom(holder, 0);
13108            }
13109
13110            /**
13111             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
13112             * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
13113             *
13114             * @param holder The ViewHolder whose bounds should be copied.
13115             * @param flags  The adapter change flags that were passed into
13116             *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
13117             *               List)}.
13118             * @return This {@link ItemHolderInfo}
13119             */
13120            @NonNull
13121            public ItemHolderInfo setFrom(@NonNull RecyclerView.ViewHolder holder,
13122                    @AdapterChanges int flags) {
13123                final View view = holder.itemView;
13124                this.left = view.getLeft();
13125                this.top = view.getTop();
13126                this.right = view.getRight();
13127                this.bottom = view.getBottom();
13128                return this;
13129            }
13130        }
13131    }
13132
13133    @Override
13134    protected int getChildDrawingOrder(int childCount, int i) {
13135        if (mChildDrawingOrderCallback == null) {
13136            return super.getChildDrawingOrder(childCount, i);
13137        } else {
13138            return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
13139        }
13140    }
13141
13142    /**
13143     * A callback interface that can be used to alter the drawing order of RecyclerView children.
13144     * <p>
13145     * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
13146     * that applies to that method also applies to this callback. For example, changing the drawing
13147     * order of two views will not have any effect if their elevation values are different since
13148     * elevation overrides the result of this callback.
13149     */
13150    public interface ChildDrawingOrderCallback {
13151        /**
13152         * Returns the index of the child to draw for this iteration. Override this
13153         * if you want to change the drawing order of children. By default, it
13154         * returns i.
13155         *
13156         * @param i The current iteration.
13157         * @return The index of the child to draw this iteration.
13158         *
13159         * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
13160         */
13161        int onGetChildDrawingOrder(int childCount, int i);
13162    }
13163
13164    private NestedScrollingChildHelper getScrollingChildHelper() {
13165        if (mScrollingChildHelper == null) {
13166            mScrollingChildHelper = new NestedScrollingChildHelper(this);
13167        }
13168        return mScrollingChildHelper;
13169    }
13170}
13171