RecyclerView.java revision b31c3281d870e9abb673db239234d580dcc4feff
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 androidx.annotation.CallSuper;
41import androidx.annotation.IntDef;
42import androidx.annotation.NonNull;
43import androidx.annotation.Nullable;
44import androidx.annotation.Px;
45import androidx.annotation.RestrictTo;
46import androidx.annotation.VisibleForTesting;
47import androidx.core.os.TraceCompat;
48import androidx.core.util.Preconditions;
49import androidx.customview.view.AbsSavedState;
50import androidx.core.view.InputDeviceCompat;
51import androidx.core.view.MotionEventCompat;
52import androidx.core.view.NestedScrollingChild2;
53import androidx.core.view.NestedScrollingChildHelper;
54import androidx.core.view.ScrollingView;
55import androidx.core.view.ViewCompat;
56import androidx.core.view.ViewConfigurationCompat;
57import androidx.core.view.accessibility.AccessibilityEventCompat;
58import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
59import androidx.core.widget.EdgeEffectCompat;
60import androidx.recyclerview.R;
61import androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
62import androidx.viewpager.widget.ViewPager;
63
64import android.util.AttributeSet;
65import android.util.Log;
66import android.util.SparseArray;
67import android.view.Display;
68import android.view.FocusFinder;
69import android.view.InputDevice;
70import android.view.MotionEvent;
71import android.view.VelocityTracker;
72import android.view.View;
73import android.view.ViewConfiguration;
74import android.view.ViewGroup;
75import android.view.ViewParent;
76import android.view.accessibility.AccessibilityEvent;
77import android.view.accessibility.AccessibilityManager;
78import android.view.animation.Interpolator;
79import android.widget.EdgeEffect;
80import android.widget.LinearLayout;
81import android.widget.OverScroller;
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 * A flexible view for providing a limited window into a large data set.
94 *
95 * <h3>Glossary of terms:</h3>
96 *
97 * <ul>
98 *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
99 *     that represent items in a data set.</li>
100 *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
101 *     <li><em>Index:</em> The index of an attached child view as used in a call to
102 *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
103 *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
104 *     to a <em>position</em> within the adapter.</li>
105 *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
106 *     position may be placed in a cache for later reuse to display the same type of data again
107 *     later. This can drastically improve performance by skipping initial layout inflation
108 *     or construction.</li>
109 *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
110 *     state during layout. Scrap views may be reused without becoming fully detached
111 *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
112 *     by the adapter if the view was considered <em>dirty</em>.</li>
113 *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
114 *     being displayed.</li>
115 * </ul>
116 *
117 * <h4>Positions in RecyclerView:</h4>
118 * <p>
119 * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
120 * {@link LayoutManager} to be able to detect data set changes in batches during a layout
121 * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
122 * It also helps with performance because all view bindings happen at the same time and unnecessary
123 * bindings are avoided.
124 * <p>
125 * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
126 * <ul>
127 *     <li>layout position: Position of an item in the latest layout calculation. This is the
128 *     position from the LayoutManager's perspective.</li>
129 *     <li>adapter position: Position of an item in the adapter. This is the position from
130 *     the Adapter's perspective.</li>
131 * </ul>
132 * <p>
133 * These two positions are the same except the time between dispatching <code>adapter.notify*
134 * </code> events and calculating the updated layout.
135 * <p>
136 * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
137 * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
138 * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
139 * last layout calculation. You can rely on these positions to be consistent with what user is
140 * currently seeing on the screen. For example, if you have a list of items on the screen and user
141 * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
142 * is seeing.
143 * <p>
144 * The other set of position related methods are in the form of
145 * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
146 * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
147 * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
148 * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
149 * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
150 * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
151 * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
152 * <code>null</code> results from these methods.
153 * <p>
154 * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
155 * writing an {@link Adapter}, you probably want to use adapter positions.
156 *
157 * @attr ref androidx.recyclerview.R.styleable#RecyclerView_layoutManager
158 */
159public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild2 {
160
161    static final String TAG = "RecyclerView";
162
163    static final boolean DEBUG = false;
164
165    static final boolean VERBOSE_TRACING = false;
166
167    private static final int[]  NESTED_SCROLLING_ATTRS =
168            {16843830 /* android.R.attr.nestedScrollingEnabled */};
169
170    private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
171
172    /**
173     * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
174     * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
175     * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
176     * recursively traverses itemView and invalidates display list for each ViewGroup that matches
177     * this criteria.
178     */
179    static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
180            || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
181    /**
182     * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
183     * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
184     * 0 when mode is unspecified.
185     */
186    static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
187
188    static final boolean POST_UPDATES_ON_ANIMATION = Build.VERSION.SDK_INT >= 16;
189
190    /**
191     * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
192     * RenderThread but before the next frame begins. We schedule prefetch work in this window.
193     */
194    private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
195
196    /**
197     * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
198     * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
199     */
200    private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
201
202    /**
203     * on API 15-, a focused child can still be considered a focused child of RV even after
204     * it's being removed or its focusable flag is set to false. This is because when this focused
205     * child is detached, the reference to this child is not removed in clearFocus. API 16 and above
206     * properly handle this case by calling ensureInputFocusOnFirstFocusable or rootViewRequestFocus
207     * to request focus on a new child, which will clear the focus on the old (detached) child as a
208     * side-effect.
209     */
210    private static final boolean IGNORE_DETACHED_FOCUSED_CHILD = Build.VERSION.SDK_INT <= 15;
211
212    static final boolean DISPATCH_TEMP_DETACH = false;
213
214    /** @hide */
215    @RestrictTo(LIBRARY_GROUP)
216    @IntDef({HORIZONTAL, VERTICAL})
217    @Retention(RetentionPolicy.SOURCE)
218    public @interface Orientation {}
219
220    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
221    public static final int VERTICAL = LinearLayout.VERTICAL;
222
223    static final int DEFAULT_ORIENTATION = VERTICAL;
224    public static final int NO_POSITION = -1;
225    public static final long NO_ID = -1;
226    public static final int INVALID_TYPE = -1;
227
228    /**
229     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
230     * that the RecyclerView should use the standard touch slop for smooth,
231     * continuous scrolling.
232     */
233    public static final int TOUCH_SLOP_DEFAULT = 0;
234
235    /**
236     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
237     * that the RecyclerView should use the standard touch slop for scrolling
238     * widgets that snap to a page or other coarse-grained barrier.
239     */
240    public static final int TOUCH_SLOP_PAGING = 1;
241
242    static final int MAX_SCROLL_DURATION = 2000;
243
244    /**
245     * RecyclerView is calculating a scroll.
246     * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
247     * it. Try to avoid using EditText, focusable views or handle them with care.
248     */
249    static final String TRACE_SCROLL_TAG = "RV Scroll";
250
251    /**
252     * OnLayout has been called by the View system.
253     * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
254     * update themselves directly. This will cause a full re-layout but when it happens via the
255     * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
256     */
257    private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
258
259    /**
260     * NotifyDataSetChanged or equal has been called.
261     * If this is taking a long time, try sending granular notify adapter changes instead of just
262     * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
263     * might help.
264     */
265    private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
266
267    /**
268     * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
269     * If this is taking a long time, you may have dispatched too many Adapter updates causing too
270     * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
271     * methods.
272     */
273    private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
274
275    /**
276     * RecyclerView is rebinding a View.
277     * If this is taking a lot of time, consider optimizing your layout or make sure you are not
278     * doing extra operations in onBindViewHolder call.
279     */
280    static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
281
282    /**
283     * RecyclerView is attempting to pre-populate off screen views.
284     */
285    static final String TRACE_PREFETCH_TAG = "RV Prefetch";
286
287    /**
288     * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
289     * RecyclerView.
290     */
291    static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
292
293    /**
294     * RecyclerView is creating a new View.
295     * If too many of these present in Systrace:
296     * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
297     * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
298     * > Adapter#onFailedToRecycleView(ViewHolder)})
299     *
300     * - There might be too many item view types.
301     * > Try merging them
302     *
303     * - There might be too many itemChange animations and not enough space in RecyclerPool.
304     * >Try increasing your pool size and item cache size.
305     */
306    static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
307    private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
308            new Class[]{Context.class, AttributeSet.class, int.class, int.class};
309
310    private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
311
312    final Recycler mRecycler = new Recycler();
313
314    private SavedState mPendingSavedState;
315
316    /**
317     * Handles adapter updates
318     */
319    AdapterHelper mAdapterHelper;
320
321    /**
322     * Handles abstraction between LayoutManager children and RecyclerView children
323     */
324    ChildHelper mChildHelper;
325
326    /**
327     * Keeps data about views to be used for animations
328     */
329    final ViewInfoStore mViewInfoStore = new ViewInfoStore();
330
331    /**
332     * Prior to L, there is no way to query this variable which is why we override the setter and
333     * track it here.
334     */
335    boolean mClipToPadding;
336
337    /**
338     * Note: this Runnable is only ever posted if:
339     * 1) We've been through first layout
340     * 2) We know we have a fixed size (mHasFixedSize)
341     * 3) We're attached
342     */
343    final Runnable mUpdateChildViewsRunnable = new Runnable() {
344        @Override
345        public void run() {
346            if (!mFirstLayoutComplete || isLayoutRequested()) {
347                // a layout request will happen, we should not do layout here.
348                return;
349            }
350            if (!mIsAttached) {
351                requestLayout();
352                // if we are not attached yet, mark us as requiring layout and skip
353                return;
354            }
355            if (mLayoutFrozen) {
356                mLayoutWasDefered = true;
357                return; //we'll process updates when ice age ends.
358            }
359            consumePendingUpdateOperations();
360        }
361    };
362
363    final Rect mTempRect = new Rect();
364    private final Rect mTempRect2 = new Rect();
365    final RectF mTempRectF = new RectF();
366    Adapter mAdapter;
367    @VisibleForTesting LayoutManager mLayout;
368    RecyclerListener mRecyclerListener;
369    final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
370    private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
371            new ArrayList<>();
372    private OnItemTouchListener mActiveOnItemTouchListener;
373    boolean mIsAttached;
374    boolean mHasFixedSize;
375    boolean mEnableFastScroller;
376    @VisibleForTesting boolean mFirstLayoutComplete;
377
378    /**
379     * The current depth of nested calls to {@link #startInterceptRequestLayout()} (number of
380     * calls to {@link #startInterceptRequestLayout()} - number of calls to
381     * {@link #stopInterceptRequestLayout(boolean)} .  This is used to signal whether we
382     * should defer layout operations caused by layout requests from children of
383     * {@link RecyclerView}.
384     */
385    private int mInterceptRequestLayoutDepth = 0;
386
387    /**
388     * True if a call to requestLayout was intercepted and prevented from executing like normal and
389     * we plan on continuing with normal execution later.
390     */
391    boolean mLayoutWasDefered;
392
393    boolean mLayoutFrozen;
394    private boolean mIgnoreMotionEventTillDown;
395
396    // binary OR of change events that were eaten during a layout or scroll.
397    private int mEatenAccessibilityChangeFlags;
398    boolean mAdapterUpdateDuringMeasure;
399
400    private final AccessibilityManager mAccessibilityManager;
401    private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
402
403    /**
404     * True after an event occurs that signals that the entire data set has changed. In that case,
405     * we cannot run any animations since we don't know what happened until layout.
406     *
407     * Attached items are invalid until next layout, at which point layout will animate/replace
408     * items as necessary, building up content from the (effectively) new adapter from scratch.
409     *
410     * Cached items must be discarded when setting this to true, so that the cache may be freely
411     * used by prefetching until the next layout occurs.
412     *
413     * @see #processDataSetCompletelyChanged(boolean)
414     */
415    boolean mDataSetHasChangedAfterLayout = false;
416
417    /**
418     * True after the data set has completely changed and
419     * {@link LayoutManager#onItemsChanged(RecyclerView)} should be called during the subsequent
420     * measure/layout.
421     *
422     * @see #processDataSetCompletelyChanged(boolean)
423     */
424    boolean mDispatchItemsChangedEvent = false;
425
426    /**
427     * This variable is incremented during a dispatchLayout and/or scroll.
428     * Some methods should not be called during these periods (e.g. adapter data change).
429     * Doing so will create hard to find bugs so we better check it and throw an exception.
430     *
431     * @see #assertInLayoutOrScroll(String)
432     * @see #assertNotInLayoutOrScroll(String)
433     */
434    private int mLayoutOrScrollCounter = 0;
435
436    /**
437     * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
438     * (for API compatibility).
439     * <p>
440     * It is a bad practice for a developer to update the data in a scroll callback since it is
441     * potentially called during a layout.
442     */
443    private int mDispatchScrollCounter = 0;
444
445    @NonNull
446    private EdgeEffectFactory mEdgeEffectFactory = new EdgeEffectFactory();
447    private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
448
449    ItemAnimator mItemAnimator = new DefaultItemAnimator();
450
451    private static final int INVALID_POINTER = -1;
452
453    /**
454     * The RecyclerView is not currently scrolling.
455     * @see #getScrollState()
456     */
457    public static final int SCROLL_STATE_IDLE = 0;
458
459    /**
460     * The RecyclerView is currently being dragged by outside input such as user touch input.
461     * @see #getScrollState()
462     */
463    public static final int SCROLL_STATE_DRAGGING = 1;
464
465    /**
466     * The RecyclerView is currently animating to a final position while not under
467     * outside control.
468     * @see #getScrollState()
469     */
470    public static final int SCROLL_STATE_SETTLING = 2;
471
472    static final long FOREVER_NS = Long.MAX_VALUE;
473
474    // Touch/scrolling handling
475
476    private int mScrollState = SCROLL_STATE_IDLE;
477    private int mScrollPointerId = INVALID_POINTER;
478    private VelocityTracker mVelocityTracker;
479    private int mInitialTouchX;
480    private int mInitialTouchY;
481    private int mLastTouchX;
482    private int mLastTouchY;
483    private int mTouchSlop;
484    private OnFlingListener mOnFlingListener;
485    private final int mMinFlingVelocity;
486    private final int mMaxFlingVelocity;
487
488    // This value is used when handling rotary encoder generic motion events.
489    private float mScaledHorizontalScrollFactor = Float.MIN_VALUE;
490    private float mScaledVerticalScrollFactor = Float.MIN_VALUE;
491
492    private boolean mPreserveFocusAfterLayout = true;
493
494    final ViewFlinger mViewFlinger = new ViewFlinger();
495
496    GapWorker mGapWorker;
497    GapWorker.LayoutPrefetchRegistryImpl mPrefetchRegistry =
498            ALLOW_THREAD_GAP_WORK ? new GapWorker.LayoutPrefetchRegistryImpl() : null;
499
500    final State mState = new State();
501
502    private OnScrollListener mScrollListener;
503    private List<OnScrollListener> mScrollListeners;
504
505    // For use in item animations
506    boolean mItemsAddedOrRemoved = false;
507    boolean mItemsChanged = false;
508    private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
509            new ItemAnimatorRestoreListener();
510    boolean mPostedAnimatorRunner = false;
511    RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
512    private ChildDrawingOrderCallback mChildDrawingOrderCallback;
513
514    // simple array to keep min and max child position during a layout calculation
515    // preserved not to create a new one in each layout pass
516    private final int[] mMinMaxLayoutPositions = new int[2];
517
518    private NestedScrollingChildHelper mScrollingChildHelper;
519    private final int[] mScrollOffset = new int[2];
520    private final int[] mScrollConsumed = new int[2];
521    private final int[] mNestedOffsets = new int[2];
522
523    /**
524     * Reusable int array for use in calls to {@link #scrollStep(int, int, int[])} so that the
525     * method may mutate it to "return" 2 ints.
526     */
527    private final int[] mScrollStepConsumed = new int[2];
528
529    /**
530     * These are views that had their a11y importance changed during a layout. We defer these events
531     * until the end of the layout because a11y service may make sync calls back to the RV while
532     * the View's state is undefined.
533     */
534    @VisibleForTesting
535    final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList<>();
536
537    private Runnable mItemAnimatorRunner = new Runnable() {
538        @Override
539        public void run() {
540            if (mItemAnimator != null) {
541                mItemAnimator.runPendingAnimations();
542            }
543            mPostedAnimatorRunner = false;
544        }
545    };
546
547    static final Interpolator sQuinticInterpolator = new Interpolator() {
548        @Override
549        public float getInterpolation(float t) {
550            t -= 1.0f;
551            return t * t * t * t * t + 1.0f;
552        }
553    };
554
555    /**
556     * The callback to convert view info diffs into animations.
557     */
558    private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
559            new ViewInfoStore.ProcessCallback() {
560                @Override
561                public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
562                        @Nullable ItemHolderInfo postInfo) {
563                    mRecycler.unscrapView(viewHolder);
564                    animateDisappearance(viewHolder, info, postInfo);
565                }
566                @Override
567                public void processAppeared(ViewHolder viewHolder,
568                        ItemHolderInfo preInfo, ItemHolderInfo info) {
569                    animateAppearance(viewHolder, preInfo, info);
570                }
571
572                @Override
573                public void processPersistent(ViewHolder viewHolder,
574                        @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
575                    viewHolder.setIsRecyclable(false);
576                    if (mDataSetHasChangedAfterLayout) {
577                        // since it was rebound, use change instead as we'll be mapping them from
578                        // stable ids. If stable ids were false, we would not be running any
579                        // animations
580                        if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo,
581                                postInfo)) {
582                            postAnimationRunner();
583                        }
584                    } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
585                        postAnimationRunner();
586                    }
587                }
588                @Override
589                public void unused(ViewHolder viewHolder) {
590                    mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
591                }
592            };
593
594    public RecyclerView(@NonNull Context context) {
595        this(context, null);
596    }
597
598    public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
599        this(context, attrs, 0);
600    }
601
602    public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
603        super(context, attrs, defStyle);
604        if (attrs != null) {
605            TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
606            mClipToPadding = a.getBoolean(0, true);
607            a.recycle();
608        } else {
609            mClipToPadding = true;
610        }
611        setScrollContainer(true);
612        setFocusableInTouchMode(true);
613
614        final ViewConfiguration vc = ViewConfiguration.get(context);
615        mTouchSlop = vc.getScaledTouchSlop();
616        mScaledHorizontalScrollFactor =
617                ViewConfigurationCompat.getScaledHorizontalScrollFactor(vc, context);
618        mScaledVerticalScrollFactor =
619                ViewConfigurationCompat.getScaledVerticalScrollFactor(vc, context);
620        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
621        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
622        setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
623
624        mItemAnimator.setListener(mItemAnimatorListener);
625        initAdapterManager();
626        initChildrenHelper();
627        // If not explicitly specified this view is important for accessibility.
628        if (ViewCompat.getImportantForAccessibility(this)
629                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
630            ViewCompat.setImportantForAccessibility(this,
631                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
632        }
633        mAccessibilityManager = (AccessibilityManager) getContext()
634                .getSystemService(Context.ACCESSIBILITY_SERVICE);
635        setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
636        // Create the layoutManager if specified.
637
638        boolean nestedScrollingEnabled = true;
639
640        if (attrs != null) {
641            int defStyleRes = 0;
642            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
643                    defStyle, defStyleRes);
644            String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
645            int descendantFocusability = a.getInt(
646                    R.styleable.RecyclerView_android_descendantFocusability, -1);
647            if (descendantFocusability == -1) {
648                setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
649            }
650            mEnableFastScroller = a.getBoolean(R.styleable.RecyclerView_fastScrollEnabled, false);
651            if (mEnableFastScroller) {
652                StateListDrawable verticalThumbDrawable = (StateListDrawable) a
653                        .getDrawable(R.styleable.RecyclerView_fastScrollVerticalThumbDrawable);
654                Drawable verticalTrackDrawable = a
655                        .getDrawable(R.styleable.RecyclerView_fastScrollVerticalTrackDrawable);
656                StateListDrawable horizontalThumbDrawable = (StateListDrawable) a
657                        .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalThumbDrawable);
658                Drawable horizontalTrackDrawable = a
659                        .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalTrackDrawable);
660                initFastScroller(verticalThumbDrawable, verticalTrackDrawable,
661                        horizontalThumbDrawable, horizontalTrackDrawable);
662            }
663            a.recycle();
664            createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
665
666            if (Build.VERSION.SDK_INT >= 21) {
667                a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
668                        defStyle, defStyleRes);
669                nestedScrollingEnabled = a.getBoolean(0, true);
670                a.recycle();
671            }
672        } else {
673            setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
674        }
675
676        // Re-set whether nested scrolling is enabled so that it is set on all API levels
677        setNestedScrollingEnabled(nestedScrollingEnabled);
678    }
679
680    /**
681     * Label appended to all public exception strings, used to help find which RV in an app is
682     * hitting an exception.
683     */
684    String exceptionLabel() {
685        return " " + super.toString()
686                + ", adapter:" + mAdapter
687                + ", layout:" + mLayout
688                + ", context:" + getContext();
689    }
690
691    /**
692     * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
693     * @return An instance of AccessibilityDelegateCompat used by RecyclerView
694     */
695    @Nullable
696    public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
697        return mAccessibilityDelegate;
698    }
699
700    /**
701     * Sets the accessibility delegate compatibility implementation used by RecyclerView.
702     * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
703     */
704    public void setAccessibilityDelegateCompat(
705            @Nullable RecyclerViewAccessibilityDelegate accessibilityDelegate) {
706        mAccessibilityDelegate = accessibilityDelegate;
707        ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
708    }
709
710    /**
711     * Instantiate and set a LayoutManager, if specified in the attributes.
712     */
713    private void createLayoutManager(Context context, String className, AttributeSet attrs,
714            int defStyleAttr, int defStyleRes) {
715        if (className != null) {
716            className = className.trim();
717            if (!className.isEmpty()) {
718                className = getFullClassName(context, className);
719                try {
720                    ClassLoader classLoader;
721                    if (isInEditMode()) {
722                        // Stupid layoutlib cannot handle simple class loaders.
723                        classLoader = this.getClass().getClassLoader();
724                    } else {
725                        classLoader = context.getClassLoader();
726                    }
727                    Class<? extends LayoutManager> layoutManagerClass =
728                            classLoader.loadClass(className).asSubclass(LayoutManager.class);
729                    Constructor<? extends LayoutManager> constructor;
730                    Object[] constructorArgs = null;
731                    try {
732                        constructor = layoutManagerClass
733                                .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
734                        constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
735                    } catch (NoSuchMethodException e) {
736                        try {
737                            constructor = layoutManagerClass.getConstructor();
738                        } catch (NoSuchMethodException e1) {
739                            e1.initCause(e);
740                            throw new IllegalStateException(attrs.getPositionDescription()
741                                    + ": Error creating LayoutManager " + className, e1);
742                        }
743                    }
744                    constructor.setAccessible(true);
745                    setLayoutManager(constructor.newInstance(constructorArgs));
746                } catch (ClassNotFoundException e) {
747                    throw new IllegalStateException(attrs.getPositionDescription()
748                            + ": Unable to find LayoutManager " + className, e);
749                } catch (InvocationTargetException e) {
750                    throw new IllegalStateException(attrs.getPositionDescription()
751                            + ": Could not instantiate the LayoutManager: " + className, e);
752                } catch (InstantiationException e) {
753                    throw new IllegalStateException(attrs.getPositionDescription()
754                            + ": Could not instantiate the LayoutManager: " + className, e);
755                } catch (IllegalAccessException e) {
756                    throw new IllegalStateException(attrs.getPositionDescription()
757                            + ": Cannot access non-public constructor " + className, e);
758                } catch (ClassCastException e) {
759                    throw new IllegalStateException(attrs.getPositionDescription()
760                            + ": Class is not a LayoutManager " + className, e);
761                }
762            }
763        }
764    }
765
766    private String getFullClassName(Context context, String className) {
767        if (className.charAt(0) == '.') {
768            return context.getPackageName() + className;
769        }
770        if (className.contains(".")) {
771            return className;
772        }
773        return RecyclerView.class.getPackage().getName() + '.' + className;
774    }
775
776    private void initChildrenHelper() {
777        mChildHelper = new ChildHelper(new ChildHelper.Callback() {
778            @Override
779            public int getChildCount() {
780                return RecyclerView.this.getChildCount();
781            }
782
783            @Override
784            public void addView(View child, int index) {
785                if (VERBOSE_TRACING) {
786                    TraceCompat.beginSection("RV addView");
787                }
788                RecyclerView.this.addView(child, index);
789                if (VERBOSE_TRACING) {
790                    TraceCompat.endSection();
791                }
792                dispatchChildAttached(child);
793            }
794
795            @Override
796            public int indexOfChild(View view) {
797                return RecyclerView.this.indexOfChild(view);
798            }
799
800            @Override
801            public void removeViewAt(int index) {
802                final View child = RecyclerView.this.getChildAt(index);
803                if (child != null) {
804                    dispatchChildDetached(child);
805
806                    // Clear any android.view.animation.Animation that may prevent the item from
807                    // detaching when being removed. If a child is re-added before the
808                    // lazy detach occurs, it will receive invalid attach/detach sequencing.
809                    child.clearAnimation();
810                }
811                if (VERBOSE_TRACING) {
812                    TraceCompat.beginSection("RV removeViewAt");
813                }
814                RecyclerView.this.removeViewAt(index);
815                if (VERBOSE_TRACING) {
816                    TraceCompat.endSection();
817                }
818            }
819
820            @Override
821            public View getChildAt(int offset) {
822                return RecyclerView.this.getChildAt(offset);
823            }
824
825            @Override
826            public void removeAllViews() {
827                final int count = getChildCount();
828                for (int i = 0; i < count; i++) {
829                    View child = getChildAt(i);
830                    dispatchChildDetached(child);
831
832                    // Clear any android.view.animation.Animation that may prevent the item from
833                    // detaching when being removed. If a child is re-added before the
834                    // lazy detach occurs, it will receive invalid attach/detach sequencing.
835                    child.clearAnimation();
836                }
837                RecyclerView.this.removeAllViews();
838            }
839
840            @Override
841            public ViewHolder getChildViewHolder(View view) {
842                return getChildViewHolderInt(view);
843            }
844
845            @Override
846            public void attachViewToParent(View child, int index,
847                    ViewGroup.LayoutParams layoutParams) {
848                final ViewHolder vh = getChildViewHolderInt(child);
849                if (vh != null) {
850                    if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
851                        throw new IllegalArgumentException("Called attach on a child which is not"
852                                + " detached: " + vh + exceptionLabel());
853                    }
854                    if (DEBUG) {
855                        Log.d(TAG, "reAttach " + vh);
856                    }
857                    vh.clearTmpDetachFlag();
858                }
859                RecyclerView.this.attachViewToParent(child, index, layoutParams);
860            }
861
862            @Override
863            public void detachViewFromParent(int offset) {
864                final View view = getChildAt(offset);
865                if (view != null) {
866                    final ViewHolder vh = getChildViewHolderInt(view);
867                    if (vh != null) {
868                        if (vh.isTmpDetached() && !vh.shouldIgnore()) {
869                            throw new IllegalArgumentException("called detach on an already"
870                                    + " detached child " + vh + exceptionLabel());
871                        }
872                        if (DEBUG) {
873                            Log.d(TAG, "tmpDetach " + vh);
874                        }
875                        vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
876                    }
877                }
878                RecyclerView.this.detachViewFromParent(offset);
879            }
880
881            @Override
882            public void onEnteredHiddenState(View child) {
883                final ViewHolder vh = getChildViewHolderInt(child);
884                if (vh != null) {
885                    vh.onEnteredHiddenState(RecyclerView.this);
886                }
887            }
888
889            @Override
890            public void onLeftHiddenState(View child) {
891                final ViewHolder vh = getChildViewHolderInt(child);
892                if (vh != null) {
893                    vh.onLeftHiddenState(RecyclerView.this);
894                }
895            }
896        });
897    }
898
899    void initAdapterManager() {
900        mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
901            @Override
902            public ViewHolder findViewHolder(int position) {
903                final ViewHolder vh = findViewHolderForPosition(position, true);
904                if (vh == null) {
905                    return null;
906                }
907                // ensure it is not hidden because for adapter helper, the only thing matter is that
908                // LM thinks view is a child.
909                if (mChildHelper.isHidden(vh.itemView)) {
910                    if (DEBUG) {
911                        Log.d(TAG, "assuming view holder cannot be find because it is hidden");
912                    }
913                    return null;
914                }
915                return vh;
916            }
917
918            @Override
919            public void offsetPositionsForRemovingInvisible(int start, int count) {
920                offsetPositionRecordsForRemove(start, count, true);
921                mItemsAddedOrRemoved = true;
922                mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
923            }
924
925            @Override
926            public void offsetPositionsForRemovingLaidOutOrNewView(
927                    int positionStart, int itemCount) {
928                offsetPositionRecordsForRemove(positionStart, itemCount, false);
929                mItemsAddedOrRemoved = true;
930            }
931
932
933            @Override
934            public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
935                viewRangeUpdate(positionStart, itemCount, payload);
936                mItemsChanged = true;
937            }
938
939            @Override
940            public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
941                dispatchUpdate(op);
942            }
943
944            void dispatchUpdate(AdapterHelper.UpdateOp op) {
945                switch (op.cmd) {
946                    case AdapterHelper.UpdateOp.ADD:
947                        mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
948                        break;
949                    case AdapterHelper.UpdateOp.REMOVE:
950                        mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
951                        break;
952                    case AdapterHelper.UpdateOp.UPDATE:
953                        mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
954                                op.payload);
955                        break;
956                    case AdapterHelper.UpdateOp.MOVE:
957                        mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
958                        break;
959                }
960            }
961
962            @Override
963            public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
964                dispatchUpdate(op);
965            }
966
967            @Override
968            public void offsetPositionsForAdd(int positionStart, int itemCount) {
969                offsetPositionRecordsForInsert(positionStart, itemCount);
970                mItemsAddedOrRemoved = true;
971            }
972
973            @Override
974            public void offsetPositionsForMove(int from, int to) {
975                offsetPositionRecordsForMove(from, to);
976                // should we create mItemsMoved ?
977                mItemsAddedOrRemoved = true;
978            }
979        });
980    }
981
982    /**
983     * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
984     * size is not affected by the adapter contents. RecyclerView can still change its size based
985     * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
986     * size of its children or contents of its adapter (except the number of items in the adapter).
987     * <p>
988     * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
989     * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
990     *
991     * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
992     */
993    public void setHasFixedSize(boolean hasFixedSize) {
994        mHasFixedSize = hasFixedSize;
995    }
996
997    /**
998     * @return true if the app has specified that changes in adapter content cannot change
999     * the size of the RecyclerView itself.
1000     */
1001    public boolean hasFixedSize() {
1002        return mHasFixedSize;
1003    }
1004
1005    @Override
1006    public void setClipToPadding(boolean clipToPadding) {
1007        if (clipToPadding != mClipToPadding) {
1008            invalidateGlows();
1009        }
1010        mClipToPadding = clipToPadding;
1011        super.setClipToPadding(clipToPadding);
1012        if (mFirstLayoutComplete) {
1013            requestLayout();
1014        }
1015    }
1016
1017    /**
1018     * Returns whether this RecyclerView will clip its children to its padding, and resize (but
1019     * not clip) any EdgeEffect to the padded region, if padding is present.
1020     * <p>
1021     * By default, children are clipped to the padding of their parent
1022     * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
1023     *
1024     * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
1025     *         clip) any EdgeEffect to the padded region, false otherwise.
1026     *
1027     * @attr name android:clipToPadding
1028     */
1029    @Override
1030    public boolean getClipToPadding() {
1031        return mClipToPadding;
1032    }
1033
1034    /**
1035     * Configure the scrolling touch slop for a specific use case.
1036     *
1037     * Set up the RecyclerView's scrolling motion threshold based on common usages.
1038     * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
1039     *
1040     * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
1041     *                     the intended usage of this RecyclerView
1042     */
1043    public void setScrollingTouchSlop(int slopConstant) {
1044        final ViewConfiguration vc = ViewConfiguration.get(getContext());
1045        switch (slopConstant) {
1046            default:
1047                Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
1048                        + slopConstant + "; using default value");
1049                // fall-through
1050            case TOUCH_SLOP_DEFAULT:
1051                mTouchSlop = vc.getScaledTouchSlop();
1052                break;
1053
1054            case TOUCH_SLOP_PAGING:
1055                mTouchSlop = vc.getScaledPagingTouchSlop();
1056                break;
1057        }
1058    }
1059
1060    /**
1061     * Swaps the current adapter with the provided one. It is similar to
1062     * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
1063     * {@link ViewHolder} and does not clear the RecycledViewPool.
1064     * <p>
1065     * Note that it still calls onAdapterChanged callbacks.
1066     *
1067     * @param adapter The new adapter to set, or null to set no adapter.
1068     * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
1069     *                                      Views. If adapters have stable ids and/or you want to
1070     *                                      animate the disappearing views, you may prefer to set
1071     *                                      this to false.
1072     * @see #setAdapter(Adapter)
1073     */
1074    public void swapAdapter(@Nullable Adapter adapter, boolean removeAndRecycleExistingViews) {
1075        // bail out if layout is frozen
1076        setLayoutFrozen(false);
1077        setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
1078        processDataSetCompletelyChanged(true);
1079        requestLayout();
1080    }
1081    /**
1082     * Set a new adapter to provide child views on demand.
1083     * <p>
1084     * When adapter is changed, all existing views are recycled back to the pool. If the pool has
1085     * only one adapter, it will be cleared.
1086     *
1087     * @param adapter The new adapter to set, or null to set no adapter.
1088     * @see #swapAdapter(Adapter, boolean)
1089     */
1090    public void setAdapter(@Nullable Adapter adapter) {
1091        // bail out if layout is frozen
1092        setLayoutFrozen(false);
1093        setAdapterInternal(adapter, false, true);
1094        processDataSetCompletelyChanged(false);
1095        requestLayout();
1096    }
1097
1098    /**
1099     * Removes and recycles all views - both those currently attached, and those in the Recycler.
1100     */
1101    void removeAndRecycleViews() {
1102        // end all running animations
1103        if (mItemAnimator != null) {
1104            mItemAnimator.endAnimations();
1105        }
1106        // Since animations are ended, mLayout.children should be equal to
1107        // recyclerView.children. This may not be true if item animator's end does not work as
1108        // expected. (e.g. not release children instantly). It is safer to use mLayout's child
1109        // count.
1110        if (mLayout != null) {
1111            mLayout.removeAndRecycleAllViews(mRecycler);
1112            mLayout.removeAndRecycleScrapInt(mRecycler);
1113        }
1114        // we should clear it here before adapters are swapped to ensure correct callbacks.
1115        mRecycler.clear();
1116    }
1117
1118    /**
1119     * Replaces the current adapter with the new one and triggers listeners.
1120     * @param adapter The new adapter
1121     * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
1122     *                               item types with the current adapter (helps us avoid cache
1123     *                               invalidation).
1124     * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
1125     *                               compatibleWithPrevious is false, this parameter is ignored.
1126     */
1127    private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
1128            boolean removeAndRecycleViews) {
1129        if (mAdapter != null) {
1130            mAdapter.unregisterAdapterDataObserver(mObserver);
1131            mAdapter.onDetachedFromRecyclerView(this);
1132        }
1133        if (!compatibleWithPrevious || removeAndRecycleViews) {
1134            removeAndRecycleViews();
1135        }
1136        mAdapterHelper.reset();
1137        final Adapter oldAdapter = mAdapter;
1138        mAdapter = adapter;
1139        if (adapter != null) {
1140            adapter.registerAdapterDataObserver(mObserver);
1141            adapter.onAttachedToRecyclerView(this);
1142        }
1143        if (mLayout != null) {
1144            mLayout.onAdapterChanged(oldAdapter, mAdapter);
1145        }
1146        mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
1147        mState.mStructureChanged = true;
1148    }
1149
1150    /**
1151     * Retrieves the previously set adapter or null if no adapter is set.
1152     *
1153     * @return The previously set adapter
1154     * @see #setAdapter(Adapter)
1155     */
1156    @Nullable
1157    public Adapter getAdapter() {
1158        return mAdapter;
1159    }
1160
1161    /**
1162     * Register a listener that will be notified whenever a child view is recycled.
1163     *
1164     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1165     * that a child view is no longer needed. If an application associates expensive
1166     * or heavyweight data with item views, this may be a good place to release
1167     * or free those resources.</p>
1168     *
1169     * @param listener Listener to register, or null to clear
1170     */
1171    public void setRecyclerListener(@Nullable RecyclerListener listener) {
1172        mRecyclerListener = listener;
1173    }
1174
1175    /**
1176     * <p>Return the offset of the RecyclerView's text baseline from the its top
1177     * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
1178     * this method returns -1.</p>
1179     *
1180     * @return the offset of the baseline within the RecyclerView's bounds or -1
1181     *         if baseline alignment is not supported
1182     */
1183    @Override
1184    public int getBaseline() {
1185        if (mLayout != null) {
1186            return mLayout.getBaseline();
1187        } else {
1188            return super.getBaseline();
1189        }
1190    }
1191
1192    /**
1193     * Register a listener that will be notified whenever a child view is attached to or detached
1194     * from RecyclerView.
1195     *
1196     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1197     * that a child view is no longer needed. If an application associates expensive
1198     * or heavyweight data with item views, this may be a good place to release
1199     * or free those resources.</p>
1200     *
1201     * @param listener Listener to register
1202     */
1203    public void addOnChildAttachStateChangeListener(
1204            @NonNull OnChildAttachStateChangeListener listener) {
1205        if (mOnChildAttachStateListeners == null) {
1206            mOnChildAttachStateListeners = new ArrayList<>();
1207        }
1208        mOnChildAttachStateListeners.add(listener);
1209    }
1210
1211    /**
1212     * Removes the provided listener from child attached state listeners list.
1213     *
1214     * @param listener Listener to unregister
1215     */
1216    public void removeOnChildAttachStateChangeListener(
1217            @NonNull OnChildAttachStateChangeListener listener) {
1218        if (mOnChildAttachStateListeners == null) {
1219            return;
1220        }
1221        mOnChildAttachStateListeners.remove(listener);
1222    }
1223
1224    /**
1225     * Removes all listeners that were added via
1226     * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
1227     */
1228    public void clearOnChildAttachStateChangeListeners() {
1229        if (mOnChildAttachStateListeners != null) {
1230            mOnChildAttachStateListeners.clear();
1231        }
1232    }
1233
1234    /**
1235     * Set the {@link LayoutManager} that this RecyclerView will use.
1236     *
1237     * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
1238     * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
1239     * layout arrangements for child views. These arrangements are controlled by the
1240     * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
1241     *
1242     * <p>Several default strategies are provided for common uses such as lists and grids.</p>
1243     *
1244     * @param layout LayoutManager to use
1245     */
1246    public void setLayoutManager(@Nullable LayoutManager layout) {
1247        if (layout == mLayout) {
1248            return;
1249        }
1250        stopScroll();
1251        // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
1252        // chance that LayoutManagers will re-use views.
1253        if (mLayout != null) {
1254            // end all running animations
1255            if (mItemAnimator != null) {
1256                mItemAnimator.endAnimations();
1257            }
1258            mLayout.removeAndRecycleAllViews(mRecycler);
1259            mLayout.removeAndRecycleScrapInt(mRecycler);
1260            mRecycler.clear();
1261
1262            if (mIsAttached) {
1263                mLayout.dispatchDetachedFromWindow(this, mRecycler);
1264            }
1265            mLayout.setRecyclerView(null);
1266            mLayout = null;
1267        } else {
1268            mRecycler.clear();
1269        }
1270        // this is just a defensive measure for faulty item animators.
1271        mChildHelper.removeAllViewsUnfiltered();
1272        mLayout = layout;
1273        if (layout != null) {
1274            if (layout.mRecyclerView != null) {
1275                throw new IllegalArgumentException("LayoutManager " + layout
1276                        + " is already attached to a RecyclerView:"
1277                        + layout.mRecyclerView.exceptionLabel());
1278            }
1279            mLayout.setRecyclerView(this);
1280            if (mIsAttached) {
1281                mLayout.dispatchAttachedToWindow(this);
1282            }
1283        }
1284        mRecycler.updateViewCacheSize();
1285        requestLayout();
1286    }
1287
1288    /**
1289     * Set a {@link OnFlingListener} for this {@link RecyclerView}.
1290     * <p>
1291     * If the {@link OnFlingListener} is set then it will receive
1292     * calls to {@link #fling(int,int)} and will be able to intercept them.
1293     *
1294     * @param onFlingListener The {@link OnFlingListener} instance.
1295     */
1296    public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
1297        mOnFlingListener = onFlingListener;
1298    }
1299
1300    /**
1301     * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
1302     *
1303     * @return The {@link OnFlingListener} instance currently set (can be null).
1304     */
1305    @Nullable
1306    public OnFlingListener getOnFlingListener() {
1307        return mOnFlingListener;
1308    }
1309
1310    @Override
1311    protected Parcelable onSaveInstanceState() {
1312        SavedState state = new SavedState(super.onSaveInstanceState());
1313        if (mPendingSavedState != null) {
1314            state.copyFrom(mPendingSavedState);
1315        } else if (mLayout != null) {
1316            state.mLayoutState = mLayout.onSaveInstanceState();
1317        } else {
1318            state.mLayoutState = null;
1319        }
1320
1321        return state;
1322    }
1323
1324    @Override
1325    protected void onRestoreInstanceState(Parcelable state) {
1326        if (!(state instanceof SavedState)) {
1327            super.onRestoreInstanceState(state);
1328            return;
1329        }
1330
1331        mPendingSavedState = (SavedState) state;
1332        super.onRestoreInstanceState(mPendingSavedState.getSuperState());
1333        if (mLayout != null && mPendingSavedState.mLayoutState != null) {
1334            mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
1335        }
1336    }
1337
1338    /**
1339     * Override to prevent freezing of any views created by the adapter.
1340     */
1341    @Override
1342    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1343        dispatchFreezeSelfOnly(container);
1344    }
1345
1346    /**
1347     * Override to prevent thawing of any views created by the adapter.
1348     */
1349    @Override
1350    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1351        dispatchThawSelfOnly(container);
1352    }
1353
1354    /**
1355     * Adds a view to the animatingViews list.
1356     * mAnimatingViews holds the child views that are currently being kept around
1357     * purely for the purpose of being animated out of view. They are drawn as a regular
1358     * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
1359     * as they are managed separately from the regular child views.
1360     * @param viewHolder The ViewHolder to be removed
1361     */
1362    private void addAnimatingView(ViewHolder viewHolder) {
1363        final View view = viewHolder.itemView;
1364        final boolean alreadyParented = view.getParent() == this;
1365        mRecycler.unscrapView(getChildViewHolder(view));
1366        if (viewHolder.isTmpDetached()) {
1367            // re-attach
1368            mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
1369        } else if (!alreadyParented) {
1370            mChildHelper.addView(view, true);
1371        } else {
1372            mChildHelper.hide(view);
1373        }
1374    }
1375
1376    /**
1377     * Removes a view from the animatingViews list.
1378     * @param view The view to be removed
1379     * @see #addAnimatingView(RecyclerView.ViewHolder)
1380     * @return true if an animating view is removed
1381     */
1382    boolean removeAnimatingView(View view) {
1383        startInterceptRequestLayout();
1384        final boolean removed = mChildHelper.removeViewIfHidden(view);
1385        if (removed) {
1386            final ViewHolder viewHolder = getChildViewHolderInt(view);
1387            mRecycler.unscrapView(viewHolder);
1388            mRecycler.recycleViewHolderInternal(viewHolder);
1389            if (DEBUG) {
1390                Log.d(TAG, "after removing animated view: " + view + ", " + this);
1391            }
1392        }
1393        // only clear request eaten flag if we removed the view.
1394        stopInterceptRequestLayout(!removed);
1395        return removed;
1396    }
1397
1398    /**
1399     * Return the {@link LayoutManager} currently responsible for
1400     * layout policy for this RecyclerView.
1401     *
1402     * @return The currently bound LayoutManager
1403     */
1404    @Nullable
1405    public LayoutManager getLayoutManager() {
1406        return mLayout;
1407    }
1408
1409    /**
1410     * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
1411     * if no pool is set for this view a new one will be created. See
1412     * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
1413     *
1414     * @return The pool used to store recycled item views for reuse.
1415     * @see #setRecycledViewPool(RecycledViewPool)
1416     */
1417    @NonNull
1418    public RecycledViewPool getRecycledViewPool() {
1419        return mRecycler.getRecycledViewPool();
1420    }
1421
1422    /**
1423     * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
1424     * This can be useful if you have multiple RecyclerViews with adapters that use the same
1425     * view types, for example if you have several data sets with the same kinds of item views
1426     * displayed by a {@link ViewPager ViewPager}.
1427     *
1428     * @param pool Pool to set. If this parameter is null a new pool will be created and used.
1429     */
1430    public void setRecycledViewPool(@Nullable RecycledViewPool pool) {
1431        mRecycler.setRecycledViewPool(pool);
1432    }
1433
1434    /**
1435     * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
1436     *
1437     * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
1438     *
1439     * @see ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)
1440     */
1441    public void setViewCacheExtension(@Nullable ViewCacheExtension extension) {
1442        mRecycler.setViewCacheExtension(extension);
1443    }
1444
1445    /**
1446     * Set the number of offscreen views to retain before adding them to the potentially shared
1447     * {@link #getRecycledViewPool() recycled view pool}.
1448     *
1449     * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
1450     * a LayoutManager to reuse those views unmodified without needing to return to the adapter
1451     * to rebind them.</p>
1452     *
1453     * @param size Number of views to cache offscreen before returning them to the general
1454     *             recycled view pool
1455     */
1456    public void setItemViewCacheSize(int size) {
1457        mRecycler.setViewCacheSize(size);
1458    }
1459
1460    /**
1461     * Return the current scrolling state of the RecyclerView.
1462     *
1463     * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
1464     * {@link #SCROLL_STATE_SETTLING}
1465     */
1466    public int getScrollState() {
1467        return mScrollState;
1468    }
1469
1470    void setScrollState(int state) {
1471        if (state == mScrollState) {
1472            return;
1473        }
1474        if (DEBUG) {
1475            Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
1476                    new Exception());
1477        }
1478        mScrollState = state;
1479        if (state != SCROLL_STATE_SETTLING) {
1480            stopScrollersInternal();
1481        }
1482        dispatchOnScrollStateChanged(state);
1483    }
1484
1485    /**
1486     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1487     * affect both measurement and drawing of individual item views.
1488     *
1489     * <p>Item decorations are ordered. Decorations placed earlier in the list will
1490     * be run/queried/drawn first for their effects on item views. Padding added to views
1491     * will be nested; a padding added by an earlier decoration will mean further
1492     * item decorations in the list will be asked to draw/pad within the previous decoration's
1493     * given area.</p>
1494     *
1495     * @param decor Decoration to add
1496     * @param index Position in the decoration chain to insert this decoration at. If this value
1497     *              is negative the decoration will be added at the end.
1498     */
1499    public void addItemDecoration(@NonNull ItemDecoration decor, int index) {
1500        if (mLayout != null) {
1501            mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
1502                    + " layout");
1503        }
1504        if (mItemDecorations.isEmpty()) {
1505            setWillNotDraw(false);
1506        }
1507        if (index < 0) {
1508            mItemDecorations.add(decor);
1509        } else {
1510            mItemDecorations.add(index, decor);
1511        }
1512        markItemDecorInsetsDirty();
1513        requestLayout();
1514    }
1515
1516    /**
1517     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1518     * affect both measurement and drawing of individual item views.
1519     *
1520     * <p>Item decorations are ordered. Decorations placed earlier in the list will
1521     * be run/queried/drawn first for their effects on item views. Padding added to views
1522     * will be nested; a padding added by an earlier decoration will mean further
1523     * item decorations in the list will be asked to draw/pad within the previous decoration's
1524     * given area.</p>
1525     *
1526     * @param decor Decoration to add
1527     */
1528    public void addItemDecoration(@NonNull ItemDecoration decor) {
1529        addItemDecoration(decor, -1);
1530    }
1531
1532    /**
1533     * Returns an {@link ItemDecoration} previously added to this RecyclerView.
1534     *
1535     * @param index The index position of the desired ItemDecoration.
1536     * @return the ItemDecoration at index position
1537     * @throws IndexOutOfBoundsException on invalid index
1538     */
1539    @NonNull
1540    public ItemDecoration getItemDecorationAt(int index) {
1541        final int size = getItemDecorationCount();
1542        if (index < 0 || index >= size) {
1543            throw new IndexOutOfBoundsException(index + " is an invalid index for size " + size);
1544        }
1545
1546        return mItemDecorations.get(index);
1547    }
1548
1549    /**
1550     * Returns the number of {@link ItemDecoration} currently added to this RecyclerView.
1551     *
1552     * @return number of ItemDecorations currently added added to this RecyclerView.
1553     */
1554    public int getItemDecorationCount() {
1555        return mItemDecorations.size();
1556    }
1557
1558    /**
1559     * Removes the {@link ItemDecoration} associated with the supplied index position.
1560     *
1561     * @param index The index position of the ItemDecoration to be removed.
1562     */
1563    public void removeItemDecorationAt(int index) {
1564        final int size = getItemDecorationCount();
1565        if (index < 0 || index >= size) {
1566            throw new IndexOutOfBoundsException(index + " is an invalid index for size " + size);
1567        }
1568
1569        removeItemDecoration(getItemDecorationAt(index));
1570    }
1571
1572    /**
1573     * Remove an {@link ItemDecoration} from this RecyclerView.
1574     *
1575     * <p>The given decoration will no longer impact the measurement and drawing of
1576     * item views.</p>
1577     *
1578     * @param decor Decoration to remove
1579     * @see #addItemDecoration(ItemDecoration)
1580     */
1581    public void removeItemDecoration(@NonNull ItemDecoration decor) {
1582        if (mLayout != null) {
1583            mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
1584                    + " layout");
1585        }
1586        mItemDecorations.remove(decor);
1587        if (mItemDecorations.isEmpty()) {
1588            setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
1589        }
1590        markItemDecorInsetsDirty();
1591        requestLayout();
1592    }
1593
1594    /**
1595     * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
1596     * <p>
1597     * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
1598     * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
1599     * true if childDrawingOrderCallback is not null, false otherwise.
1600     * <p>
1601     * Note that child drawing order may be overridden by View's elevation.
1602     *
1603     * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
1604     *                                  system.
1605     */
1606    public void setChildDrawingOrderCallback(
1607            @Nullable ChildDrawingOrderCallback childDrawingOrderCallback) {
1608        if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
1609            return;
1610        }
1611        mChildDrawingOrderCallback = childDrawingOrderCallback;
1612        setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
1613    }
1614
1615    /**
1616     * Set a listener that will be notified of any changes in scroll state or position.
1617     *
1618     * @param listener Listener to set or null to clear
1619     *
1620     * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
1621     *             {@link #removeOnScrollListener(OnScrollListener)}
1622     */
1623    @Deprecated
1624    public void setOnScrollListener(@Nullable OnScrollListener listener) {
1625        mScrollListener = listener;
1626    }
1627
1628    /**
1629     * Add a listener that will be notified of any changes in scroll state or position.
1630     *
1631     * <p>Components that add a listener should take care to remove it when finished.
1632     * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
1633     * to remove all attached listeners.</p>
1634     *
1635     * @param listener listener to set
1636     */
1637    public void addOnScrollListener(@NonNull OnScrollListener listener) {
1638        if (mScrollListeners == null) {
1639            mScrollListeners = new ArrayList<>();
1640        }
1641        mScrollListeners.add(listener);
1642    }
1643
1644    /**
1645     * Remove a listener that was notified of any changes in scroll state or position.
1646     *
1647     * @param listener listener to set or null to clear
1648     */
1649    public void removeOnScrollListener(@NonNull OnScrollListener listener) {
1650        if (mScrollListeners != null) {
1651            mScrollListeners.remove(listener);
1652        }
1653    }
1654
1655    /**
1656     * Remove all secondary listener that were notified of any changes in scroll state or position.
1657     */
1658    public void clearOnScrollListeners() {
1659        if (mScrollListeners != null) {
1660            mScrollListeners.clear();
1661        }
1662    }
1663
1664    /**
1665     * Convenience method to scroll to a certain position.
1666     *
1667     * RecyclerView does not implement scrolling logic, rather forwards the call to
1668     * {@link RecyclerView.LayoutManager#scrollToPosition(int)}
1669     * @param position Scroll to this adapter position
1670     * @see RecyclerView.LayoutManager#scrollToPosition(int)
1671     */
1672    public void scrollToPosition(int position) {
1673        if (mLayoutFrozen) {
1674            return;
1675        }
1676        stopScroll();
1677        if (mLayout == null) {
1678            Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
1679                    + "Call setLayoutManager with a non-null argument.");
1680            return;
1681        }
1682        mLayout.scrollToPosition(position);
1683        awakenScrollBars();
1684    }
1685
1686    void jumpToPositionForSmoothScroller(int position) {
1687        if (mLayout == null) {
1688            return;
1689        }
1690        mLayout.scrollToPosition(position);
1691        awakenScrollBars();
1692    }
1693
1694    /**
1695     * Starts a smooth scroll to an adapter position.
1696     * <p>
1697     * To support smooth scrolling, you must override
1698     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
1699     * {@link SmoothScroller}.
1700     * <p>
1701     * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
1702     * provide a custom smooth scroll logic, override
1703     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
1704     * LayoutManager.
1705     *
1706     * @param position The adapter position to scroll to
1707     * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
1708     */
1709    public void smoothScrollToPosition(int position) {
1710        if (mLayoutFrozen) {
1711            return;
1712        }
1713        if (mLayout == null) {
1714            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
1715                    + "Call setLayoutManager with a non-null argument.");
1716            return;
1717        }
1718        mLayout.smoothScrollToPosition(this, mState, position);
1719    }
1720
1721    @Override
1722    public void scrollTo(int x, int y) {
1723        Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
1724                + "Use scrollToPosition instead");
1725    }
1726
1727    @Override
1728    public void scrollBy(int x, int y) {
1729        if (mLayout == null) {
1730            Log.e(TAG, "Cannot scroll without a LayoutManager set. "
1731                    + "Call setLayoutManager with a non-null argument.");
1732            return;
1733        }
1734        if (mLayoutFrozen) {
1735            return;
1736        }
1737        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1738        final boolean canScrollVertical = mLayout.canScrollVertically();
1739        if (canScrollHorizontal || canScrollVertical) {
1740            scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
1741        }
1742    }
1743
1744    /**
1745     * Scrolls the RV by 'dx' and 'dy' via calls to
1746     * {@link LayoutManager#scrollHorizontallyBy(int, Recycler, State)} and
1747     * {@link LayoutManager#scrollVerticallyBy(int, Recycler, State)}.
1748     *
1749     * Also sets how much of the scroll was actually consumed in 'consumed' parameter (indexes 0 and
1750     * 1 for the x axis and y axis, respectively).
1751     *
1752     * This method should only be called in the context of an existing scroll operation such that
1753     * any other necessary operations (such as a call to {@link #consumePendingUpdateOperations()})
1754     * is already handled.
1755     */
1756    private void scrollStep(int dx, int dy, @Nullable int[] consumed) {
1757        startInterceptRequestLayout();
1758        onEnterLayoutOrScroll();
1759
1760        TraceCompat.beginSection(TRACE_SCROLL_TAG);
1761        fillRemainingScrollValues(mState);
1762
1763        int consumedX = 0;
1764        int consumedY = 0;
1765        if (dx != 0) {
1766            consumedX = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
1767        }
1768        if (dy != 0) {
1769            consumedY = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
1770        }
1771
1772        TraceCompat.endSection();
1773        repositionShadowingViews();
1774
1775        onExitLayoutOrScroll();
1776        stopInterceptRequestLayout(false);
1777
1778        if (consumed != null) {
1779            consumed[0] = consumedX;
1780            consumed[1] = consumedY;
1781        }
1782    }
1783
1784    /**
1785     * Helper method reflect data changes to the state.
1786     * <p>
1787     * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
1788     * but data actually changed.
1789     * <p>
1790     * This method consumes all deferred changes to avoid that case.
1791     */
1792    void consumePendingUpdateOperations() {
1793        if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
1794            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1795            dispatchLayout();
1796            TraceCompat.endSection();
1797            return;
1798        }
1799        if (!mAdapterHelper.hasPendingUpdates()) {
1800            return;
1801        }
1802
1803        // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
1804        // of the visible items is affected and if not, just ignore the change.
1805        if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
1806                .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
1807                        | AdapterHelper.UpdateOp.MOVE)) {
1808            TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
1809            startInterceptRequestLayout();
1810            onEnterLayoutOrScroll();
1811            mAdapterHelper.preProcess();
1812            if (!mLayoutWasDefered) {
1813                if (hasUpdatedView()) {
1814                    dispatchLayout();
1815                } else {
1816                    // no need to layout, clean state
1817                    mAdapterHelper.consumePostponedUpdates();
1818                }
1819            }
1820            stopInterceptRequestLayout(true);
1821            onExitLayoutOrScroll();
1822            TraceCompat.endSection();
1823        } else if (mAdapterHelper.hasPendingUpdates()) {
1824            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1825            dispatchLayout();
1826            TraceCompat.endSection();
1827        }
1828    }
1829
1830    /**
1831     * @return True if an existing view holder needs to be updated
1832     */
1833    private boolean hasUpdatedView() {
1834        final int childCount = mChildHelper.getChildCount();
1835        for (int i = 0; i < childCount; i++) {
1836            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
1837            if (holder == null || holder.shouldIgnore()) {
1838                continue;
1839            }
1840            if (holder.isUpdated()) {
1841                return true;
1842            }
1843        }
1844        return false;
1845    }
1846
1847    /**
1848     * Does not perform bounds checking. Used by internal methods that have already validated input.
1849     * <p>
1850     * It also reports any unused scroll request to the related EdgeEffect.
1851     *
1852     * @param x The amount of horizontal scroll request
1853     * @param y The amount of vertical scroll request
1854     * @param ev The originating MotionEvent, or null if not from a touch event.
1855     *
1856     * @return Whether any scroll was consumed in either direction.
1857     */
1858    boolean scrollByInternal(int x, int y, MotionEvent ev) {
1859        int unconsumedX = 0, unconsumedY = 0;
1860        int consumedX = 0, consumedY = 0;
1861
1862        consumePendingUpdateOperations();
1863        if (mAdapter != null) {
1864            scrollStep(x, y, mScrollStepConsumed);
1865            consumedX = mScrollStepConsumed[0];
1866            consumedY = mScrollStepConsumed[1];
1867            unconsumedX = x - consumedX;
1868            unconsumedY = y - consumedY;
1869        }
1870        if (!mItemDecorations.isEmpty()) {
1871            invalidate();
1872        }
1873
1874        if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset,
1875                TYPE_TOUCH)) {
1876            // Update the last touch co-ords, taking any scroll offset into account
1877            mLastTouchX -= mScrollOffset[0];
1878            mLastTouchY -= mScrollOffset[1];
1879            if (ev != null) {
1880                ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
1881            }
1882            mNestedOffsets[0] += mScrollOffset[0];
1883            mNestedOffsets[1] += mScrollOffset[1];
1884        } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
1885            if (ev != null && !MotionEventCompat.isFromSource(ev, InputDevice.SOURCE_MOUSE)) {
1886                pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
1887            }
1888            considerReleasingGlowsOnScroll(x, y);
1889        }
1890        if (consumedX != 0 || consumedY != 0) {
1891            dispatchOnScrolled(consumedX, consumedY);
1892        }
1893        if (!awakenScrollBars()) {
1894            invalidate();
1895        }
1896        return consumedX != 0 || consumedY != 0;
1897    }
1898
1899    /**
1900     * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
1901     * range. This value is used to compute the length of the thumb within the scrollbar's track.
1902     * </p>
1903     *
1904     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1905     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
1906     *
1907     * <p>Default implementation returns 0.</p>
1908     *
1909     * <p>If you want to support scroll bars, override
1910     * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
1911     * LayoutManager. </p>
1912     *
1913     * @return The horizontal offset of the scrollbar's thumb
1914     * @see RecyclerView.LayoutManager#computeHorizontalScrollOffset
1915     * (RecyclerView.State)
1916     */
1917    @Override
1918    public int computeHorizontalScrollOffset() {
1919        if (mLayout == null) {
1920            return 0;
1921        }
1922        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
1923    }
1924
1925    /**
1926     * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
1927     * horizontal range. This value is used to compute the length of the thumb within the
1928     * scrollbar's track.</p>
1929     *
1930     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1931     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
1932     *
1933     * <p>Default implementation returns 0.</p>
1934     *
1935     * <p>If you want to support scroll bars, override
1936     * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
1937     * LayoutManager.</p>
1938     *
1939     * @return The horizontal extent of the scrollbar's thumb
1940     * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
1941     */
1942    @Override
1943    public int computeHorizontalScrollExtent() {
1944        if (mLayout == null) {
1945            return 0;
1946        }
1947        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
1948    }
1949
1950    /**
1951     * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
1952     *
1953     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1954     * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
1955     *
1956     * <p>Default implementation returns 0.</p>
1957     *
1958     * <p>If you want to support scroll bars, override
1959     * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
1960     * LayoutManager.</p>
1961     *
1962     * @return The total horizontal range represented by the vertical scrollbar
1963     * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
1964     */
1965    @Override
1966    public int computeHorizontalScrollRange() {
1967        if (mLayout == null) {
1968            return 0;
1969        }
1970        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
1971    }
1972
1973    /**
1974     * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
1975     * This value is used to compute the length of the thumb within the scrollbar's track. </p>
1976     *
1977     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1978     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
1979     *
1980     * <p>Default implementation returns 0.</p>
1981     *
1982     * <p>If you want to support scroll bars, override
1983     * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
1984     * LayoutManager.</p>
1985     *
1986     * @return The vertical offset of the scrollbar's thumb
1987     * @see RecyclerView.LayoutManager#computeVerticalScrollOffset
1988     * (RecyclerView.State)
1989     */
1990    @Override
1991    public int computeVerticalScrollOffset() {
1992        if (mLayout == null) {
1993            return 0;
1994        }
1995        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
1996    }
1997
1998    /**
1999     * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
2000     * This value is used to compute the length of the thumb within the scrollbar's track.</p>
2001     *
2002     * <p>The range is expressed in arbitrary units that must be the same as the units used by
2003     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
2004     *
2005     * <p>Default implementation returns 0.</p>
2006     *
2007     * <p>If you want to support scroll bars, override
2008     * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
2009     * LayoutManager.</p>
2010     *
2011     * @return The vertical extent of the scrollbar's thumb
2012     * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
2013     */
2014    @Override
2015    public int computeVerticalScrollExtent() {
2016        if (mLayout == null) {
2017            return 0;
2018        }
2019        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
2020    }
2021
2022    /**
2023     * <p>Compute the vertical range that the vertical scrollbar represents.</p>
2024     *
2025     * <p>The range is expressed in arbitrary units that must be the same as the units used by
2026     * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
2027     *
2028     * <p>Default implementation returns 0.</p>
2029     *
2030     * <p>If you want to support scroll bars, override
2031     * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
2032     * LayoutManager.</p>
2033     *
2034     * @return The total vertical range represented by the vertical scrollbar
2035     * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
2036     */
2037    @Override
2038    public int computeVerticalScrollRange() {
2039        if (mLayout == null) {
2040            return 0;
2041        }
2042        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
2043    }
2044
2045    /**
2046     * This method should be called before any code that may trigger a child view to cause a call to
2047     * {@link RecyclerView#requestLayout()}.  Doing so enables {@link RecyclerView} to avoid
2048     * reacting to additional redundant calls to {@link #requestLayout()}.
2049     * <p>
2050     * A call to this method must always be accompanied by a call to
2051     * {@link #stopInterceptRequestLayout(boolean)} that follows the code that may trigger a
2052     * child View to cause a call to {@link RecyclerView#requestLayout()}.
2053     *
2054     * @see #stopInterceptRequestLayout(boolean)
2055     */
2056    void startInterceptRequestLayout() {
2057        mInterceptRequestLayoutDepth++;
2058        if (mInterceptRequestLayoutDepth == 1 && !mLayoutFrozen) {
2059            mLayoutWasDefered = false;
2060        }
2061    }
2062
2063    /**
2064     * This method should be called after any code that may trigger a child view to cause a call to
2065     * {@link RecyclerView#requestLayout()}.
2066     * <p>
2067     * A call to this method must always be accompanied by a call to
2068     * {@link #startInterceptRequestLayout()} that precedes the code that may trigger a child
2069     * View to cause a call to {@link RecyclerView#requestLayout()}.
2070     *
2071     * @see #startInterceptRequestLayout()
2072     */
2073    void stopInterceptRequestLayout(boolean performLayoutChildren) {
2074        if (mInterceptRequestLayoutDepth < 1) {
2075            //noinspection PointlessBooleanExpression
2076            if (DEBUG) {
2077                throw new IllegalStateException("stopInterceptRequestLayout was called more "
2078                        + "times than startInterceptRequestLayout."
2079                        + exceptionLabel());
2080            }
2081            mInterceptRequestLayoutDepth = 1;
2082        }
2083        if (!performLayoutChildren && !mLayoutFrozen) {
2084            // Reset the layout request eaten counter.
2085            // This is necessary since eatRequest calls can be nested in which case the other
2086            // call will override the inner one.
2087            // for instance:
2088            // eat layout for process adapter updates
2089            //   eat layout for dispatchLayout
2090            //     a bunch of req layout calls arrive
2091
2092            mLayoutWasDefered = false;
2093        }
2094        if (mInterceptRequestLayoutDepth == 1) {
2095            // when layout is frozen we should delay dispatchLayout()
2096            if (performLayoutChildren && mLayoutWasDefered && !mLayoutFrozen
2097                    && mLayout != null && mAdapter != null) {
2098                dispatchLayout();
2099            }
2100            if (!mLayoutFrozen) {
2101                mLayoutWasDefered = false;
2102            }
2103        }
2104        mInterceptRequestLayoutDepth--;
2105    }
2106
2107    /**
2108     * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
2109     * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
2110     * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
2111     * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
2112     * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
2113     * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
2114     * called.
2115     *
2116     * <p>
2117     * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
2118     * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
2119     * RecyclerView, State, int)}.
2120     * <p>
2121     * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
2122     * stop frozen.
2123     * <p>
2124     * Note: Running ItemAnimator is not stopped automatically,  it's caller's
2125     * responsibility to call ItemAnimator.end().
2126     *
2127     * @param frozen   true to freeze layout and scroll, false to re-enable.
2128     */
2129    public void setLayoutFrozen(boolean frozen) {
2130        if (frozen != mLayoutFrozen) {
2131            assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
2132            if (!frozen) {
2133                mLayoutFrozen = false;
2134                if (mLayoutWasDefered && mLayout != null && mAdapter != null) {
2135                    requestLayout();
2136                }
2137                mLayoutWasDefered = false;
2138            } else {
2139                final long now = SystemClock.uptimeMillis();
2140                MotionEvent cancelEvent = MotionEvent.obtain(now, now,
2141                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2142                onTouchEvent(cancelEvent);
2143                mLayoutFrozen = true;
2144                mIgnoreMotionEventTillDown = true;
2145                stopScroll();
2146            }
2147        }
2148    }
2149
2150    /**
2151     * Returns true if layout and scroll are frozen.
2152     *
2153     * @return true if layout and scroll are frozen
2154     * @see #setLayoutFrozen(boolean)
2155     */
2156    public boolean isLayoutFrozen() {
2157        return mLayoutFrozen;
2158    }
2159
2160    /**
2161     * Animate a scroll by the given amount of pixels along either axis.
2162     *
2163     * @param dx Pixels to scroll horizontally
2164     * @param dy Pixels to scroll vertically
2165     */
2166    public void smoothScrollBy(@Px int dx, @Px int dy) {
2167        smoothScrollBy(dx, dy, null);
2168    }
2169
2170    /**
2171     * Animate a scroll by the given amount of pixels along either axis.
2172     *
2173     * @param dx Pixels to scroll horizontally
2174     * @param dy Pixels to scroll vertically
2175     * @param interpolator {@link Interpolator} to be used for scrolling. If it is
2176     *                     {@code null}, RecyclerView is going to use the default interpolator.
2177     */
2178    public void smoothScrollBy(@Px int dx, @Px int dy, @Nullable Interpolator interpolator) {
2179        if (mLayout == null) {
2180            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
2181                    + "Call setLayoutManager with a non-null argument.");
2182            return;
2183        }
2184        if (mLayoutFrozen) {
2185            return;
2186        }
2187        if (!mLayout.canScrollHorizontally()) {
2188            dx = 0;
2189        }
2190        if (!mLayout.canScrollVertically()) {
2191            dy = 0;
2192        }
2193        if (dx != 0 || dy != 0) {
2194            mViewFlinger.smoothScrollBy(dx, dy, interpolator);
2195        }
2196    }
2197
2198    /**
2199     * Begin a standard fling with an initial velocity along each axis in pixels per second.
2200     * If the velocity given is below the system-defined minimum this method will return false
2201     * and no fling will occur.
2202     *
2203     * @param velocityX Initial horizontal velocity in pixels per second
2204     * @param velocityY Initial vertical velocity in pixels per second
2205     * @return true if the fling was started, false if the velocity was too low to fling or
2206     * LayoutManager does not support scrolling in the axis fling is issued.
2207     *
2208     * @see LayoutManager#canScrollVertically()
2209     * @see LayoutManager#canScrollHorizontally()
2210     */
2211    public boolean fling(int velocityX, int velocityY) {
2212        if (mLayout == null) {
2213            Log.e(TAG, "Cannot fling without a LayoutManager set. "
2214                    + "Call setLayoutManager with a non-null argument.");
2215            return false;
2216        }
2217        if (mLayoutFrozen) {
2218            return false;
2219        }
2220
2221        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
2222        final boolean canScrollVertical = mLayout.canScrollVertically();
2223
2224        if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
2225            velocityX = 0;
2226        }
2227        if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
2228            velocityY = 0;
2229        }
2230        if (velocityX == 0 && velocityY == 0) {
2231            // If we don't have any velocity, return false
2232            return false;
2233        }
2234
2235        if (!dispatchNestedPreFling(velocityX, velocityY)) {
2236            final boolean canScroll = canScrollHorizontal || canScrollVertical;
2237            dispatchNestedFling(velocityX, velocityY, canScroll);
2238
2239            if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
2240                return true;
2241            }
2242
2243            if (canScroll) {
2244                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2245                if (canScrollHorizontal) {
2246                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2247                }
2248                if (canScrollVertical) {
2249                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2250                }
2251                startNestedScroll(nestedScrollAxis, TYPE_NON_TOUCH);
2252
2253                velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
2254                velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
2255                mViewFlinger.fling(velocityX, velocityY);
2256                return true;
2257            }
2258        }
2259        return false;
2260    }
2261
2262    /**
2263     * Stop any current scroll in progress, such as one started by
2264     * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
2265     */
2266    public void stopScroll() {
2267        setScrollState(SCROLL_STATE_IDLE);
2268        stopScrollersInternal();
2269    }
2270
2271    /**
2272     * Similar to {@link #stopScroll()} but does not set the state.
2273     */
2274    private void stopScrollersInternal() {
2275        mViewFlinger.stop();
2276        if (mLayout != null) {
2277            mLayout.stopSmoothScroller();
2278        }
2279    }
2280
2281    /**
2282     * Returns the minimum velocity to start a fling.
2283     *
2284     * @return The minimum velocity to start a fling
2285     */
2286    public int getMinFlingVelocity() {
2287        return mMinFlingVelocity;
2288    }
2289
2290
2291    /**
2292     * Returns the maximum fling velocity used by this RecyclerView.
2293     *
2294     * @return The maximum fling velocity used by this RecyclerView.
2295     */
2296    public int getMaxFlingVelocity() {
2297        return mMaxFlingVelocity;
2298    }
2299
2300    /**
2301     * Apply a pull to relevant overscroll glow effects
2302     */
2303    private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
2304        boolean invalidate = false;
2305        if (overscrollX < 0) {
2306            ensureLeftGlow();
2307            EdgeEffectCompat.onPull(mLeftGlow, -overscrollX / getWidth(), 1f - y  / getHeight());
2308            invalidate = true;
2309        } else if (overscrollX > 0) {
2310            ensureRightGlow();
2311            EdgeEffectCompat.onPull(mRightGlow, overscrollX / getWidth(), y / getHeight());
2312            invalidate = true;
2313        }
2314
2315        if (overscrollY < 0) {
2316            ensureTopGlow();
2317            EdgeEffectCompat.onPull(mTopGlow, -overscrollY / getHeight(), x / getWidth());
2318            invalidate = true;
2319        } else if (overscrollY > 0) {
2320            ensureBottomGlow();
2321            EdgeEffectCompat.onPull(mBottomGlow, overscrollY / getHeight(), 1f - x / getWidth());
2322            invalidate = true;
2323        }
2324
2325        if (invalidate || overscrollX != 0 || overscrollY != 0) {
2326            ViewCompat.postInvalidateOnAnimation(this);
2327        }
2328    }
2329
2330    private void releaseGlows() {
2331        boolean needsInvalidate = false;
2332        if (mLeftGlow != null) {
2333            mLeftGlow.onRelease();
2334            needsInvalidate = mLeftGlow.isFinished();
2335        }
2336        if (mTopGlow != null) {
2337            mTopGlow.onRelease();
2338            needsInvalidate |= mTopGlow.isFinished();
2339        }
2340        if (mRightGlow != null) {
2341            mRightGlow.onRelease();
2342            needsInvalidate |= mRightGlow.isFinished();
2343        }
2344        if (mBottomGlow != null) {
2345            mBottomGlow.onRelease();
2346            needsInvalidate |= mBottomGlow.isFinished();
2347        }
2348        if (needsInvalidate) {
2349            ViewCompat.postInvalidateOnAnimation(this);
2350        }
2351    }
2352
2353    void considerReleasingGlowsOnScroll(int dx, int dy) {
2354        boolean needsInvalidate = false;
2355        if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
2356            mLeftGlow.onRelease();
2357            needsInvalidate = mLeftGlow.isFinished();
2358        }
2359        if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
2360            mRightGlow.onRelease();
2361            needsInvalidate |= mRightGlow.isFinished();
2362        }
2363        if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
2364            mTopGlow.onRelease();
2365            needsInvalidate |= mTopGlow.isFinished();
2366        }
2367        if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
2368            mBottomGlow.onRelease();
2369            needsInvalidate |= mBottomGlow.isFinished();
2370        }
2371        if (needsInvalidate) {
2372            ViewCompat.postInvalidateOnAnimation(this);
2373        }
2374    }
2375
2376    void absorbGlows(int velocityX, int velocityY) {
2377        if (velocityX < 0) {
2378            ensureLeftGlow();
2379            mLeftGlow.onAbsorb(-velocityX);
2380        } else if (velocityX > 0) {
2381            ensureRightGlow();
2382            mRightGlow.onAbsorb(velocityX);
2383        }
2384
2385        if (velocityY < 0) {
2386            ensureTopGlow();
2387            mTopGlow.onAbsorb(-velocityY);
2388        } else if (velocityY > 0) {
2389            ensureBottomGlow();
2390            mBottomGlow.onAbsorb(velocityY);
2391        }
2392
2393        if (velocityX != 0 || velocityY != 0) {
2394            ViewCompat.postInvalidateOnAnimation(this);
2395        }
2396    }
2397
2398    void ensureLeftGlow() {
2399        if (mLeftGlow != null) {
2400            return;
2401        }
2402        mLeftGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_LEFT);
2403        if (mClipToPadding) {
2404            mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2405                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2406        } else {
2407            mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2408        }
2409    }
2410
2411    void ensureRightGlow() {
2412        if (mRightGlow != null) {
2413            return;
2414        }
2415        mRightGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_RIGHT);
2416        if (mClipToPadding) {
2417            mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2418                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2419        } else {
2420            mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2421        }
2422    }
2423
2424    void ensureTopGlow() {
2425        if (mTopGlow != null) {
2426            return;
2427        }
2428        mTopGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_TOP);
2429        if (mClipToPadding) {
2430            mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2431                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2432        } else {
2433            mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2434        }
2435
2436    }
2437
2438    void ensureBottomGlow() {
2439        if (mBottomGlow != null) {
2440            return;
2441        }
2442        mBottomGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_BOTTOM);
2443        if (mClipToPadding) {
2444            mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2445                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2446        } else {
2447            mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2448        }
2449    }
2450
2451    void invalidateGlows() {
2452        mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
2453    }
2454
2455    /**
2456     * Set a {@link EdgeEffectFactory} for this {@link RecyclerView}.
2457     * <p>
2458     * When a new {@link EdgeEffectFactory} is set, any existing over-scroll effects are cleared
2459     * and new effects are created as needed using
2460     * {@link EdgeEffectFactory#createEdgeEffect(RecyclerView, int)}
2461     *
2462     * @param edgeEffectFactory The {@link EdgeEffectFactory} instance.
2463     */
2464    public void setEdgeEffectFactory(@NonNull EdgeEffectFactory edgeEffectFactory) {
2465        Preconditions.checkNotNull(edgeEffectFactory);
2466        mEdgeEffectFactory = edgeEffectFactory;
2467        invalidateGlows();
2468    }
2469
2470    /**
2471     * Retrieves the previously set {@link EdgeEffectFactory} or the default factory if nothing
2472     * was set.
2473     *
2474     * @return The previously set {@link EdgeEffectFactory}
2475     * @see #setEdgeEffectFactory(EdgeEffectFactory)
2476     */
2477    @NonNull
2478    public EdgeEffectFactory getEdgeEffectFactory() {
2479        return mEdgeEffectFactory;
2480    }
2481
2482    /**
2483     * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
2484     * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
2485     * that differs from other ViewGroups.
2486     * <p>
2487     * It first does a focus search within the RecyclerView. If this search finds a View that is in
2488     * the focus direction with respect to the currently focused View, RecyclerView returns that
2489     * child as the next focus target. When it cannot find such child, it calls
2490     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
2491     * in the focus search direction. If LayoutManager adds a View that matches the
2492     * focus search criteria, it will be returned as the focus search result. Otherwise,
2493     * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
2494     * <p>
2495     * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
2496     * is not in the focus direction is still valid focus target which may not be the desired
2497     * behavior if the Adapter has more children in the focus direction. To handle this case,
2498     * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
2499     * focus search in that direction. If there are no Views to gain focus, it will call
2500     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
2501     * focus search with the original (relative) direction. This allows RecyclerView to provide
2502     * better candidates to the focus search while still allowing the view system to take focus from
2503     * the RecyclerView and give it to a more suitable child if such child exists.
2504     *
2505     * @param focused The view that currently has focus
2506     * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
2507     * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
2508     * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
2509     *
2510     * @return A new View that can be the next focus after the focused View
2511     */
2512    @Override
2513    public View focusSearch(View focused, int direction) {
2514        View result = mLayout.onInterceptFocusSearch(focused, direction);
2515        if (result != null) {
2516            return result;
2517        }
2518        final boolean canRunFocusFailure = mAdapter != null && mLayout != null
2519                && !isComputingLayout() && !mLayoutFrozen;
2520
2521        final FocusFinder ff = FocusFinder.getInstance();
2522        if (canRunFocusFailure
2523                && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
2524            // convert direction to absolute direction and see if we have a view there and if not
2525            // tell LayoutManager to add if it can.
2526            boolean needsFocusFailureLayout = false;
2527            if (mLayout.canScrollVertically()) {
2528                final int absDir =
2529                        direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
2530                final View found = ff.findNextFocus(this, focused, absDir);
2531                needsFocusFailureLayout = found == null;
2532                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2533                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2534                    direction = absDir;
2535                }
2536            }
2537            if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
2538                boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
2539                final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
2540                        ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2541                final View found = ff.findNextFocus(this, focused, absDir);
2542                needsFocusFailureLayout = found == null;
2543                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2544                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2545                    direction = absDir;
2546                }
2547            }
2548            if (needsFocusFailureLayout) {
2549                consumePendingUpdateOperations();
2550                final View focusedItemView = findContainingItemView(focused);
2551                if (focusedItemView == null) {
2552                    // panic, focused view is not a child anymore, cannot call super.
2553                    return null;
2554                }
2555                startInterceptRequestLayout();
2556                mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2557                stopInterceptRequestLayout(false);
2558            }
2559            result = ff.findNextFocus(this, focused, direction);
2560        } else {
2561            result = ff.findNextFocus(this, focused, direction);
2562            if (result == null && canRunFocusFailure) {
2563                consumePendingUpdateOperations();
2564                final View focusedItemView = findContainingItemView(focused);
2565                if (focusedItemView == null) {
2566                    // panic, focused view is not a child anymore, cannot call super.
2567                    return null;
2568                }
2569                startInterceptRequestLayout();
2570                result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2571                stopInterceptRequestLayout(false);
2572            }
2573        }
2574        if (result != null && !result.hasFocusable()) {
2575            if (getFocusedChild() == null) {
2576                // Scrolling to this unfocusable view is not meaningful since there is no currently
2577                // focused view which RV needs to keep visible.
2578                return super.focusSearch(focused, direction);
2579            }
2580            // If the next view returned by onFocusSearchFailed in layout manager has no focusable
2581            // views, we still scroll to that view in order to make it visible on the screen.
2582            // If it's focusable, framework already calls RV's requestChildFocus which handles
2583            // bringing this newly focused item onto the screen.
2584            requestChildOnScreen(result, null);
2585            return focused;
2586        }
2587        return isPreferredNextFocus(focused, result, direction)
2588                ? result : super.focusSearch(focused, direction);
2589    }
2590
2591    /**
2592     * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
2593     * assign it as the next focus View instead of letting view hierarchy decide.
2594     * A good candidate means a View that is aligned in the focus direction wrt the focused View
2595     * and is not the RecyclerView itself.
2596     * When this method returns false, RecyclerView will let the parent make the decision so the
2597     * same View may still get the focus as a result of that search.
2598     */
2599    private boolean isPreferredNextFocus(View focused, View next, int direction) {
2600        if (next == null || next == this) {
2601            return false;
2602        }
2603        // panic, result view is not a child anymore, maybe workaround b/37864393
2604        if (findContainingItemView(next) == null) {
2605            return false;
2606        }
2607        if (focused == null) {
2608            return true;
2609        }
2610        // panic, focused view is not a child anymore, maybe workaround b/37864393
2611        if (findContainingItemView(focused) == null) {
2612            return true;
2613        }
2614
2615        mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2616        mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
2617        offsetDescendantRectToMyCoords(focused, mTempRect);
2618        offsetDescendantRectToMyCoords(next, mTempRect2);
2619        final int rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL ? -1 : 1;
2620        int rightness = 0;
2621        if ((mTempRect.left < mTempRect2.left
2622                || mTempRect.right <= mTempRect2.left)
2623                && mTempRect.right < mTempRect2.right) {
2624            rightness = 1;
2625        } else if ((mTempRect.right > mTempRect2.right
2626                || mTempRect.left >= mTempRect2.right)
2627                && mTempRect.left > mTempRect2.left) {
2628            rightness = -1;
2629        }
2630        int downness = 0;
2631        if ((mTempRect.top < mTempRect2.top
2632                || mTempRect.bottom <= mTempRect2.top)
2633                && mTempRect.bottom < mTempRect2.bottom) {
2634            downness = 1;
2635        } else if ((mTempRect.bottom > mTempRect2.bottom
2636                || mTempRect.top >= mTempRect2.bottom)
2637                && mTempRect.top > mTempRect2.top) {
2638            downness = -1;
2639        }
2640        switch (direction) {
2641            case View.FOCUS_LEFT:
2642                return rightness < 0;
2643            case View.FOCUS_RIGHT:
2644                return rightness > 0;
2645            case View.FOCUS_UP:
2646                return downness < 0;
2647            case View.FOCUS_DOWN:
2648                return downness > 0;
2649            case View.FOCUS_FORWARD:
2650                return downness > 0 || (downness == 0 && rightness * rtl >= 0);
2651            case View.FOCUS_BACKWARD:
2652                return downness < 0 || (downness == 0 && rightness * rtl <= 0);
2653        }
2654        throw new IllegalArgumentException("Invalid direction: " + direction + exceptionLabel());
2655    }
2656
2657    @Override
2658    public void requestChildFocus(View child, View focused) {
2659        if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
2660            requestChildOnScreen(child, focused);
2661        }
2662        super.requestChildFocus(child, focused);
2663    }
2664
2665    /**
2666     * Requests that the given child of the RecyclerView be positioned onto the screen. This method
2667     * can be called for both unfocusable and focusable child views. For unfocusable child views,
2668     * the {@param focused} parameter passed is null, whereas for a focusable child, this parameter
2669     * indicates the actual descendant view within this child view that holds the focus.
2670     * @param child The child view of this RecyclerView that wants to come onto the screen.
2671     * @param focused The descendant view that actually has the focus if child is focusable, null
2672     *                otherwise.
2673     */
2674    private void requestChildOnScreen(@NonNull View child, @Nullable View focused) {
2675        View rectView = (focused != null) ? focused : child;
2676        mTempRect.set(0, 0, rectView.getWidth(), rectView.getHeight());
2677
2678        // get item decor offsets w/o refreshing. If they are invalid, there will be another
2679        // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
2680        // View in viewport.
2681        final ViewGroup.LayoutParams focusedLayoutParams = rectView.getLayoutParams();
2682        if (focusedLayoutParams instanceof LayoutParams) {
2683            // if focused child has item decors, use them. Otherwise, ignore.
2684            final LayoutParams lp = (LayoutParams) focusedLayoutParams;
2685            if (!lp.mInsetsDirty) {
2686                final Rect insets = lp.mDecorInsets;
2687                mTempRect.left -= insets.left;
2688                mTempRect.right += insets.right;
2689                mTempRect.top -= insets.top;
2690                mTempRect.bottom += insets.bottom;
2691            }
2692        }
2693
2694        if (focused != null) {
2695            offsetDescendantRectToMyCoords(focused, mTempRect);
2696            offsetRectIntoDescendantCoords(child, mTempRect);
2697        }
2698        mLayout.requestChildRectangleOnScreen(this, child, mTempRect, !mFirstLayoutComplete,
2699                (focused == null));
2700    }
2701
2702    @Override
2703    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
2704        return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
2705    }
2706
2707    @Override
2708    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
2709        if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
2710            super.addFocusables(views, direction, focusableMode);
2711        }
2712    }
2713
2714    @Override
2715    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
2716        if (isComputingLayout()) {
2717            // if we are in the middle of a layout calculation, don't let any child take focus.
2718            // RV will handle it after layout calculation is finished.
2719            return false;
2720        }
2721        return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
2722    }
2723
2724    @Override
2725    protected void onAttachedToWindow() {
2726        super.onAttachedToWindow();
2727        mLayoutOrScrollCounter = 0;
2728        mIsAttached = true;
2729        mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
2730        if (mLayout != null) {
2731            mLayout.dispatchAttachedToWindow(this);
2732        }
2733        mPostedAnimatorRunner = false;
2734
2735        if (ALLOW_THREAD_GAP_WORK) {
2736            // Register with gap worker
2737            mGapWorker = GapWorker.sGapWorker.get();
2738            if (mGapWorker == null) {
2739                mGapWorker = new GapWorker();
2740
2741                // break 60 fps assumption if data from display appears valid
2742                // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
2743                Display display = ViewCompat.getDisplay(this);
2744                float refreshRate = 60.0f;
2745                if (!isInEditMode() && display != null) {
2746                    float displayRefreshRate = display.getRefreshRate();
2747                    if (displayRefreshRate >= 30.0f) {
2748                        refreshRate = displayRefreshRate;
2749                    }
2750                }
2751                mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
2752                GapWorker.sGapWorker.set(mGapWorker);
2753            }
2754            mGapWorker.add(this);
2755        }
2756    }
2757
2758    @Override
2759    protected void onDetachedFromWindow() {
2760        super.onDetachedFromWindow();
2761        if (mItemAnimator != null) {
2762            mItemAnimator.endAnimations();
2763        }
2764        stopScroll();
2765        mIsAttached = false;
2766        if (mLayout != null) {
2767            mLayout.dispatchDetachedFromWindow(this, mRecycler);
2768        }
2769        mPendingAccessibilityImportanceChange.clear();
2770        removeCallbacks(mItemAnimatorRunner);
2771        mViewInfoStore.onDetach();
2772
2773        if (ALLOW_THREAD_GAP_WORK && mGapWorker != null) {
2774            // Unregister with gap worker
2775            mGapWorker.remove(this);
2776            mGapWorker = null;
2777        }
2778    }
2779
2780    /**
2781     * Returns true if RecyclerView is attached to window.
2782     */
2783    @Override
2784    public boolean isAttachedToWindow() {
2785        return mIsAttached;
2786    }
2787
2788    /**
2789     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2790     * {@link IllegalStateException} if it <b>is not</b>.
2791     *
2792     * @param message The message for the exception. Can be null.
2793     * @see #assertNotInLayoutOrScroll(String)
2794     */
2795    void assertInLayoutOrScroll(String message) {
2796        if (!isComputingLayout()) {
2797            if (message == null) {
2798                throw new IllegalStateException("Cannot call this method unless RecyclerView is "
2799                        + "computing a layout or scrolling" + exceptionLabel());
2800            }
2801            throw new IllegalStateException(message + exceptionLabel());
2802
2803        }
2804    }
2805
2806    /**
2807     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2808     * {@link IllegalStateException} if it <b>is</b>.
2809     *
2810     * @param message The message for the exception. Can be null.
2811     * @see #assertInLayoutOrScroll(String)
2812     */
2813    void assertNotInLayoutOrScroll(String message) {
2814        if (isComputingLayout()) {
2815            if (message == null) {
2816                throw new IllegalStateException("Cannot call this method while RecyclerView is "
2817                        + "computing a layout or scrolling" + exceptionLabel());
2818            }
2819            throw new IllegalStateException(message);
2820        }
2821        if (mDispatchScrollCounter > 0) {
2822            Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might"
2823                            + "be run during a measure & layout pass where you cannot change the"
2824                            + "RecyclerView data. Any method call that might change the structure"
2825                            + "of the RecyclerView or the adapter contents should be postponed to"
2826                            + "the next frame.",
2827                    new IllegalStateException("" + exceptionLabel()));
2828        }
2829    }
2830
2831    /**
2832     * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
2833     * to child views or this view's standard scrolling behavior.
2834     *
2835     * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
2836     * returns true from
2837     * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
2838     * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
2839     * for each incoming MotionEvent until the end of the gesture.</p>
2840     *
2841     * @param listener Listener to add
2842     * @see SimpleOnItemTouchListener
2843     */
2844    public void addOnItemTouchListener(@NonNull OnItemTouchListener listener) {
2845        mOnItemTouchListeners.add(listener);
2846    }
2847
2848    /**
2849     * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
2850     *
2851     * @param listener Listener to remove
2852     */
2853    public void removeOnItemTouchListener(@NonNull OnItemTouchListener listener) {
2854        mOnItemTouchListeners.remove(listener);
2855        if (mActiveOnItemTouchListener == listener) {
2856            mActiveOnItemTouchListener = null;
2857        }
2858    }
2859
2860    private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
2861        final int action = e.getAction();
2862        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
2863            mActiveOnItemTouchListener = null;
2864        }
2865
2866        final int listenerCount = mOnItemTouchListeners.size();
2867        for (int i = 0; i < listenerCount; i++) {
2868            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2869            if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
2870                mActiveOnItemTouchListener = listener;
2871                return true;
2872            }
2873        }
2874        return false;
2875    }
2876
2877    private boolean dispatchOnItemTouch(MotionEvent e) {
2878        final int action = e.getAction();
2879        if (mActiveOnItemTouchListener != null) {
2880            if (action == MotionEvent.ACTION_DOWN) {
2881                // Stale state from a previous gesture, we're starting a new one. Clear it.
2882                mActiveOnItemTouchListener = null;
2883            } else {
2884                mActiveOnItemTouchListener.onTouchEvent(this, e);
2885                if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
2886                    // Clean up for the next gesture.
2887                    mActiveOnItemTouchListener = null;
2888                }
2889                return true;
2890            }
2891        }
2892
2893        // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
2894        // as called from onInterceptTouchEvent; skip it.
2895        if (action != MotionEvent.ACTION_DOWN) {
2896            final int listenerCount = mOnItemTouchListeners.size();
2897            for (int i = 0; i < listenerCount; i++) {
2898                final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2899                if (listener.onInterceptTouchEvent(this, e)) {
2900                    mActiveOnItemTouchListener = listener;
2901                    return true;
2902                }
2903            }
2904        }
2905        return false;
2906    }
2907
2908    @Override
2909    public boolean onInterceptTouchEvent(MotionEvent e) {
2910        if (mLayoutFrozen) {
2911            // When layout is frozen,  RV does not intercept the motion event.
2912            // A child view e.g. a button may still get the click.
2913            return false;
2914        }
2915        if (dispatchOnItemTouchIntercept(e)) {
2916            cancelTouch();
2917            return true;
2918        }
2919
2920        if (mLayout == null) {
2921            return false;
2922        }
2923
2924        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2925        final boolean canScrollVertically = mLayout.canScrollVertically();
2926
2927        if (mVelocityTracker == null) {
2928            mVelocityTracker = VelocityTracker.obtain();
2929        }
2930        mVelocityTracker.addMovement(e);
2931
2932        final int action = e.getActionMasked();
2933        final int actionIndex = e.getActionIndex();
2934
2935        switch (action) {
2936            case MotionEvent.ACTION_DOWN:
2937                if (mIgnoreMotionEventTillDown) {
2938                    mIgnoreMotionEventTillDown = false;
2939                }
2940                mScrollPointerId = e.getPointerId(0);
2941                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2942                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2943
2944                if (mScrollState == SCROLL_STATE_SETTLING) {
2945                    getParent().requestDisallowInterceptTouchEvent(true);
2946                    setScrollState(SCROLL_STATE_DRAGGING);
2947                }
2948
2949                // Clear the nested offsets
2950                mNestedOffsets[0] = mNestedOffsets[1] = 0;
2951
2952                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2953                if (canScrollHorizontally) {
2954                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2955                }
2956                if (canScrollVertically) {
2957                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2958                }
2959                startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
2960                break;
2961
2962            case MotionEvent.ACTION_POINTER_DOWN:
2963                mScrollPointerId = e.getPointerId(actionIndex);
2964                mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2965                mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2966                break;
2967
2968            case MotionEvent.ACTION_MOVE: {
2969                final int index = e.findPointerIndex(mScrollPointerId);
2970                if (index < 0) {
2971                    Log.e(TAG, "Error processing scroll; pointer index for id "
2972                            + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2973                    return false;
2974                }
2975
2976                final int x = (int) (e.getX(index) + 0.5f);
2977                final int y = (int) (e.getY(index) + 0.5f);
2978                if (mScrollState != SCROLL_STATE_DRAGGING) {
2979                    final int dx = x - mInitialTouchX;
2980                    final int dy = y - mInitialTouchY;
2981                    boolean startScroll = false;
2982                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2983                        mLastTouchX = x;
2984                        startScroll = true;
2985                    }
2986                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2987                        mLastTouchY = y;
2988                        startScroll = true;
2989                    }
2990                    if (startScroll) {
2991                        setScrollState(SCROLL_STATE_DRAGGING);
2992                    }
2993                }
2994            } break;
2995
2996            case MotionEvent.ACTION_POINTER_UP: {
2997                onPointerUp(e);
2998            } break;
2999
3000            case MotionEvent.ACTION_UP: {
3001                mVelocityTracker.clear();
3002                stopNestedScroll(TYPE_TOUCH);
3003            } break;
3004
3005            case MotionEvent.ACTION_CANCEL: {
3006                cancelTouch();
3007            }
3008        }
3009        return mScrollState == SCROLL_STATE_DRAGGING;
3010    }
3011
3012    @Override
3013    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
3014        final int listenerCount = mOnItemTouchListeners.size();
3015        for (int i = 0; i < listenerCount; i++) {
3016            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
3017            listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
3018        }
3019        super.requestDisallowInterceptTouchEvent(disallowIntercept);
3020    }
3021
3022    @Override
3023    public boolean onTouchEvent(MotionEvent e) {
3024        if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
3025            return false;
3026        }
3027        if (dispatchOnItemTouch(e)) {
3028            cancelTouch();
3029            return true;
3030        }
3031
3032        if (mLayout == null) {
3033            return false;
3034        }
3035
3036        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
3037        final boolean canScrollVertically = mLayout.canScrollVertically();
3038
3039        if (mVelocityTracker == null) {
3040            mVelocityTracker = VelocityTracker.obtain();
3041        }
3042        boolean eventAddedToVelocityTracker = false;
3043
3044        final MotionEvent vtev = MotionEvent.obtain(e);
3045        final int action = e.getActionMasked();
3046        final int actionIndex = e.getActionIndex();
3047
3048        if (action == MotionEvent.ACTION_DOWN) {
3049            mNestedOffsets[0] = mNestedOffsets[1] = 0;
3050        }
3051        vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
3052
3053        switch (action) {
3054            case MotionEvent.ACTION_DOWN: {
3055                mScrollPointerId = e.getPointerId(0);
3056                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
3057                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
3058
3059                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
3060                if (canScrollHorizontally) {
3061                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
3062                }
3063                if (canScrollVertically) {
3064                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
3065                }
3066                startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
3067            } break;
3068
3069            case MotionEvent.ACTION_POINTER_DOWN: {
3070                mScrollPointerId = e.getPointerId(actionIndex);
3071                mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
3072                mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
3073            } break;
3074
3075            case MotionEvent.ACTION_MOVE: {
3076                final int index = e.findPointerIndex(mScrollPointerId);
3077                if (index < 0) {
3078                    Log.e(TAG, "Error processing scroll; pointer index for id "
3079                            + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
3080                    return false;
3081                }
3082
3083                final int x = (int) (e.getX(index) + 0.5f);
3084                final int y = (int) (e.getY(index) + 0.5f);
3085                int dx = mLastTouchX - x;
3086                int dy = mLastTouchY - y;
3087
3088                if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset, TYPE_TOUCH)) {
3089                    dx -= mScrollConsumed[0];
3090                    dy -= mScrollConsumed[1];
3091                    vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
3092                    // Updated the nested offsets
3093                    mNestedOffsets[0] += mScrollOffset[0];
3094                    mNestedOffsets[1] += mScrollOffset[1];
3095                }
3096
3097                if (mScrollState != SCROLL_STATE_DRAGGING) {
3098                    boolean startScroll = false;
3099                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
3100                        if (dx > 0) {
3101                            dx -= mTouchSlop;
3102                        } else {
3103                            dx += mTouchSlop;
3104                        }
3105                        startScroll = true;
3106                    }
3107                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
3108                        if (dy > 0) {
3109                            dy -= mTouchSlop;
3110                        } else {
3111                            dy += mTouchSlop;
3112                        }
3113                        startScroll = true;
3114                    }
3115                    if (startScroll) {
3116                        setScrollState(SCROLL_STATE_DRAGGING);
3117                    }
3118                }
3119
3120                if (mScrollState == SCROLL_STATE_DRAGGING) {
3121                    mLastTouchX = x - mScrollOffset[0];
3122                    mLastTouchY = y - mScrollOffset[1];
3123
3124                    if (scrollByInternal(
3125                            canScrollHorizontally ? dx : 0,
3126                            canScrollVertically ? dy : 0,
3127                            vtev)) {
3128                        getParent().requestDisallowInterceptTouchEvent(true);
3129                    }
3130                    if (mGapWorker != null && (dx != 0 || dy != 0)) {
3131                        mGapWorker.postFromTraversal(this, dx, dy);
3132                    }
3133                }
3134            } break;
3135
3136            case MotionEvent.ACTION_POINTER_UP: {
3137                onPointerUp(e);
3138            } break;
3139
3140            case MotionEvent.ACTION_UP: {
3141                mVelocityTracker.addMovement(vtev);
3142                eventAddedToVelocityTracker = true;
3143                mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
3144                final float xvel = canScrollHorizontally
3145                        ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
3146                final float yvel = canScrollVertically
3147                        ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
3148                if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
3149                    setScrollState(SCROLL_STATE_IDLE);
3150                }
3151                resetTouch();
3152            } break;
3153
3154            case MotionEvent.ACTION_CANCEL: {
3155                cancelTouch();
3156            } break;
3157        }
3158
3159        if (!eventAddedToVelocityTracker) {
3160            mVelocityTracker.addMovement(vtev);
3161        }
3162        vtev.recycle();
3163
3164        return true;
3165    }
3166
3167    private void resetTouch() {
3168        if (mVelocityTracker != null) {
3169            mVelocityTracker.clear();
3170        }
3171        stopNestedScroll(TYPE_TOUCH);
3172        releaseGlows();
3173    }
3174
3175    private void cancelTouch() {
3176        resetTouch();
3177        setScrollState(SCROLL_STATE_IDLE);
3178    }
3179
3180    private void onPointerUp(MotionEvent e) {
3181        final int actionIndex = e.getActionIndex();
3182        if (e.getPointerId(actionIndex) == mScrollPointerId) {
3183            // Pick a new pointer to pick up the slack.
3184            final int newIndex = actionIndex == 0 ? 1 : 0;
3185            mScrollPointerId = e.getPointerId(newIndex);
3186            mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
3187            mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
3188        }
3189    }
3190
3191    @Override
3192    public boolean onGenericMotionEvent(MotionEvent event) {
3193        if (mLayout == null) {
3194            return false;
3195        }
3196        if (mLayoutFrozen) {
3197            return false;
3198        }
3199        if (event.getAction() == MotionEventCompat.ACTION_SCROLL) {
3200            final float vScroll, hScroll;
3201            if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
3202                if (mLayout.canScrollVertically()) {
3203                    // Inverse the sign of the vertical scroll to align the scroll orientation
3204                    // with AbsListView.
3205                    vScroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
3206                } else {
3207                    vScroll = 0f;
3208                }
3209                if (mLayout.canScrollHorizontally()) {
3210                    hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
3211                } else {
3212                    hScroll = 0f;
3213                }
3214            } else if ((event.getSource() & InputDeviceCompat.SOURCE_ROTARY_ENCODER) != 0) {
3215                final float axisScroll = event.getAxisValue(MotionEventCompat.AXIS_SCROLL);
3216                if (mLayout.canScrollVertically()) {
3217                    // Invert the sign of the vertical scroll to align the scroll orientation
3218                    // with AbsListView.
3219                    vScroll = -axisScroll;
3220                    hScroll = 0f;
3221                } else if (mLayout.canScrollHorizontally()) {
3222                    vScroll = 0f;
3223                    hScroll = axisScroll;
3224                } else {
3225                    vScroll = 0f;
3226                    hScroll = 0f;
3227                }
3228            } else {
3229                vScroll = 0f;
3230                hScroll = 0f;
3231            }
3232
3233            if (vScroll != 0 || hScroll != 0) {
3234                scrollByInternal((int) (hScroll * mScaledHorizontalScrollFactor),
3235                        (int) (vScroll * mScaledVerticalScrollFactor), event);
3236            }
3237        }
3238        return false;
3239    }
3240
3241    @Override
3242    protected void onMeasure(int widthSpec, int heightSpec) {
3243        if (mLayout == null) {
3244            defaultOnMeasure(widthSpec, heightSpec);
3245            return;
3246        }
3247        if (mLayout.isAutoMeasureEnabled()) {
3248            final int widthMode = MeasureSpec.getMode(widthSpec);
3249            final int heightMode = MeasureSpec.getMode(heightSpec);
3250
3251            /**
3252             * This specific call should be considered deprecated and replaced with
3253             * {@link #defaultOnMeasure(int, int)}. It can't actually be replaced as it could
3254             * break existing third party code but all documentation directs developers to not
3255             * override {@link LayoutManager#onMeasure(int, int)} when
3256             * {@link LayoutManager#isAutoMeasureEnabled()} returns true.
3257             */
3258            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3259
3260            final boolean measureSpecModeIsExactly =
3261                    widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
3262            if (measureSpecModeIsExactly || mAdapter == null) {
3263                return;
3264            }
3265
3266            if (mState.mLayoutStep == State.STEP_START) {
3267                dispatchLayoutStep1();
3268            }
3269            // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
3270            // consistency
3271            mLayout.setMeasureSpecs(widthSpec, heightSpec);
3272            mState.mIsMeasuring = true;
3273            dispatchLayoutStep2();
3274
3275            // now we can get the width and height from the children.
3276            mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
3277
3278            // if RecyclerView has non-exact width and height and if there is at least one child
3279            // which also has non-exact width & height, we have to re-measure.
3280            if (mLayout.shouldMeasureTwice()) {
3281                mLayout.setMeasureSpecs(
3282                        MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
3283                        MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
3284                mState.mIsMeasuring = true;
3285                dispatchLayoutStep2();
3286                // now we can get the width and height from the children.
3287                mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
3288            }
3289        } else {
3290            if (mHasFixedSize) {
3291                mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3292                return;
3293            }
3294            // custom onMeasure
3295            if (mAdapterUpdateDuringMeasure) {
3296                startInterceptRequestLayout();
3297                onEnterLayoutOrScroll();
3298                processAdapterUpdatesAndSetAnimationFlags();
3299                onExitLayoutOrScroll();
3300
3301                if (mState.mRunPredictiveAnimations) {
3302                    mState.mInPreLayout = true;
3303                } else {
3304                    // consume remaining updates to provide a consistent state with the layout pass.
3305                    mAdapterHelper.consumeUpdatesInOnePass();
3306                    mState.mInPreLayout = false;
3307                }
3308                mAdapterUpdateDuringMeasure = false;
3309                stopInterceptRequestLayout(false);
3310            } else if (mState.mRunPredictiveAnimations) {
3311                // If mAdapterUpdateDuringMeasure is false and mRunPredictiveAnimations is true:
3312                // this means there is already an onMeasure() call performed to handle the pending
3313                // adapter change, two onMeasure() calls can happen if RV is a child of LinearLayout
3314                // with layout_width=MATCH_PARENT. RV cannot call LM.onMeasure() second time
3315                // because getViewForPosition() will crash when LM uses a child to measure.
3316                setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
3317                return;
3318            }
3319
3320            if (mAdapter != null) {
3321                mState.mItemCount = mAdapter.getItemCount();
3322            } else {
3323                mState.mItemCount = 0;
3324            }
3325            startInterceptRequestLayout();
3326            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3327            stopInterceptRequestLayout(false);
3328            mState.mInPreLayout = false; // clear
3329        }
3330    }
3331
3332    /**
3333     * An implementation of {@link View#onMeasure(int, int)} to fall back to in various scenarios
3334     * where this RecyclerView is otherwise lacking better information.
3335     */
3336    void defaultOnMeasure(int widthSpec, int heightSpec) {
3337        // calling LayoutManager here is not pretty but that API is already public and it is better
3338        // than creating another method since this is internal.
3339        final int width = LayoutManager.chooseSize(widthSpec,
3340                getPaddingLeft() + getPaddingRight(),
3341                ViewCompat.getMinimumWidth(this));
3342        final int height = LayoutManager.chooseSize(heightSpec,
3343                getPaddingTop() + getPaddingBottom(),
3344                ViewCompat.getMinimumHeight(this));
3345
3346        setMeasuredDimension(width, height);
3347    }
3348
3349    @Override
3350    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
3351        super.onSizeChanged(w, h, oldw, oldh);
3352        if (w != oldw || h != oldh) {
3353            invalidateGlows();
3354            // layout's w/h are updated during measure/layout steps.
3355        }
3356    }
3357
3358    /**
3359     * Sets the {@link ItemAnimator} that will handle animations involving changes
3360     * to the items in this RecyclerView. By default, RecyclerView instantiates and
3361     * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
3362     * enabled for the RecyclerView depends on the ItemAnimator and whether
3363     * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
3364     * supports item animations}.
3365     *
3366     * @param animator The ItemAnimator being set. If null, no animations will occur
3367     * when changes occur to the items in this RecyclerView.
3368     */
3369    public void setItemAnimator(@Nullable ItemAnimator animator) {
3370        if (mItemAnimator != null) {
3371            mItemAnimator.endAnimations();
3372            mItemAnimator.setListener(null);
3373        }
3374        mItemAnimator = animator;
3375        if (mItemAnimator != null) {
3376            mItemAnimator.setListener(mItemAnimatorListener);
3377        }
3378    }
3379
3380    void onEnterLayoutOrScroll() {
3381        mLayoutOrScrollCounter++;
3382    }
3383
3384    void onExitLayoutOrScroll() {
3385        onExitLayoutOrScroll(true);
3386    }
3387
3388    void onExitLayoutOrScroll(boolean enableChangeEvents) {
3389        mLayoutOrScrollCounter--;
3390        if (mLayoutOrScrollCounter < 1) {
3391            if (DEBUG && mLayoutOrScrollCounter < 0) {
3392                throw new IllegalStateException("layout or scroll counter cannot go below zero."
3393                        + "Some calls are not matching" + exceptionLabel());
3394            }
3395            mLayoutOrScrollCounter = 0;
3396            if (enableChangeEvents) {
3397                dispatchContentChangedIfNecessary();
3398                dispatchPendingImportantForAccessibilityChanges();
3399            }
3400        }
3401    }
3402
3403    boolean isAccessibilityEnabled() {
3404        return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
3405    }
3406
3407    private void dispatchContentChangedIfNecessary() {
3408        final int flags = mEatenAccessibilityChangeFlags;
3409        mEatenAccessibilityChangeFlags = 0;
3410        if (flags != 0 && isAccessibilityEnabled()) {
3411            final AccessibilityEvent event = AccessibilityEvent.obtain();
3412            event.setEventType(AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
3413            AccessibilityEventCompat.setContentChangeTypes(event, flags);
3414            sendAccessibilityEventUnchecked(event);
3415        }
3416    }
3417
3418    /**
3419     * Returns whether RecyclerView is currently computing a layout.
3420     * <p>
3421     * If this method returns true, it means that RecyclerView is in a lockdown state and any
3422     * attempt to update adapter contents will result in an exception because adapter contents
3423     * cannot be changed while RecyclerView is trying to compute the layout.
3424     * <p>
3425     * It is very unlikely that your code will be running during this state as it is
3426     * called by the framework when a layout traversal happens or RecyclerView starts to scroll
3427     * in response to system events (touch, accessibility etc).
3428     * <p>
3429     * This case may happen if you have some custom logic to change adapter contents in
3430     * response to a View callback (e.g. focus change callback) which might be triggered during a
3431     * layout calculation. In these cases, you should just postpone the change using a Handler or a
3432     * similar mechanism.
3433     *
3434     * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
3435     *         otherwise
3436     */
3437    public boolean isComputingLayout() {
3438        return mLayoutOrScrollCounter > 0;
3439    }
3440
3441    /**
3442     * Returns true if an accessibility event should not be dispatched now. This happens when an
3443     * accessibility request arrives while RecyclerView does not have a stable state which is very
3444     * hard to handle for a LayoutManager. Instead, this method records necessary information about
3445     * the event and dispatches a window change event after the critical section is finished.
3446     *
3447     * @return True if the accessibility event should be postponed.
3448     */
3449    boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
3450        if (isComputingLayout()) {
3451            int type = 0;
3452            if (event != null) {
3453                type = AccessibilityEventCompat.getContentChangeTypes(event);
3454            }
3455            if (type == 0) {
3456                type = AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED;
3457            }
3458            mEatenAccessibilityChangeFlags |= type;
3459            return true;
3460        }
3461        return false;
3462    }
3463
3464    @Override
3465    public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
3466        if (shouldDeferAccessibilityEvent(event)) {
3467            return;
3468        }
3469        super.sendAccessibilityEventUnchecked(event);
3470    }
3471
3472    /**
3473     * Gets the current ItemAnimator for this RecyclerView. A null return value
3474     * indicates that there is no animator and that item changes will happen without
3475     * any animations. By default, RecyclerView instantiates and
3476     * uses an instance of {@link DefaultItemAnimator}.
3477     *
3478     * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
3479     * when changes occur to the items in this RecyclerView.
3480     */
3481    @Nullable
3482    public ItemAnimator getItemAnimator() {
3483        return mItemAnimator;
3484    }
3485
3486    /**
3487     * Post a runnable to the next frame to run pending item animations. Only the first such
3488     * request will be posted, governed by the mPostedAnimatorRunner flag.
3489     */
3490    void postAnimationRunner() {
3491        if (!mPostedAnimatorRunner && mIsAttached) {
3492            ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
3493            mPostedAnimatorRunner = true;
3494        }
3495    }
3496
3497    private boolean predictiveItemAnimationsEnabled() {
3498        return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
3499    }
3500
3501    /**
3502     * Consumes adapter updates and calculates which type of animations we want to run.
3503     * Called in onMeasure and dispatchLayout.
3504     * <p>
3505     * This method may process only the pre-layout state of updates or all of them.
3506     */
3507    private void processAdapterUpdatesAndSetAnimationFlags() {
3508        if (mDataSetHasChangedAfterLayout) {
3509            // Processing these items have no value since data set changed unexpectedly.
3510            // Instead, we just reset it.
3511            mAdapterHelper.reset();
3512            if (mDispatchItemsChangedEvent) {
3513                mLayout.onItemsChanged(this);
3514            }
3515        }
3516        // simple animations are a subset of advanced animations (which will cause a
3517        // pre-layout step)
3518        // If layout supports predictive animations, pre-process to decide if we want to run them
3519        if (predictiveItemAnimationsEnabled()) {
3520            mAdapterHelper.preProcess();
3521        } else {
3522            mAdapterHelper.consumeUpdatesInOnePass();
3523        }
3524        boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
3525        mState.mRunSimpleAnimations = mFirstLayoutComplete
3526                && mItemAnimator != null
3527                && (mDataSetHasChangedAfterLayout
3528                || animationTypeSupported
3529                || mLayout.mRequestedSimpleAnimations)
3530                && (!mDataSetHasChangedAfterLayout
3531                || mAdapter.hasStableIds());
3532        mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
3533                && animationTypeSupported
3534                && !mDataSetHasChangedAfterLayout
3535                && predictiveItemAnimationsEnabled();
3536    }
3537
3538    /**
3539     * Wrapper around layoutChildren() that handles animating changes caused by layout.
3540     * Animations work on the assumption that there are five different kinds of items
3541     * in play:
3542     * PERSISTENT: items are visible before and after layout
3543     * REMOVED: items were visible before layout and were removed by the app
3544     * ADDED: items did not exist before layout and were added by the app
3545     * DISAPPEARING: items exist in the data set before/after, but changed from
3546     * visible to non-visible in the process of layout (they were moved off
3547     * screen as a side-effect of other changes)
3548     * APPEARING: items exist in the data set before/after, but changed from
3549     * non-visible to visible in the process of layout (they were moved on
3550     * screen as a side-effect of other changes)
3551     * The overall approach figures out what items exist before/after layout and
3552     * infers one of the five above states for each of the items. Then the animations
3553     * are set up accordingly:
3554     * PERSISTENT views are animated via
3555     * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3556     * DISAPPEARING views are animated via
3557     * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3558     * APPEARING views are animated via
3559     * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3560     * and changed views are animated via
3561     * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
3562     */
3563    void dispatchLayout() {
3564        if (mAdapter == null) {
3565            Log.e(TAG, "No adapter attached; skipping layout");
3566            // leave the state in START
3567            return;
3568        }
3569        if (mLayout == null) {
3570            Log.e(TAG, "No layout manager attached; skipping layout");
3571            // leave the state in START
3572            return;
3573        }
3574        mState.mIsMeasuring = false;
3575        if (mState.mLayoutStep == State.STEP_START) {
3576            dispatchLayoutStep1();
3577            mLayout.setExactMeasureSpecsFrom(this);
3578            dispatchLayoutStep2();
3579        } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
3580                || mLayout.getHeight() != getHeight()) {
3581            // First 2 steps are done in onMeasure but looks like we have to run again due to
3582            // changed size.
3583            mLayout.setExactMeasureSpecsFrom(this);
3584            dispatchLayoutStep2();
3585        } else {
3586            // always make sure we sync them (to ensure mode is exact)
3587            mLayout.setExactMeasureSpecsFrom(this);
3588        }
3589        dispatchLayoutStep3();
3590    }
3591
3592    private void saveFocusInfo() {
3593        View child = null;
3594        if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
3595            child = getFocusedChild();
3596        }
3597
3598        final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
3599        if (focusedVh == null) {
3600            resetFocusInfo();
3601        } else {
3602            mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
3603            // mFocusedItemPosition should hold the current adapter position of the previously
3604            // focused item. If the item is removed, we store the previous adapter position of the
3605            // removed item.
3606            mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION
3607                    : (focusedVh.isRemoved() ? focusedVh.mOldPosition
3608                            : focusedVh.getAdapterPosition());
3609            mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
3610        }
3611    }
3612
3613    private void resetFocusInfo() {
3614        mState.mFocusedItemId = NO_ID;
3615        mState.mFocusedItemPosition = NO_POSITION;
3616        mState.mFocusedSubChildId = View.NO_ID;
3617    }
3618
3619    /**
3620     * Finds the best view candidate to request focus on using mFocusedItemPosition index of the
3621     * previously focused item. It first traverses the adapter forward to find a focusable candidate
3622     * and if no such candidate is found, it reverses the focus search direction for the items
3623     * before the mFocusedItemPosition'th index;
3624     * @return The best candidate to request focus on, or null if no such candidate exists. Null
3625     * indicates all the existing adapter items are unfocusable.
3626     */
3627    @Nullable
3628    private View findNextViewToFocus() {
3629        int startFocusSearchIndex = mState.mFocusedItemPosition != -1 ? mState.mFocusedItemPosition
3630                : 0;
3631        ViewHolder nextFocus;
3632        final int itemCount = mState.getItemCount();
3633        for (int i = startFocusSearchIndex; i < itemCount; i++) {
3634            nextFocus = findViewHolderForAdapterPosition(i);
3635            if (nextFocus == null) {
3636                break;
3637            }
3638            if (nextFocus.itemView.hasFocusable()) {
3639                return nextFocus.itemView;
3640            }
3641        }
3642        final int limit = Math.min(itemCount, startFocusSearchIndex);
3643        for (int i = limit - 1; i >= 0; i--) {
3644            nextFocus = findViewHolderForAdapterPosition(i);
3645            if (nextFocus == null) {
3646                return null;
3647            }
3648            if (nextFocus.itemView.hasFocusable()) {
3649                return nextFocus.itemView;
3650            }
3651        }
3652        return null;
3653    }
3654
3655    private void recoverFocusFromState() {
3656        if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()
3657                || getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
3658                || (getDescendantFocusability() == FOCUS_BEFORE_DESCENDANTS && isFocused())) {
3659            // No-op if either of these cases happens:
3660            // 1. RV has no focus, or 2. RV blocks focus to its children, or 3. RV takes focus
3661            // before its children and is focused (i.e. it already stole the focus away from its
3662            // descendants).
3663            return;
3664        }
3665        // only recover focus if RV itself has the focus or the focused view is hidden
3666        if (!isFocused()) {
3667            final View focusedChild = getFocusedChild();
3668            if (IGNORE_DETACHED_FOCUSED_CHILD
3669                    && (focusedChild.getParent() == null || !focusedChild.hasFocus())) {
3670                // Special handling of API 15-. A focused child can be invalid because mFocus is not
3671                // cleared when the child is detached (mParent = null),
3672                // This happens because clearFocus on API 15- does not invalidate mFocus of its
3673                // parent when this child is detached.
3674                // For API 16+, this is not an issue because requestFocus takes care of clearing the
3675                // prior detached focused child. For API 15- the problem happens in 2 cases because
3676                // clearChild does not call clearChildFocus on RV: 1. setFocusable(false) is called
3677                // for the current focused item which calls clearChild or 2. when the prior focused
3678                // child is removed, removeDetachedView called in layout step 3 which calls
3679                // clearChild. We should ignore this invalid focused child in all our calculations
3680                // for the next view to receive focus, and apply the focus recovery logic instead.
3681                if (mChildHelper.getChildCount() == 0) {
3682                    // No children left. Request focus on the RV itself since one of its children
3683                    // was holding focus previously.
3684                    requestFocus();
3685                    return;
3686                }
3687            } else if (!mChildHelper.isHidden(focusedChild)) {
3688                // If the currently focused child is hidden, apply the focus recovery logic.
3689                // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
3690                return;
3691            }
3692        }
3693        ViewHolder focusTarget = null;
3694        // RV first attempts to locate the previously focused item to request focus on using
3695        // mFocusedItemId. If such an item no longer exists, it then makes a best-effort attempt to
3696        // find the next best candidate to request focus on based on mFocusedItemPosition.
3697        if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
3698            focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
3699        }
3700        View viewToFocus = null;
3701        if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView)
3702                || !focusTarget.itemView.hasFocusable()) {
3703            if (mChildHelper.getChildCount() > 0) {
3704                // At this point, RV has focus and either of these conditions are true:
3705                // 1. There's no previously focused item either because RV received focused before
3706                // layout, or the previously focused item was removed, or RV doesn't have stable IDs
3707                // 2. Previous focus child is hidden, or 3. Previous focused child is no longer
3708                // focusable. In either of these cases, we make sure that RV still passes down the
3709                // focus to one of its focusable children using a best-effort algorithm.
3710                viewToFocus = findNextViewToFocus();
3711            }
3712        } else {
3713            // looks like the focused item has been replaced with another view that represents the
3714            // same item in the adapter. Request focus on that.
3715            viewToFocus = focusTarget.itemView;
3716        }
3717
3718        if (viewToFocus != null) {
3719            if (mState.mFocusedSubChildId != NO_ID) {
3720                View child = viewToFocus.findViewById(mState.mFocusedSubChildId);
3721                if (child != null && child.isFocusable()) {
3722                    viewToFocus = child;
3723                }
3724            }
3725            viewToFocus.requestFocus();
3726        }
3727    }
3728
3729    private int getDeepestFocusedViewWithId(View view) {
3730        int lastKnownId = view.getId();
3731        while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
3732            view = ((ViewGroup) view).getFocusedChild();
3733            final int id = view.getId();
3734            if (id != View.NO_ID) {
3735                lastKnownId = view.getId();
3736            }
3737        }
3738        return lastKnownId;
3739    }
3740
3741    final void fillRemainingScrollValues(State state) {
3742        if (getScrollState() == SCROLL_STATE_SETTLING) {
3743            final OverScroller scroller = mViewFlinger.mScroller;
3744            state.mRemainingScrollHorizontal = scroller.getFinalX() - scroller.getCurrX();
3745            state.mRemainingScrollVertical = scroller.getFinalY() - scroller.getCurrY();
3746        } else {
3747            state.mRemainingScrollHorizontal = 0;
3748            state.mRemainingScrollVertical = 0;
3749        }
3750    }
3751
3752    /**
3753     * The first step of a layout where we;
3754     * - process adapter updates
3755     * - decide which animation should run
3756     * - save information about current views
3757     * - If necessary, run predictive layout and save its information
3758     */
3759    private void dispatchLayoutStep1() {
3760        mState.assertLayoutStep(State.STEP_START);
3761        fillRemainingScrollValues(mState);
3762        mState.mIsMeasuring = false;
3763        startInterceptRequestLayout();
3764        mViewInfoStore.clear();
3765        onEnterLayoutOrScroll();
3766        processAdapterUpdatesAndSetAnimationFlags();
3767        saveFocusInfo();
3768        mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
3769        mItemsAddedOrRemoved = mItemsChanged = false;
3770        mState.mInPreLayout = mState.mRunPredictiveAnimations;
3771        mState.mItemCount = mAdapter.getItemCount();
3772        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3773
3774        if (mState.mRunSimpleAnimations) {
3775            // Step 0: Find out where all non-removed items are, pre-layout
3776            int count = mChildHelper.getChildCount();
3777            for (int i = 0; i < count; ++i) {
3778                final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3779                if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
3780                    continue;
3781                }
3782                final ItemHolderInfo animationInfo = mItemAnimator
3783                        .recordPreLayoutInformation(mState, holder,
3784                                ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
3785                                holder.getUnmodifiedPayloads());
3786                mViewInfoStore.addToPreLayout(holder, animationInfo);
3787                if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
3788                        && !holder.shouldIgnore() && !holder.isInvalid()) {
3789                    long key = getChangedHolderKey(holder);
3790                    // This is NOT the only place where a ViewHolder is added to old change holders
3791                    // list. There is another case where:
3792                    //    * A VH is currently hidden but not deleted
3793                    //    * The hidden item is changed in the adapter
3794                    //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
3795                    // When this case is detected, RV will un-hide that view and add to the old
3796                    // change holders list.
3797                    mViewInfoStore.addToOldChangeHolders(key, holder);
3798                }
3799            }
3800        }
3801        if (mState.mRunPredictiveAnimations) {
3802            // Step 1: run prelayout: This will use the old positions of items. The layout manager
3803            // is expected to layout everything, even removed items (though not to add removed
3804            // items back to the container). This gives the pre-layout position of APPEARING views
3805            // which come into existence as part of the real layout.
3806
3807            // Save old positions so that LayoutManager can run its mapping logic.
3808            saveOldPositions();
3809            final boolean didStructureChange = mState.mStructureChanged;
3810            mState.mStructureChanged = false;
3811            // temporarily disable flag because we are asking for previous layout
3812            mLayout.onLayoutChildren(mRecycler, mState);
3813            mState.mStructureChanged = didStructureChange;
3814
3815            for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
3816                final View child = mChildHelper.getChildAt(i);
3817                final ViewHolder viewHolder = getChildViewHolderInt(child);
3818                if (viewHolder.shouldIgnore()) {
3819                    continue;
3820                }
3821                if (!mViewInfoStore.isInPreLayout(viewHolder)) {
3822                    int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
3823                    boolean wasHidden = viewHolder
3824                            .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3825                    if (!wasHidden) {
3826                        flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
3827                    }
3828                    final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
3829                            mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
3830                    if (wasHidden) {
3831                        recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
3832                    } else {
3833                        mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
3834                    }
3835                }
3836            }
3837            // we don't process disappearing list because they may re-appear in post layout pass.
3838            clearOldPositions();
3839        } else {
3840            clearOldPositions();
3841        }
3842        onExitLayoutOrScroll();
3843        stopInterceptRequestLayout(false);
3844        mState.mLayoutStep = State.STEP_LAYOUT;
3845    }
3846
3847    /**
3848     * The second layout step where we do the actual layout of the views for the final state.
3849     * This step might be run multiple times if necessary (e.g. measure).
3850     */
3851    private void dispatchLayoutStep2() {
3852        startInterceptRequestLayout();
3853        onEnterLayoutOrScroll();
3854        mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
3855        mAdapterHelper.consumeUpdatesInOnePass();
3856        mState.mItemCount = mAdapter.getItemCount();
3857        mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
3858
3859        // Step 2: Run layout
3860        mState.mInPreLayout = false;
3861        mLayout.onLayoutChildren(mRecycler, mState);
3862
3863        mState.mStructureChanged = false;
3864        mPendingSavedState = null;
3865
3866        // onLayoutChildren may have caused client code to disable item animations; re-check
3867        mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
3868        mState.mLayoutStep = State.STEP_ANIMATIONS;
3869        onExitLayoutOrScroll();
3870        stopInterceptRequestLayout(false);
3871    }
3872
3873    /**
3874     * The final step of the layout where we save the information about views for animations,
3875     * trigger animations and do any necessary cleanup.
3876     */
3877    private void dispatchLayoutStep3() {
3878        mState.assertLayoutStep(State.STEP_ANIMATIONS);
3879        startInterceptRequestLayout();
3880        onEnterLayoutOrScroll();
3881        mState.mLayoutStep = State.STEP_START;
3882        if (mState.mRunSimpleAnimations) {
3883            // Step 3: Find out where things are now, and process change animations.
3884            // traverse list in reverse because we may call animateChange in the loop which may
3885            // remove the target view holder.
3886            for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
3887                ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3888                if (holder.shouldIgnore()) {
3889                    continue;
3890                }
3891                long key = getChangedHolderKey(holder);
3892                final ItemHolderInfo animationInfo = mItemAnimator
3893                        .recordPostLayoutInformation(mState, holder);
3894                ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
3895                if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
3896                    // run a change animation
3897
3898                    // If an Item is CHANGED but the updated version is disappearing, it creates
3899                    // a conflicting case.
3900                    // Since a view that is marked as disappearing is likely to be going out of
3901                    // bounds, we run a change animation. Both views will be cleaned automatically
3902                    // once their animations finish.
3903                    // On the other hand, if it is the same view holder instance, we run a
3904                    // disappearing animation instead because we are not going to rebind the updated
3905                    // VH unless it is enforced by the layout manager.
3906                    final boolean oldDisappearing = mViewInfoStore.isDisappearing(
3907                            oldChangeViewHolder);
3908                    final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
3909                    if (oldDisappearing && oldChangeViewHolder == holder) {
3910                        // run disappear animation instead of change
3911                        mViewInfoStore.addToPostLayout(holder, animationInfo);
3912                    } else {
3913                        final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
3914                                oldChangeViewHolder);
3915                        // we add and remove so that any post info is merged.
3916                        mViewInfoStore.addToPostLayout(holder, animationInfo);
3917                        ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
3918                        if (preInfo == null) {
3919                            handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
3920                        } else {
3921                            animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
3922                                    oldDisappearing, newDisappearing);
3923                        }
3924                    }
3925                } else {
3926                    mViewInfoStore.addToPostLayout(holder, animationInfo);
3927                }
3928            }
3929
3930            // Step 4: Process view info lists and trigger animations
3931            mViewInfoStore.process(mViewInfoProcessCallback);
3932        }
3933
3934        mLayout.removeAndRecycleScrapInt(mRecycler);
3935        mState.mPreviousLayoutItemCount = mState.mItemCount;
3936        mDataSetHasChangedAfterLayout = false;
3937        mDispatchItemsChangedEvent = false;
3938        mState.mRunSimpleAnimations = false;
3939
3940        mState.mRunPredictiveAnimations = false;
3941        mLayout.mRequestedSimpleAnimations = false;
3942        if (mRecycler.mChangedScrap != null) {
3943            mRecycler.mChangedScrap.clear();
3944        }
3945        if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
3946            // Initial prefetch has expanded cache, so reset until next prefetch.
3947            // This prevents initial prefetches from expanding the cache permanently.
3948            mLayout.mPrefetchMaxCountObserved = 0;
3949            mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
3950            mRecycler.updateViewCacheSize();
3951        }
3952
3953        mLayout.onLayoutCompleted(mState);
3954        onExitLayoutOrScroll();
3955        stopInterceptRequestLayout(false);
3956        mViewInfoStore.clear();
3957        if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
3958            dispatchOnScrolled(0, 0);
3959        }
3960        recoverFocusFromState();
3961        resetFocusInfo();
3962    }
3963
3964    /**
3965     * This handles the case where there is an unexpected VH missing in the pre-layout map.
3966     * <p>
3967     * We might be able to detect the error in the application which will help the developer to
3968     * resolve the issue.
3969     * <p>
3970     * If it is not an expected error, we at least print an error to notify the developer and ignore
3971     * the animation.
3972     *
3973     * https://code.google.com/p/android/issues/detail?id=193958
3974     *
3975     * @param key The change key
3976     * @param holder Current ViewHolder
3977     * @param oldChangeViewHolder Changed ViewHolder
3978     */
3979    private void handleMissingPreInfoForChangeError(long key,
3980            ViewHolder holder, ViewHolder oldChangeViewHolder) {
3981        // check if two VH have the same key, if so, print that as an error
3982        final int childCount = mChildHelper.getChildCount();
3983        for (int i = 0; i < childCount; i++) {
3984            View view = mChildHelper.getChildAt(i);
3985            ViewHolder other = getChildViewHolderInt(view);
3986            if (other == holder) {
3987                continue;
3988            }
3989            final long otherKey = getChangedHolderKey(other);
3990            if (otherKey == key) {
3991                if (mAdapter != null && mAdapter.hasStableIds()) {
3992                    throw new IllegalStateException("Two different ViewHolders have the same stable"
3993                            + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
3994                            + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
3995                            + exceptionLabel());
3996                } else {
3997                    throw new IllegalStateException("Two different ViewHolders have the same change"
3998                            + " ID. This might happen due to inconsistent Adapter update events or"
3999                            + " if the LayoutManager lays out the same View multiple times."
4000                            + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
4001                            + exceptionLabel());
4002                }
4003            }
4004        }
4005        // Very unlikely to happen but if it does, notify the developer.
4006        Log.e(TAG, "Problem while matching changed view holders with the new"
4007                + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
4008                + " cannot be found but it is necessary for " + holder + exceptionLabel());
4009    }
4010
4011    /**
4012     * Records the animation information for a view holder that was bounced from hidden list. It
4013     * also clears the bounce back flag.
4014     */
4015    void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
4016            ItemHolderInfo animationInfo) {
4017        // looks like this view bounced back from hidden list!
4018        viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
4019        if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
4020                && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
4021            long key = getChangedHolderKey(viewHolder);
4022            mViewInfoStore.addToOldChangeHolders(key, viewHolder);
4023        }
4024        mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
4025    }
4026
4027    private void findMinMaxChildLayoutPositions(int[] into) {
4028        final int count = mChildHelper.getChildCount();
4029        if (count == 0) {
4030            into[0] = NO_POSITION;
4031            into[1] = NO_POSITION;
4032            return;
4033        }
4034        int minPositionPreLayout = Integer.MAX_VALUE;
4035        int maxPositionPreLayout = Integer.MIN_VALUE;
4036        for (int i = 0; i < count; ++i) {
4037            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
4038            if (holder.shouldIgnore()) {
4039                continue;
4040            }
4041            final int pos = holder.getLayoutPosition();
4042            if (pos < minPositionPreLayout) {
4043                minPositionPreLayout = pos;
4044            }
4045            if (pos > maxPositionPreLayout) {
4046                maxPositionPreLayout = pos;
4047            }
4048        }
4049        into[0] = minPositionPreLayout;
4050        into[1] = maxPositionPreLayout;
4051    }
4052
4053    private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
4054        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
4055        return mMinMaxLayoutPositions[0] != minPositionPreLayout
4056                || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
4057    }
4058
4059    @Override
4060    protected void removeDetachedView(View child, boolean animate) {
4061        ViewHolder vh = getChildViewHolderInt(child);
4062        if (vh != null) {
4063            if (vh.isTmpDetached()) {
4064                vh.clearTmpDetachFlag();
4065            } else if (!vh.shouldIgnore()) {
4066                throw new IllegalArgumentException("Called removeDetachedView with a view which"
4067                        + " is not flagged as tmp detached." + vh + exceptionLabel());
4068            }
4069        }
4070
4071        // Clear any android.view.animation.Animation that may prevent the item from
4072        // detaching when being removed. If a child is re-added before the
4073        // lazy detach occurs, it will receive invalid attach/detach sequencing.
4074        child.clearAnimation();
4075
4076        dispatchChildDetached(child);
4077        super.removeDetachedView(child, animate);
4078    }
4079
4080    /**
4081     * Returns a unique key to be used while handling change animations.
4082     * It might be child's position or stable id depending on the adapter type.
4083     */
4084    long getChangedHolderKey(ViewHolder holder) {
4085        return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
4086    }
4087
4088    void animateAppearance(@NonNull ViewHolder itemHolder,
4089            @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
4090        itemHolder.setIsRecyclable(false);
4091        if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
4092            postAnimationRunner();
4093        }
4094    }
4095
4096    void animateDisappearance(@NonNull ViewHolder holder,
4097            @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
4098        addAnimatingView(holder);
4099        holder.setIsRecyclable(false);
4100        if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
4101            postAnimationRunner();
4102        }
4103    }
4104
4105    private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
4106            @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
4107            boolean oldHolderDisappearing, boolean newHolderDisappearing) {
4108        oldHolder.setIsRecyclable(false);
4109        if (oldHolderDisappearing) {
4110            addAnimatingView(oldHolder);
4111        }
4112        if (oldHolder != newHolder) {
4113            if (newHolderDisappearing) {
4114                addAnimatingView(newHolder);
4115            }
4116            oldHolder.mShadowedHolder = newHolder;
4117            // old holder should disappear after animation ends
4118            addAnimatingView(oldHolder);
4119            mRecycler.unscrapView(oldHolder);
4120            newHolder.setIsRecyclable(false);
4121            newHolder.mShadowingHolder = oldHolder;
4122        }
4123        if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
4124            postAnimationRunner();
4125        }
4126    }
4127
4128    @Override
4129    protected void onLayout(boolean changed, int l, int t, int r, int b) {
4130        TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
4131        dispatchLayout();
4132        TraceCompat.endSection();
4133        mFirstLayoutComplete = true;
4134    }
4135
4136    @Override
4137    public void requestLayout() {
4138        if (mInterceptRequestLayoutDepth == 0 && !mLayoutFrozen) {
4139            super.requestLayout();
4140        } else {
4141            mLayoutWasDefered = true;
4142        }
4143    }
4144
4145    void markItemDecorInsetsDirty() {
4146        final int childCount = mChildHelper.getUnfilteredChildCount();
4147        for (int i = 0; i < childCount; i++) {
4148            final View child = mChildHelper.getUnfilteredChildAt(i);
4149            ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
4150        }
4151        mRecycler.markItemDecorInsetsDirty();
4152    }
4153
4154    @Override
4155    public void draw(Canvas c) {
4156        super.draw(c);
4157
4158        final int count = mItemDecorations.size();
4159        for (int i = 0; i < count; i++) {
4160            mItemDecorations.get(i).onDrawOver(c, this, mState);
4161        }
4162        // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
4163        // need find children closest to edges. Not sure if it is worth the effort.
4164        boolean needsInvalidate = false;
4165        if (mLeftGlow != null && !mLeftGlow.isFinished()) {
4166            final int restore = c.save();
4167            final int padding = mClipToPadding ? getPaddingBottom() : 0;
4168            c.rotate(270);
4169            c.translate(-getHeight() + padding, 0);
4170            needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
4171            c.restoreToCount(restore);
4172        }
4173        if (mTopGlow != null && !mTopGlow.isFinished()) {
4174            final int restore = c.save();
4175            if (mClipToPadding) {
4176                c.translate(getPaddingLeft(), getPaddingTop());
4177            }
4178            needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
4179            c.restoreToCount(restore);
4180        }
4181        if (mRightGlow != null && !mRightGlow.isFinished()) {
4182            final int restore = c.save();
4183            final int width = getWidth();
4184            final int padding = mClipToPadding ? getPaddingTop() : 0;
4185            c.rotate(90);
4186            c.translate(-padding, -width);
4187            needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
4188            c.restoreToCount(restore);
4189        }
4190        if (mBottomGlow != null && !mBottomGlow.isFinished()) {
4191            final int restore = c.save();
4192            c.rotate(180);
4193            if (mClipToPadding) {
4194                c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
4195            } else {
4196                c.translate(-getWidth(), -getHeight());
4197            }
4198            needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
4199            c.restoreToCount(restore);
4200        }
4201
4202        // If some views are animating, ItemDecorators are likely to move/change with them.
4203        // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
4204        // display lists are not invalidated.
4205        if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
4206                && mItemAnimator.isRunning()) {
4207            needsInvalidate = true;
4208        }
4209
4210        if (needsInvalidate) {
4211            ViewCompat.postInvalidateOnAnimation(this);
4212        }
4213    }
4214
4215    @Override
4216    public void onDraw(Canvas c) {
4217        super.onDraw(c);
4218
4219        final int count = mItemDecorations.size();
4220        for (int i = 0; i < count; i++) {
4221            mItemDecorations.get(i).onDraw(c, this, mState);
4222        }
4223    }
4224
4225    @Override
4226    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
4227        return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
4228    }
4229
4230    @Override
4231    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
4232        if (mLayout == null) {
4233            throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
4234        }
4235        return mLayout.generateDefaultLayoutParams();
4236    }
4237
4238    @Override
4239    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
4240        if (mLayout == null) {
4241            throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
4242        }
4243        return mLayout.generateLayoutParams(getContext(), attrs);
4244    }
4245
4246    @Override
4247    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
4248        if (mLayout == null) {
4249            throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
4250        }
4251        return mLayout.generateLayoutParams(p);
4252    }
4253
4254    /**
4255     * Returns true if RecyclerView is currently running some animations.
4256     * <p>
4257     * If you want to be notified when animations are finished, use
4258     * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
4259     *
4260     * @return True if there are some item animations currently running or waiting to be started.
4261     */
4262    public boolean isAnimating() {
4263        return mItemAnimator != null && mItemAnimator.isRunning();
4264    }
4265
4266    void saveOldPositions() {
4267        final int childCount = mChildHelper.getUnfilteredChildCount();
4268        for (int i = 0; i < childCount; i++) {
4269            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4270            if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
4271                throw new IllegalStateException("view holder cannot have position -1 unless it"
4272                        + " is removed" + exceptionLabel());
4273            }
4274            if (!holder.shouldIgnore()) {
4275                holder.saveOldPosition();
4276            }
4277        }
4278    }
4279
4280    void clearOldPositions() {
4281        final int childCount = mChildHelper.getUnfilteredChildCount();
4282        for (int i = 0; i < childCount; i++) {
4283            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4284            if (!holder.shouldIgnore()) {
4285                holder.clearOldPosition();
4286            }
4287        }
4288        mRecycler.clearOldPositions();
4289    }
4290
4291    void offsetPositionRecordsForMove(int from, int to) {
4292        final int childCount = mChildHelper.getUnfilteredChildCount();
4293        final int start, end, inBetweenOffset;
4294        if (from < to) {
4295            start = from;
4296            end = to;
4297            inBetweenOffset = -1;
4298        } else {
4299            start = to;
4300            end = from;
4301            inBetweenOffset = 1;
4302        }
4303
4304        for (int i = 0; i < childCount; i++) {
4305            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4306            if (holder == null || holder.mPosition < start || holder.mPosition > end) {
4307                continue;
4308            }
4309            if (DEBUG) {
4310                Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
4311                        + holder);
4312            }
4313            if (holder.mPosition == from) {
4314                holder.offsetPosition(to - from, false);
4315            } else {
4316                holder.offsetPosition(inBetweenOffset, false);
4317            }
4318
4319            mState.mStructureChanged = true;
4320        }
4321        mRecycler.offsetPositionRecordsForMove(from, to);
4322        requestLayout();
4323    }
4324
4325    void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
4326        final int childCount = mChildHelper.getUnfilteredChildCount();
4327        for (int i = 0; i < childCount; i++) {
4328            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4329            if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
4330                if (DEBUG) {
4331                    Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
4332                            + holder + " now at position " + (holder.mPosition + itemCount));
4333                }
4334                holder.offsetPosition(itemCount, false);
4335                mState.mStructureChanged = true;
4336            }
4337        }
4338        mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
4339        requestLayout();
4340    }
4341
4342    void offsetPositionRecordsForRemove(int positionStart, int itemCount,
4343            boolean applyToPreLayout) {
4344        final int positionEnd = positionStart + itemCount;
4345        final int childCount = mChildHelper.getUnfilteredChildCount();
4346        for (int i = 0; i < childCount; i++) {
4347            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4348            if (holder != null && !holder.shouldIgnore()) {
4349                if (holder.mPosition >= positionEnd) {
4350                    if (DEBUG) {
4351                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4352                                + " holder " + holder + " now at position "
4353                                + (holder.mPosition - itemCount));
4354                    }
4355                    holder.offsetPosition(-itemCount, applyToPreLayout);
4356                    mState.mStructureChanged = true;
4357                } else if (holder.mPosition >= positionStart) {
4358                    if (DEBUG) {
4359                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4360                                + " holder " + holder + " now REMOVED");
4361                    }
4362                    holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
4363                            applyToPreLayout);
4364                    mState.mStructureChanged = true;
4365                }
4366            }
4367        }
4368        mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
4369        requestLayout();
4370    }
4371
4372    /**
4373     * Rebind existing views for the given range, or create as needed.
4374     *
4375     * @param positionStart Adapter position to start at
4376     * @param itemCount Number of views that must explicitly be rebound
4377     */
4378    void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
4379        final int childCount = mChildHelper.getUnfilteredChildCount();
4380        final int positionEnd = positionStart + itemCount;
4381
4382        for (int i = 0; i < childCount; i++) {
4383            final View child = mChildHelper.getUnfilteredChildAt(i);
4384            final ViewHolder holder = getChildViewHolderInt(child);
4385            if (holder == null || holder.shouldIgnore()) {
4386                continue;
4387            }
4388            if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
4389                // We re-bind these view holders after pre-processing is complete so that
4390                // ViewHolders have their final positions assigned.
4391                holder.addFlags(ViewHolder.FLAG_UPDATE);
4392                holder.addChangePayload(payload);
4393                // lp cannot be null since we get ViewHolder from it.
4394                ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
4395            }
4396        }
4397        mRecycler.viewRangeUpdate(positionStart, itemCount);
4398    }
4399
4400    boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
4401        return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
4402                viewHolder.getUnmodifiedPayloads());
4403    }
4404
4405    /**
4406     * Processes the fact that, as far as we can tell, the data set has completely changed.
4407     *
4408     * <ul>
4409     *   <li>Once layout occurs, all attached items should be discarded or animated.
4410     *   <li>Attached items are labeled as invalid.
4411     *   <li>Because items may still be prefetched between a "data set completely changed"
4412     *       event and a layout event, all cached items are discarded.
4413     * </ul>
4414     *
4415     * @param dispatchItemsChanged Whether to call
4416     * {@link LayoutManager#onItemsChanged(RecyclerView)} during measure/layout.
4417     */
4418    void processDataSetCompletelyChanged(boolean dispatchItemsChanged) {
4419        mDispatchItemsChangedEvent |= dispatchItemsChanged;
4420        mDataSetHasChangedAfterLayout = true;
4421        markKnownViewsInvalid();
4422    }
4423
4424    /**
4425     * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
4426     * data change event.
4427     */
4428    void markKnownViewsInvalid() {
4429        final int childCount = mChildHelper.getUnfilteredChildCount();
4430        for (int i = 0; i < childCount; i++) {
4431            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4432            if (holder != null && !holder.shouldIgnore()) {
4433                holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
4434            }
4435        }
4436        markItemDecorInsetsDirty();
4437        mRecycler.markKnownViewsInvalid();
4438    }
4439
4440    /**
4441     * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
4442     * will trigger a {@link #requestLayout()} call.
4443     */
4444    public void invalidateItemDecorations() {
4445        if (mItemDecorations.size() == 0) {
4446            return;
4447        }
4448        if (mLayout != null) {
4449            mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
4450                    + " or layout");
4451        }
4452        markItemDecorInsetsDirty();
4453        requestLayout();
4454    }
4455
4456    /**
4457     * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
4458     * focus even if the View representing the Item is replaced during a layout calculation.
4459     * <p>
4460     * By default, this value is {@code true}.
4461     *
4462     * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
4463     * focus.
4464     *
4465     * @see #setPreserveFocusAfterLayout(boolean)
4466     */
4467    public boolean getPreserveFocusAfterLayout() {
4468        return mPreserveFocusAfterLayout;
4469    }
4470
4471    /**
4472     * Set whether the RecyclerView should try to keep the same Item focused after a layout
4473     * calculation or not.
4474     * <p>
4475     * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
4476     * views may lose focus during a layout calculation as their state changes or they are replaced
4477     * with another view due to type change or animation. In these cases, RecyclerView can request
4478     * focus on the new view automatically.
4479     *
4480     * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
4481     *                                 layout calculations. Defaults to true.
4482     *
4483     * @see #getPreserveFocusAfterLayout()
4484     */
4485    public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
4486        mPreserveFocusAfterLayout = preserveFocusAfterLayout;
4487    }
4488
4489    /**
4490     * Retrieve the {@link ViewHolder} for the given child view.
4491     *
4492     * @param child Child of this RecyclerView to query for its ViewHolder
4493     * @return The child view's ViewHolder
4494     */
4495    public ViewHolder getChildViewHolder(View child) {
4496        final ViewParent parent = child.getParent();
4497        if (parent != null && parent != this) {
4498            throw new IllegalArgumentException("View " + child + " is not a direct child of "
4499                    + this);
4500        }
4501        return getChildViewHolderInt(child);
4502    }
4503
4504    /**
4505     * Traverses the ancestors of the given view and returns the item view that contains it and
4506     * also a direct child of the RecyclerView. This returned view can be used to get the
4507     * ViewHolder by calling {@link #getChildViewHolder(View)}.
4508     *
4509     * @param view The view that is a descendant of the RecyclerView.
4510     *
4511     * @return The direct child of the RecyclerView which contains the given view or null if the
4512     * provided view is not a descendant of this RecyclerView.
4513     *
4514     * @see #getChildViewHolder(View)
4515     * @see #findContainingViewHolder(View)
4516     */
4517    @Nullable
4518    public View findContainingItemView(View view) {
4519        ViewParent parent = view.getParent();
4520        while (parent != null && parent != this && parent instanceof View) {
4521            view = (View) parent;
4522            parent = view.getParent();
4523        }
4524        return parent == this ? view : null;
4525    }
4526
4527    /**
4528     * Returns the ViewHolder that contains the given view.
4529     *
4530     * @param view The view that is a descendant of the RecyclerView.
4531     *
4532     * @return The ViewHolder that contains the given view or null if the provided view is not a
4533     * descendant of this RecyclerView.
4534     */
4535    @Nullable
4536    public ViewHolder findContainingViewHolder(View view) {
4537        View itemView = findContainingItemView(view);
4538        return itemView == null ? null : getChildViewHolder(itemView);
4539    }
4540
4541
4542    static ViewHolder getChildViewHolderInt(View child) {
4543        if (child == null) {
4544            return null;
4545        }
4546        return ((LayoutParams) child.getLayoutParams()).mViewHolder;
4547    }
4548
4549    /**
4550     * @deprecated use {@link #getChildAdapterPosition(View)} or
4551     * {@link #getChildLayoutPosition(View)}.
4552     */
4553    @Deprecated
4554    public int getChildPosition(View child) {
4555        return getChildAdapterPosition(child);
4556    }
4557
4558    /**
4559     * Return the adapter position that the given child view corresponds to.
4560     *
4561     * @param child Child View to query
4562     * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
4563     */
4564    public int getChildAdapterPosition(@NonNull View child) {
4565        final ViewHolder holder = getChildViewHolderInt(child);
4566        return holder != null ? holder.getAdapterPosition() : NO_POSITION;
4567    }
4568
4569    /**
4570     * Return the adapter position of the given child view as of the latest completed layout pass.
4571     * <p>
4572     * This position may not be equal to Item's adapter position if there are pending changes
4573     * in the adapter which have not been reflected to the layout yet.
4574     *
4575     * @param child Child View to query
4576     * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
4577     * the View is representing a removed item.
4578     */
4579    public int getChildLayoutPosition(@NonNull View child) {
4580        final ViewHolder holder = getChildViewHolderInt(child);
4581        return holder != null ? holder.getLayoutPosition() : NO_POSITION;
4582    }
4583
4584    /**
4585     * Return the stable item id that the given child view corresponds to.
4586     *
4587     * @param child Child View to query
4588     * @return Item id corresponding to the given view or {@link #NO_ID}
4589     */
4590    public long getChildItemId(@NonNull View child) {
4591        if (mAdapter == null || !mAdapter.hasStableIds()) {
4592            return NO_ID;
4593        }
4594        final ViewHolder holder = getChildViewHolderInt(child);
4595        return holder != null ? holder.getItemId() : NO_ID;
4596    }
4597
4598    /**
4599     * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
4600     * {@link #findViewHolderForAdapterPosition(int)}
4601     */
4602    @Deprecated
4603    public ViewHolder findViewHolderForPosition(int position) {
4604        return findViewHolderForPosition(position, false);
4605    }
4606
4607    /**
4608     * Return the ViewHolder for the item in the given position of the data set as of the latest
4609     * layout pass.
4610     * <p>
4611     * This method checks only the children of RecyclerView. If the item at the given
4612     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4613     * <p>
4614     * Note that when Adapter contents change, ViewHolder positions are not updated until the
4615     * next layout calculation. If there are pending adapter updates, the return value of this
4616     * method may not match your adapter contents. You can use
4617     * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
4618     * <p>
4619     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4620     * with the same layout position representing the same Item. In this case, the updated
4621     * ViewHolder will be returned.
4622     *
4623     * @param position The position of the item in the data set of the adapter
4624     * @return The ViewHolder at <code>position</code> or null if there is no such item
4625     */
4626    public ViewHolder findViewHolderForLayoutPosition(int position) {
4627        return findViewHolderForPosition(position, false);
4628    }
4629
4630    /**
4631     * Return the ViewHolder for the item in the given position of the data set. Unlike
4632     * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
4633     * adapter changes that may not be reflected to the layout yet. On the other hand, if
4634     * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
4635     * calculated yet, this method will return <code>null</code> since the new positions of views
4636     * are unknown until the layout is calculated.
4637     * <p>
4638     * This method checks only the children of RecyclerView. If the item at the given
4639     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4640     * <p>
4641     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4642     * representing the same Item. In this case, the updated ViewHolder will be returned.
4643     *
4644     * @param position The position of the item in the data set of the adapter
4645     * @return The ViewHolder at <code>position</code> or null if there is no such item
4646     */
4647    public ViewHolder findViewHolderForAdapterPosition(int position) {
4648        if (mDataSetHasChangedAfterLayout) {
4649            return null;
4650        }
4651        final int childCount = mChildHelper.getUnfilteredChildCount();
4652        // hidden VHs are not preferred but if that is the only one we find, we rather return it
4653        ViewHolder hidden = null;
4654        for (int i = 0; i < childCount; i++) {
4655            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4656            if (holder != null && !holder.isRemoved()
4657                    && getAdapterPositionFor(holder) == position) {
4658                if (mChildHelper.isHidden(holder.itemView)) {
4659                    hidden = holder;
4660                } else {
4661                    return holder;
4662                }
4663            }
4664        }
4665        return hidden;
4666    }
4667
4668    ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
4669        final int childCount = mChildHelper.getUnfilteredChildCount();
4670        ViewHolder hidden = null;
4671        for (int i = 0; i < childCount; i++) {
4672            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4673            if (holder != null && !holder.isRemoved()) {
4674                if (checkNewPosition) {
4675                    if (holder.mPosition != position) {
4676                        continue;
4677                    }
4678                } else if (holder.getLayoutPosition() != position) {
4679                    continue;
4680                }
4681                if (mChildHelper.isHidden(holder.itemView)) {
4682                    hidden = holder;
4683                } else {
4684                    return holder;
4685                }
4686            }
4687        }
4688        // This method should not query cached views. It creates a problem during adapter updates
4689        // when we are dealing with already laid out views. Also, for the public method, it is more
4690        // reasonable to return null if position is not laid out.
4691        return hidden;
4692    }
4693
4694    /**
4695     * Return the ViewHolder for the item with the given id. The RecyclerView must
4696     * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
4697     * return a non-null value.
4698     * <p>
4699     * This method checks only the children of RecyclerView. If the item with the given
4700     * <code>id</code> is not laid out, it <em>will not</em> create a new one.
4701     *
4702     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
4703     * same id. In this case, the updated ViewHolder will be returned.
4704     *
4705     * @param id The id for the requested item
4706     * @return The ViewHolder with the given <code>id</code> or null if there is no such item
4707     */
4708    public ViewHolder findViewHolderForItemId(long id) {
4709        if (mAdapter == null || !mAdapter.hasStableIds()) {
4710            return null;
4711        }
4712        final int childCount = mChildHelper.getUnfilteredChildCount();
4713        ViewHolder hidden = null;
4714        for (int i = 0; i < childCount; i++) {
4715            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4716            if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
4717                if (mChildHelper.isHidden(holder.itemView)) {
4718                    hidden = holder;
4719                } else {
4720                    return holder;
4721                }
4722            }
4723        }
4724        return hidden;
4725    }
4726
4727    /**
4728     * Find the topmost view under the given point.
4729     *
4730     * @param x Horizontal position in pixels to search
4731     * @param y Vertical position in pixels to search
4732     * @return The child view under (x, y) or null if no matching child is found
4733     */
4734    @Nullable
4735    public View findChildViewUnder(float x, float y) {
4736        final int count = mChildHelper.getChildCount();
4737        for (int i = count - 1; i >= 0; i--) {
4738            final View child = mChildHelper.getChildAt(i);
4739            final float translationX = child.getTranslationX();
4740            final float translationY = child.getTranslationY();
4741            if (x >= child.getLeft() + translationX
4742                    && x <= child.getRight() + translationX
4743                    && y >= child.getTop() + translationY
4744                    && y <= child.getBottom() + translationY) {
4745                return child;
4746            }
4747        }
4748        return null;
4749    }
4750
4751    @Override
4752    public boolean drawChild(Canvas canvas, View child, long drawingTime) {
4753        return super.drawChild(canvas, child, drawingTime);
4754    }
4755
4756    /**
4757     * Offset the bounds of all child views by <code>dy</code> pixels.
4758     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4759     *
4760     * @param dy Vertical pixel offset to apply to the bounds of all child views
4761     */
4762    public void offsetChildrenVertical(@Px int dy) {
4763        final int childCount = mChildHelper.getChildCount();
4764        for (int i = 0; i < childCount; i++) {
4765            mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
4766        }
4767    }
4768
4769    /**
4770     * Called when an item view is attached to this RecyclerView.
4771     *
4772     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4773     * of child views as they become attached. This will be called before a
4774     * {@link LayoutManager} measures or lays out the view and is a good time to perform these
4775     * changes.</p>
4776     *
4777     * @param child Child view that is now attached to this RecyclerView and its associated window
4778     */
4779    public void onChildAttachedToWindow(@NonNull View child) {
4780    }
4781
4782    /**
4783     * Called when an item view is detached from this RecyclerView.
4784     *
4785     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4786     * of child views as they become detached. This will be called as a
4787     * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
4788     *
4789     * @param child Child view that is now detached from this RecyclerView and its associated window
4790     */
4791    public void onChildDetachedFromWindow(@NonNull View child) {
4792    }
4793
4794    /**
4795     * Offset the bounds of all child views by <code>dx</code> pixels.
4796     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4797     *
4798     * @param dx Horizontal pixel offset to apply to the bounds of all child views
4799     */
4800    public void offsetChildrenHorizontal(@Px int dx) {
4801        final int childCount = mChildHelper.getChildCount();
4802        for (int i = 0; i < childCount; i++) {
4803            mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
4804        }
4805    }
4806
4807    /**
4808     * Returns the bounds of the view including its decoration and margins.
4809     *
4810     * @param view The view element to check
4811     * @param outBounds A rect that will receive the bounds of the element including its
4812     *                  decoration and margins.
4813     */
4814    public void getDecoratedBoundsWithMargins(@NonNull View view, @NonNull Rect outBounds) {
4815        getDecoratedBoundsWithMarginsInt(view, outBounds);
4816    }
4817
4818    static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
4819        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
4820        final Rect insets = lp.mDecorInsets;
4821        outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
4822                view.getTop() - insets.top - lp.topMargin,
4823                view.getRight() + insets.right + lp.rightMargin,
4824                view.getBottom() + insets.bottom + lp.bottomMargin);
4825    }
4826
4827    Rect getItemDecorInsetsForChild(View child) {
4828        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
4829        if (!lp.mInsetsDirty) {
4830            return lp.mDecorInsets;
4831        }
4832
4833        if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
4834            // changed/invalid items should not be updated until they are rebound.
4835            return lp.mDecorInsets;
4836        }
4837        final Rect insets = lp.mDecorInsets;
4838        insets.set(0, 0, 0, 0);
4839        final int decorCount = mItemDecorations.size();
4840        for (int i = 0; i < decorCount; i++) {
4841            mTempRect.set(0, 0, 0, 0);
4842            mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
4843            insets.left += mTempRect.left;
4844            insets.top += mTempRect.top;
4845            insets.right += mTempRect.right;
4846            insets.bottom += mTempRect.bottom;
4847        }
4848        lp.mInsetsDirty = false;
4849        return insets;
4850    }
4851
4852    /**
4853     * Called when the scroll position of this RecyclerView changes. Subclasses should use
4854     * this method to respond to scrolling within the adapter's data set instead of an explicit
4855     * listener.
4856     *
4857     * <p>This method will always be invoked before listeners. If a subclass needs to perform
4858     * any additional upkeep or bookkeeping after scrolling but before listeners run,
4859     * this is a good place to do so.</p>
4860     *
4861     * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
4862     * the distance scrolled in either direction within the adapter's data set instead of absolute
4863     * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
4864     * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
4865     * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
4866     * do not correspond to the data set scroll position. However, some subclasses may choose
4867     * to use these fields as special offsets.</p>
4868     *
4869     * @param dx horizontal distance scrolled in pixels
4870     * @param dy vertical distance scrolled in pixels
4871     */
4872    public void onScrolled(@Px int dx, @Px int dy) {
4873        // Do nothing
4874    }
4875
4876    void dispatchOnScrolled(int hresult, int vresult) {
4877        mDispatchScrollCounter++;
4878        // Pass the current scrollX/scrollY values; no actual change in these properties occurred
4879        // but some general-purpose code may choose to respond to changes this way.
4880        final int scrollX = getScrollX();
4881        final int scrollY = getScrollY();
4882        onScrollChanged(scrollX, scrollY, scrollX, scrollY);
4883
4884        // Pass the real deltas to onScrolled, the RecyclerView-specific method.
4885        onScrolled(hresult, vresult);
4886
4887        // Invoke listeners last. Subclassed view methods always handle the event first.
4888        // All internal state is consistent by the time listeners are invoked.
4889        if (mScrollListener != null) {
4890            mScrollListener.onScrolled(this, hresult, vresult);
4891        }
4892        if (mScrollListeners != null) {
4893            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4894                mScrollListeners.get(i).onScrolled(this, hresult, vresult);
4895            }
4896        }
4897        mDispatchScrollCounter--;
4898    }
4899
4900    /**
4901     * Called when the scroll state of this RecyclerView changes. Subclasses should use this
4902     * method to respond to state changes instead of an explicit listener.
4903     *
4904     * <p>This method will always be invoked before listeners, but after the LayoutManager
4905     * responds to the scroll state change.</p>
4906     *
4907     * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
4908     *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
4909     */
4910    public void onScrollStateChanged(int state) {
4911        // Do nothing
4912    }
4913
4914    void dispatchOnScrollStateChanged(int state) {
4915        // Let the LayoutManager go first; this allows it to bring any properties into
4916        // a consistent state before the RecyclerView subclass responds.
4917        if (mLayout != null) {
4918            mLayout.onScrollStateChanged(state);
4919        }
4920
4921        // Let the RecyclerView subclass handle this event next; any LayoutManager property
4922        // changes will be reflected by this time.
4923        onScrollStateChanged(state);
4924
4925        // Listeners go last. All other internal state is consistent by this point.
4926        if (mScrollListener != null) {
4927            mScrollListener.onScrollStateChanged(this, state);
4928        }
4929        if (mScrollListeners != null) {
4930            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4931                mScrollListeners.get(i).onScrollStateChanged(this, state);
4932            }
4933        }
4934    }
4935
4936    /**
4937     * Returns whether there are pending adapter updates which are not yet applied to the layout.
4938     * <p>
4939     * If this method returns <code>true</code>, it means that what user is currently seeing may not
4940     * reflect them adapter contents (depending on what has changed).
4941     * You may use this information to defer or cancel some operations.
4942     * <p>
4943     * This method returns true if RecyclerView has not yet calculated the first layout after it is
4944     * attached to the Window or the Adapter has been replaced.
4945     *
4946     * @return True if there are some adapter updates which are not yet reflected to layout or false
4947     * if layout is up to date.
4948     */
4949    public boolean hasPendingAdapterUpdates() {
4950        return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
4951                || mAdapterHelper.hasPendingUpdates();
4952    }
4953
4954    class ViewFlinger implements Runnable {
4955        private int mLastFlingX;
4956        private int mLastFlingY;
4957        private OverScroller mScroller;
4958        Interpolator mInterpolator = sQuinticInterpolator;
4959
4960        // When set to true, postOnAnimation callbacks are delayed until the run method completes
4961        private boolean mEatRunOnAnimationRequest = false;
4962
4963        // Tracks if postAnimationCallback should be re-attached when it is done
4964        private boolean mReSchedulePostAnimationCallback = false;
4965
4966        ViewFlinger() {
4967            mScroller = new OverScroller(getContext(), sQuinticInterpolator);
4968        }
4969
4970        @Override
4971        public void run() {
4972            if (mLayout == null) {
4973                stop();
4974                return; // no layout, cannot scroll.
4975            }
4976            disableRunOnAnimationRequests();
4977            consumePendingUpdateOperations();
4978            // keep a local reference so that if it is changed during onAnimation method, it won't
4979            // cause unexpected behaviors
4980            final OverScroller scroller = mScroller;
4981            final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
4982            if (scroller.computeScrollOffset()) {
4983                final int[] scrollConsumed = mScrollConsumed;
4984                final int x = scroller.getCurrX();
4985                final int y = scroller.getCurrY();
4986                int dx = x - mLastFlingX;
4987                int dy = y - mLastFlingY;
4988                int hresult = 0;
4989                int vresult = 0;
4990                mLastFlingX = x;
4991                mLastFlingY = y;
4992                int overscrollX = 0, overscrollY = 0;
4993
4994                if (dispatchNestedPreScroll(dx, dy, scrollConsumed, null, TYPE_NON_TOUCH)) {
4995                    dx -= scrollConsumed[0];
4996                    dy -= scrollConsumed[1];
4997                }
4998
4999                if (mAdapter != null) {
5000                    scrollStep(dx, dy, mScrollStepConsumed);
5001                    hresult = mScrollStepConsumed[0];
5002                    vresult = mScrollStepConsumed[1];
5003                    overscrollX = dx - hresult;
5004                    overscrollY = dy - vresult;
5005
5006                    if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
5007                            && smoothScroller.isRunning()) {
5008                        final int adapterSize = mState.getItemCount();
5009                        if (adapterSize == 0) {
5010                            smoothScroller.stop();
5011                        } else if (smoothScroller.getTargetPosition() >= adapterSize) {
5012                            smoothScroller.setTargetPosition(adapterSize - 1);
5013                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
5014                        } else {
5015                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
5016                        }
5017                    }
5018                }
5019                if (!mItemDecorations.isEmpty()) {
5020                    invalidate();
5021                }
5022                if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
5023                    considerReleasingGlowsOnScroll(dx, dy);
5024                }
5025
5026                if (!dispatchNestedScroll(hresult, vresult, overscrollX, overscrollY, null,
5027                        TYPE_NON_TOUCH)
5028                        && (overscrollX != 0 || overscrollY != 0)) {
5029                    final int vel = (int) scroller.getCurrVelocity();
5030
5031                    int velX = 0;
5032                    if (overscrollX != x) {
5033                        velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
5034                    }
5035
5036                    int velY = 0;
5037                    if (overscrollY != y) {
5038                        velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
5039                    }
5040
5041                    if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
5042                        absorbGlows(velX, velY);
5043                    }
5044                    if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
5045                            && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
5046                        scroller.abortAnimation();
5047                    }
5048                }
5049                if (hresult != 0 || vresult != 0) {
5050                    dispatchOnScrolled(hresult, vresult);
5051                }
5052
5053                if (!awakenScrollBars()) {
5054                    invalidate();
5055                }
5056
5057                final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
5058                        && vresult == dy;
5059                final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
5060                        && hresult == dx;
5061                final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
5062                        || fullyConsumedVertical;
5063
5064                if (scroller.isFinished() || (!fullyConsumedAny
5065                        && !hasNestedScrollingParent(TYPE_NON_TOUCH))) {
5066                    // setting state to idle will stop this.
5067                    setScrollState(SCROLL_STATE_IDLE);
5068                    if (ALLOW_THREAD_GAP_WORK) {
5069                        mPrefetchRegistry.clearPrefetchPositions();
5070                    }
5071                    stopNestedScroll(TYPE_NON_TOUCH);
5072                } else {
5073                    postOnAnimation();
5074                    if (mGapWorker != null) {
5075                        mGapWorker.postFromTraversal(RecyclerView.this, dx, dy);
5076                    }
5077                }
5078            }
5079            // call this after the onAnimation is complete not to have inconsistent callbacks etc.
5080            if (smoothScroller != null) {
5081                if (smoothScroller.isPendingInitialRun()) {
5082                    smoothScroller.onAnimation(0, 0);
5083                }
5084                if (!mReSchedulePostAnimationCallback) {
5085                    smoothScroller.stop(); //stop if it does not trigger any scroll
5086                }
5087            }
5088            enableRunOnAnimationRequests();
5089        }
5090
5091        private void disableRunOnAnimationRequests() {
5092            mReSchedulePostAnimationCallback = false;
5093            mEatRunOnAnimationRequest = true;
5094        }
5095
5096        private void enableRunOnAnimationRequests() {
5097            mEatRunOnAnimationRequest = false;
5098            if (mReSchedulePostAnimationCallback) {
5099                postOnAnimation();
5100            }
5101        }
5102
5103        void postOnAnimation() {
5104            if (mEatRunOnAnimationRequest) {
5105                mReSchedulePostAnimationCallback = true;
5106            } else {
5107                removeCallbacks(this);
5108                ViewCompat.postOnAnimation(RecyclerView.this, this);
5109            }
5110        }
5111
5112        public void fling(int velocityX, int velocityY) {
5113            setScrollState(SCROLL_STATE_SETTLING);
5114            mLastFlingX = mLastFlingY = 0;
5115            mScroller.fling(0, 0, velocityX, velocityY,
5116                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
5117            postOnAnimation();
5118        }
5119
5120        public void smoothScrollBy(int dx, int dy) {
5121            smoothScrollBy(dx, dy, 0, 0);
5122        }
5123
5124        public void smoothScrollBy(int dx, int dy, int vx, int vy) {
5125            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
5126        }
5127
5128        private float distanceInfluenceForSnapDuration(float f) {
5129            f -= 0.5f; // center the values about 0.
5130            f *= 0.3f * (float) Math.PI / 2.0f;
5131            return (float) Math.sin(f);
5132        }
5133
5134        private int computeScrollDuration(int dx, int dy, int vx, int vy) {
5135            final int absDx = Math.abs(dx);
5136            final int absDy = Math.abs(dy);
5137            final boolean horizontal = absDx > absDy;
5138            final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
5139            final int delta = (int) Math.sqrt(dx * dx + dy * dy);
5140            final int containerSize = horizontal ? getWidth() : getHeight();
5141            final int halfContainerSize = containerSize / 2;
5142            final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
5143            final float distance = halfContainerSize + halfContainerSize
5144                    * distanceInfluenceForSnapDuration(distanceRatio);
5145
5146            final int duration;
5147            if (velocity > 0) {
5148                duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
5149            } else {
5150                float absDelta = (float) (horizontal ? absDx : absDy);
5151                duration = (int) (((absDelta / containerSize) + 1) * 300);
5152            }
5153            return Math.min(duration, MAX_SCROLL_DURATION);
5154        }
5155
5156        public void smoothScrollBy(int dx, int dy, int duration) {
5157            smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
5158        }
5159
5160        public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
5161            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, 0, 0),
5162                    interpolator == null ? sQuinticInterpolator : interpolator);
5163        }
5164
5165        public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
5166            if (mInterpolator != interpolator) {
5167                mInterpolator = interpolator;
5168                mScroller = new OverScroller(getContext(), interpolator);
5169            }
5170            setScrollState(SCROLL_STATE_SETTLING);
5171            mLastFlingX = mLastFlingY = 0;
5172            mScroller.startScroll(0, 0, dx, dy, duration);
5173            if (Build.VERSION.SDK_INT < 23) {
5174                // b/64931938 before API 23, startScroll() does not reset getCurX()/getCurY()
5175                // to start values, which causes fillRemainingScrollValues() put in obsolete values
5176                // for LayoutManager.onLayoutChildren().
5177                mScroller.computeScrollOffset();
5178            }
5179            postOnAnimation();
5180        }
5181
5182        public void stop() {
5183            removeCallbacks(this);
5184            mScroller.abortAnimation();
5185        }
5186
5187    }
5188
5189    void repositionShadowingViews() {
5190        // Fix up shadow views used by change animations
5191        int count = mChildHelper.getChildCount();
5192        for (int i = 0; i < count; i++) {
5193            View view = mChildHelper.getChildAt(i);
5194            ViewHolder holder = getChildViewHolder(view);
5195            if (holder != null && holder.mShadowingHolder != null) {
5196                View shadowingView = holder.mShadowingHolder.itemView;
5197                int left = view.getLeft();
5198                int top = view.getTop();
5199                if (left != shadowingView.getLeft() ||  top != shadowingView.getTop()) {
5200                    shadowingView.layout(left, top,
5201                            left + shadowingView.getWidth(),
5202                            top + shadowingView.getHeight());
5203                }
5204            }
5205        }
5206    }
5207
5208    private class RecyclerViewDataObserver extends AdapterDataObserver {
5209        RecyclerViewDataObserver() {
5210        }
5211
5212        @Override
5213        public void onChanged() {
5214            assertNotInLayoutOrScroll(null);
5215            mState.mStructureChanged = true;
5216
5217            processDataSetCompletelyChanged(true);
5218            if (!mAdapterHelper.hasPendingUpdates()) {
5219                requestLayout();
5220            }
5221        }
5222
5223        @Override
5224        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
5225            assertNotInLayoutOrScroll(null);
5226            if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
5227                triggerUpdateProcessor();
5228            }
5229        }
5230
5231        @Override
5232        public void onItemRangeInserted(int positionStart, int itemCount) {
5233            assertNotInLayoutOrScroll(null);
5234            if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
5235                triggerUpdateProcessor();
5236            }
5237        }
5238
5239        @Override
5240        public void onItemRangeRemoved(int positionStart, int itemCount) {
5241            assertNotInLayoutOrScroll(null);
5242            if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
5243                triggerUpdateProcessor();
5244            }
5245        }
5246
5247        @Override
5248        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
5249            assertNotInLayoutOrScroll(null);
5250            if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
5251                triggerUpdateProcessor();
5252            }
5253        }
5254
5255        void triggerUpdateProcessor() {
5256            if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
5257                ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
5258            } else {
5259                mAdapterUpdateDuringMeasure = true;
5260                requestLayout();
5261            }
5262        }
5263    }
5264
5265    /**
5266     * EdgeEffectFactory lets you customize the over-scroll edge effect for RecyclerViews.
5267     *
5268     * @see RecyclerView#setEdgeEffectFactory(EdgeEffectFactory)
5269     */
5270    public static class EdgeEffectFactory {
5271
5272        @Retention(RetentionPolicy.SOURCE)
5273        @IntDef({DIRECTION_LEFT, DIRECTION_TOP, DIRECTION_RIGHT, DIRECTION_BOTTOM})
5274        public @interface EdgeDirection {}
5275
5276        /**
5277         * Direction constant for the left edge
5278         */
5279        public static final int DIRECTION_LEFT = 0;
5280
5281        /**
5282         * Direction constant for the top edge
5283         */
5284        public static final int DIRECTION_TOP = 1;
5285
5286        /**
5287         * Direction constant for the right edge
5288         */
5289        public static final int DIRECTION_RIGHT = 2;
5290
5291        /**
5292         * Direction constant for the bottom edge
5293         */
5294        public static final int DIRECTION_BOTTOM = 3;
5295
5296        /**
5297         * Create a new EdgeEffect for the provided direction.
5298         */
5299        protected @NonNull EdgeEffect createEdgeEffect(@NonNull RecyclerView view,
5300                @EdgeDirection int direction) {
5301            return new EdgeEffect(view.getContext());
5302        }
5303    }
5304
5305    /**
5306     * RecycledViewPool lets you share Views between multiple RecyclerViews.
5307     * <p>
5308     * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
5309     * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
5310     * <p>
5311     * RecyclerView automatically creates a pool for itself if you don't provide one.
5312     */
5313    public static class RecycledViewPool {
5314        private static final int DEFAULT_MAX_SCRAP = 5;
5315
5316        /**
5317         * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
5318         *
5319         * Note that this tracks running averages of create/bind time across all RecyclerViews
5320         * (and, indirectly, Adapters) that use this pool.
5321         *
5322         * 1) This enables us to track average create and bind times across multiple adapters. Even
5323         * though create (and especially bind) may behave differently for different Adapter
5324         * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
5325         *
5326         * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
5327         * false for all other views of its type for the same deadline. This prevents items
5328         * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
5329         */
5330        static class ScrapData {
5331            final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
5332            int mMaxScrap = DEFAULT_MAX_SCRAP;
5333            long mCreateRunningAverageNs = 0;
5334            long mBindRunningAverageNs = 0;
5335        }
5336        SparseArray<ScrapData> mScrap = new SparseArray<>();
5337
5338        private int mAttachCount = 0;
5339
5340        /**
5341         * Discard all ViewHolders.
5342         */
5343        public void clear() {
5344            for (int i = 0; i < mScrap.size(); i++) {
5345                ScrapData data = mScrap.valueAt(i);
5346                data.mScrapHeap.clear();
5347            }
5348        }
5349
5350        /**
5351         * Sets the maximum number of ViewHolders to hold in the pool before discarding.
5352         *
5353         * @param viewType ViewHolder Type
5354         * @param max Maximum number
5355         */
5356        public void setMaxRecycledViews(int viewType, int max) {
5357            ScrapData scrapData = getScrapDataForType(viewType);
5358            scrapData.mMaxScrap = max;
5359            final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
5360            while (scrapHeap.size() > max) {
5361                scrapHeap.remove(scrapHeap.size() - 1);
5362            }
5363        }
5364
5365        /**
5366         * Returns the current number of Views held by the RecycledViewPool of the given view type.
5367         */
5368        public int getRecycledViewCount(int viewType) {
5369            return getScrapDataForType(viewType).mScrapHeap.size();
5370        }
5371
5372        /**
5373         * Acquire a ViewHolder of the specified type from the pool, or {@code null} if none are
5374         * present.
5375         *
5376         * @param viewType ViewHolder type.
5377         * @return ViewHolder of the specified type acquired from the pool, or {@code null} if none
5378         * are present.
5379         */
5380        @Nullable
5381        public ViewHolder getRecycledView(int viewType) {
5382            final ScrapData scrapData = mScrap.get(viewType);
5383            if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
5384                final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
5385                return scrapHeap.remove(scrapHeap.size() - 1);
5386            }
5387            return null;
5388        }
5389
5390        /**
5391         * Total number of ViewHolders held by the pool.
5392         *
5393         * @return Number of ViewHolders held by the pool.
5394         */
5395        int size() {
5396            int count = 0;
5397            for (int i = 0; i < mScrap.size(); i++) {
5398                ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
5399                if (viewHolders != null) {
5400                    count += viewHolders.size();
5401                }
5402            }
5403            return count;
5404        }
5405
5406        /**
5407         * Add a scrap ViewHolder to the pool.
5408         * <p>
5409         * If the pool is already full for that ViewHolder's type, it will be immediately discarded.
5410         *
5411         * @param scrap ViewHolder to be added to the pool.
5412         */
5413        public void putRecycledView(ViewHolder scrap) {
5414            final int viewType = scrap.getItemViewType();
5415            final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
5416            if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
5417                return;
5418            }
5419            if (DEBUG && scrapHeap.contains(scrap)) {
5420                throw new IllegalArgumentException("this scrap item already exists");
5421            }
5422            scrap.resetInternal();
5423            scrapHeap.add(scrap);
5424        }
5425
5426        long runningAverage(long oldAverage, long newValue) {
5427            if (oldAverage == 0) {
5428                return newValue;
5429            }
5430            return (oldAverage / 4 * 3) + (newValue / 4);
5431        }
5432
5433        void factorInCreateTime(int viewType, long createTimeNs) {
5434            ScrapData scrapData = getScrapDataForType(viewType);
5435            scrapData.mCreateRunningAverageNs = runningAverage(
5436                    scrapData.mCreateRunningAverageNs, createTimeNs);
5437        }
5438
5439        void factorInBindTime(int viewType, long bindTimeNs) {
5440            ScrapData scrapData = getScrapDataForType(viewType);
5441            scrapData.mBindRunningAverageNs = runningAverage(
5442                    scrapData.mBindRunningAverageNs, bindTimeNs);
5443        }
5444
5445        boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5446            long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
5447            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5448        }
5449
5450        boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5451            long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
5452            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5453        }
5454
5455        void attach(Adapter adapter) {
5456            mAttachCount++;
5457        }
5458
5459        void detach() {
5460            mAttachCount--;
5461        }
5462
5463
5464        /**
5465         * Detaches the old adapter and attaches the new one.
5466         * <p>
5467         * RecycledViewPool will clear its cache if it has only one adapter attached and the new
5468         * adapter uses a different ViewHolder than the oldAdapter.
5469         *
5470         * @param oldAdapter The previous adapter instance. Will be detached.
5471         * @param newAdapter The new adapter instance. Will be attached.
5472         * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
5473         *                               ViewHolder and view types.
5474         */
5475        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
5476                boolean compatibleWithPrevious) {
5477            if (oldAdapter != null) {
5478                detach();
5479            }
5480            if (!compatibleWithPrevious && mAttachCount == 0) {
5481                clear();
5482            }
5483            if (newAdapter != null) {
5484                attach(newAdapter);
5485            }
5486        }
5487
5488        private ScrapData getScrapDataForType(int viewType) {
5489            ScrapData scrapData = mScrap.get(viewType);
5490            if (scrapData == null) {
5491                scrapData = new ScrapData();
5492                mScrap.put(viewType, scrapData);
5493            }
5494            return scrapData;
5495        }
5496    }
5497
5498    /**
5499     * Utility method for finding an internal RecyclerView, if present
5500     */
5501    @Nullable
5502    static RecyclerView findNestedRecyclerView(@NonNull View view) {
5503        if (!(view instanceof ViewGroup)) {
5504            return null;
5505        }
5506        if (view instanceof RecyclerView) {
5507            return (RecyclerView) view;
5508        }
5509        final ViewGroup parent = (ViewGroup) view;
5510        final int count = parent.getChildCount();
5511        for (int i = 0; i < count; i++) {
5512            final View child = parent.getChildAt(i);
5513            final RecyclerView descendant = findNestedRecyclerView(child);
5514            if (descendant != null) {
5515                return descendant;
5516            }
5517        }
5518        return null;
5519    }
5520
5521    /**
5522     * Utility method for clearing holder's internal RecyclerView, if present
5523     */
5524    static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
5525        if (holder.mNestedRecyclerView != null) {
5526            View item = holder.mNestedRecyclerView.get();
5527            while (item != null) {
5528                if (item == holder.itemView) {
5529                    return; // match found, don't need to clear
5530                }
5531
5532                ViewParent parent = item.getParent();
5533                if (parent instanceof View) {
5534                    item = (View) parent;
5535                } else {
5536                    item = null;
5537                }
5538            }
5539            holder.mNestedRecyclerView = null; // not nested
5540        }
5541    }
5542
5543    /**
5544     * Time base for deadline-aware work scheduling. Overridable for testing.
5545     *
5546     * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
5547     * isn't relevant.
5548     */
5549    long getNanoTime() {
5550        if (ALLOW_THREAD_GAP_WORK) {
5551            return System.nanoTime();
5552        } else {
5553            return 0;
5554        }
5555    }
5556
5557    /**
5558     * A Recycler is responsible for managing scrapped or detached item views for reuse.
5559     *
5560     * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
5561     * that has been marked for removal or reuse.</p>
5562     *
5563     * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
5564     * an adapter's data set representing the data at a given position or item ID.
5565     * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
5566     * If not, the view can be quickly reused by the LayoutManager with no further work.
5567     * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
5568     * may be repositioned by a LayoutManager without remeasurement.</p>
5569     */
5570    public final class Recycler {
5571        final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
5572        ArrayList<ViewHolder> mChangedScrap = null;
5573
5574        final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
5575
5576        private final List<ViewHolder>
5577                mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
5578
5579        private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
5580        int mViewCacheMax = DEFAULT_CACHE_SIZE;
5581
5582        RecycledViewPool mRecyclerPool;
5583
5584        private ViewCacheExtension mViewCacheExtension;
5585
5586        static final int DEFAULT_CACHE_SIZE = 2;
5587
5588        /**
5589         * Clear scrap views out of this recycler. Detached views contained within a
5590         * recycled view pool will remain.
5591         */
5592        public void clear() {
5593            mAttachedScrap.clear();
5594            recycleAndClearCachedViews();
5595        }
5596
5597        /**
5598         * Set the maximum number of detached, valid views we should retain for later use.
5599         *
5600         * @param viewCount Number of views to keep before sending views to the shared pool
5601         */
5602        public void setViewCacheSize(int viewCount) {
5603            mRequestedCacheMax = viewCount;
5604            updateViewCacheSize();
5605        }
5606
5607        void updateViewCacheSize() {
5608            int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
5609            mViewCacheMax = mRequestedCacheMax + extraCache;
5610
5611            // first, try the views that can be recycled
5612            for (int i = mCachedViews.size() - 1;
5613                    i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
5614                recycleCachedViewAt(i);
5615            }
5616        }
5617
5618        /**
5619         * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
5620         *
5621         * @return List of ViewHolders in the scrap list.
5622         */
5623        @NonNull
5624        public List<ViewHolder> getScrapList() {
5625            return mUnmodifiableAttachedScrap;
5626        }
5627
5628        /**
5629         * Helper method for getViewForPosition.
5630         * <p>
5631         * Checks whether a given view holder can be used for the provided position.
5632         *
5633         * @param holder ViewHolder
5634         * @return true if ViewHolder matches the provided position, false otherwise
5635         */
5636        boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
5637            // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
5638            // if it is not removed, verify the type and id.
5639            if (holder.isRemoved()) {
5640                if (DEBUG && !mState.isPreLayout()) {
5641                    throw new IllegalStateException("should not receive a removed view unless it"
5642                            + " is pre layout" + exceptionLabel());
5643                }
5644                return mState.isPreLayout();
5645            }
5646            if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
5647                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
5648                        + "adapter position" + holder + exceptionLabel());
5649            }
5650            if (!mState.isPreLayout()) {
5651                // don't check type if it is pre-layout.
5652                final int type = mAdapter.getItemViewType(holder.mPosition);
5653                if (type != holder.getItemViewType()) {
5654                    return false;
5655                }
5656            }
5657            if (mAdapter.hasStableIds()) {
5658                return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
5659            }
5660            return true;
5661        }
5662
5663        /**
5664         * Attempts to bind view, and account for relevant timing information. If
5665         * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
5666         *
5667         * @param holder Holder to be bound.
5668         * @param offsetPosition Position of item to be bound.
5669         * @param position Pre-layout position of item to be bound.
5670         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5671         *                   complete. If FOREVER_NS is passed, this method will not fail to
5672         *                   bind the holder.
5673         * @return
5674         */
5675        private boolean tryBindViewHolderByDeadline(@NonNull ViewHolder holder, int offsetPosition,
5676                int position, long deadlineNs) {
5677            holder.mOwnerRecyclerView = RecyclerView.this;
5678            final int viewType = holder.getItemViewType();
5679            long startBindNs = getNanoTime();
5680            if (deadlineNs != FOREVER_NS
5681                    && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
5682                // abort - we have a deadline we can't meet
5683                return false;
5684            }
5685            mAdapter.bindViewHolder(holder, offsetPosition);
5686            long endBindNs = getNanoTime();
5687            mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
5688            attachAccessibilityDelegateOnBind(holder);
5689            if (mState.isPreLayout()) {
5690                holder.mPreLayoutPosition = position;
5691            }
5692            return true;
5693        }
5694
5695        /**
5696         * Binds the given View to the position. The View can be a View previously retrieved via
5697         * {@link #getViewForPosition(int)} or created by
5698         * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
5699         * <p>
5700         * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
5701         * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
5702         * wants to handle its own recycling logic.
5703         * <p>
5704         * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
5705         * you don't need to call this method unless you want to bind this View to another position.
5706         *
5707         * @param view The view to update.
5708         * @param position The position of the item to bind to this View.
5709         */
5710        public void bindViewToPosition(@NonNull View view, int position) {
5711            ViewHolder holder = getChildViewHolderInt(view);
5712            if (holder == null) {
5713                throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
5714                        + " pass arbitrary views to this method, they should be created by the "
5715                        + "Adapter" + exceptionLabel());
5716            }
5717            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5718            if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5719                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5720                        + "position " + position + "(offset:" + offsetPosition + ")."
5721                        + "state:" + mState.getItemCount() + exceptionLabel());
5722            }
5723            tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
5724
5725            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5726            final LayoutParams rvLayoutParams;
5727            if (lp == null) {
5728                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5729                holder.itemView.setLayoutParams(rvLayoutParams);
5730            } else if (!checkLayoutParams(lp)) {
5731                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5732                holder.itemView.setLayoutParams(rvLayoutParams);
5733            } else {
5734                rvLayoutParams = (LayoutParams) lp;
5735            }
5736
5737            rvLayoutParams.mInsetsDirty = true;
5738            rvLayoutParams.mViewHolder = holder;
5739            rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
5740        }
5741
5742        /**
5743         * RecyclerView provides artificial position range (item count) in pre-layout state and
5744         * automatically maps these positions to {@link Adapter} positions when
5745         * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
5746         * <p>
5747         * Usually, LayoutManager does not need to worry about this. However, in some cases, your
5748         * LayoutManager may need to call some custom component with item positions in which
5749         * case you need the actual adapter position instead of the pre layout position. You
5750         * can use this method to convert a pre-layout position to adapter (post layout) position.
5751         * <p>
5752         * Note that if the provided position belongs to a deleted ViewHolder, this method will
5753         * return -1.
5754         * <p>
5755         * Calling this method in post-layout state returns the same value back.
5756         *
5757         * @param position The pre-layout position to convert. Must be greater or equal to 0 and
5758         *                 less than {@link State#getItemCount()}.
5759         */
5760        public int convertPreLayoutPositionToPostLayout(int position) {
5761            if (position < 0 || position >= mState.getItemCount()) {
5762                throw new IndexOutOfBoundsException("invalid position " + position + ". State "
5763                        + "item count is " + mState.getItemCount() + exceptionLabel());
5764            }
5765            if (!mState.isPreLayout()) {
5766                return position;
5767            }
5768            return mAdapterHelper.findPositionOffset(position);
5769        }
5770
5771        /**
5772         * Obtain a view initialized for the given position.
5773         *
5774         * This method should be used by {@link LayoutManager} implementations to obtain
5775         * views to represent data from an {@link Adapter}.
5776         * <p>
5777         * The Recycler may reuse a scrap or detached view from a shared pool if one is
5778         * available for the correct view type. If the adapter has not indicated that the
5779         * data at the given position has changed, the Recycler will attempt to hand back
5780         * a scrap view that was previously initialized for that data without rebinding.
5781         *
5782         * @param position Position to obtain a view for
5783         * @return A view representing the data at <code>position</code> from <code>adapter</code>
5784         */
5785        @NonNull
5786        public View getViewForPosition(int position) {
5787            return getViewForPosition(position, false);
5788        }
5789
5790        View getViewForPosition(int position, boolean dryRun) {
5791            return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
5792        }
5793
5794        /**
5795         * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
5796         * cache, the RecycledViewPool, or creating it directly.
5797         * <p>
5798         * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
5799         * rather than constructing or binding a ViewHolder if it doesn't think it has time.
5800         * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
5801         * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
5802         * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
5803         *
5804         * @param position Position of ViewHolder to be returned.
5805         * @param dryRun True if the ViewHolder should not be removed from scrap/cache/
5806         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5807         *                   complete. If FOREVER_NS is passed, this method will not fail to
5808         *                   create/bind the holder if needed.
5809         *
5810         * @return ViewHolder for requested position
5811         */
5812        @Nullable
5813        ViewHolder tryGetViewHolderForPositionByDeadline(int position,
5814                boolean dryRun, long deadlineNs) {
5815            if (position < 0 || position >= mState.getItemCount()) {
5816                throw new IndexOutOfBoundsException("Invalid item position " + position
5817                        + "(" + position + "). Item count:" + mState.getItemCount()
5818                        + exceptionLabel());
5819            }
5820            boolean fromScrapOrHiddenOrCache = false;
5821            ViewHolder holder = null;
5822            // 0) If there is a changed scrap, try to find from there
5823            if (mState.isPreLayout()) {
5824                holder = getChangedScrapViewForPosition(position);
5825                fromScrapOrHiddenOrCache = holder != null;
5826            }
5827            // 1) Find by position from scrap/hidden list/cache
5828            if (holder == null) {
5829                holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
5830                if (holder != null) {
5831                    if (!validateViewHolderForOffsetPosition(holder)) {
5832                        // recycle holder (and unscrap if relevant) since it can't be used
5833                        if (!dryRun) {
5834                            // we would like to recycle this but need to make sure it is not used by
5835                            // animation logic etc.
5836                            holder.addFlags(ViewHolder.FLAG_INVALID);
5837                            if (holder.isScrap()) {
5838                                removeDetachedView(holder.itemView, false);
5839                                holder.unScrap();
5840                            } else if (holder.wasReturnedFromScrap()) {
5841                                holder.clearReturnedFromScrapFlag();
5842                            }
5843                            recycleViewHolderInternal(holder);
5844                        }
5845                        holder = null;
5846                    } else {
5847                        fromScrapOrHiddenOrCache = true;
5848                    }
5849                }
5850            }
5851            if (holder == null) {
5852                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5853                if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5854                    throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5855                            + "position " + position + "(offset:" + offsetPosition + ")."
5856                            + "state:" + mState.getItemCount() + exceptionLabel());
5857                }
5858
5859                final int type = mAdapter.getItemViewType(offsetPosition);
5860                // 2) Find from scrap/cache via stable ids, if exists
5861                if (mAdapter.hasStableIds()) {
5862                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
5863                            type, dryRun);
5864                    if (holder != null) {
5865                        // update position
5866                        holder.mPosition = offsetPosition;
5867                        fromScrapOrHiddenOrCache = true;
5868                    }
5869                }
5870                if (holder == null && mViewCacheExtension != null) {
5871                    // We are NOT sending the offsetPosition because LayoutManager does not
5872                    // know it.
5873                    final View view = mViewCacheExtension
5874                            .getViewForPositionAndType(this, position, type);
5875                    if (view != null) {
5876                        holder = getChildViewHolder(view);
5877                        if (holder == null) {
5878                            throw new IllegalArgumentException("getViewForPositionAndType returned"
5879                                    + " a view which does not have a ViewHolder"
5880                                    + exceptionLabel());
5881                        } else if (holder.shouldIgnore()) {
5882                            throw new IllegalArgumentException("getViewForPositionAndType returned"
5883                                    + " a view that is ignored. You must call stopIgnoring before"
5884                                    + " returning this view." + exceptionLabel());
5885                        }
5886                    }
5887                }
5888                if (holder == null) { // fallback to pool
5889                    if (DEBUG) {
5890                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
5891                                + position + ") fetching from shared pool");
5892                    }
5893                    holder = getRecycledViewPool().getRecycledView(type);
5894                    if (holder != null) {
5895                        holder.resetInternal();
5896                        if (FORCE_INVALIDATE_DISPLAY_LIST) {
5897                            invalidateDisplayListInt(holder);
5898                        }
5899                    }
5900                }
5901                if (holder == null) {
5902                    long start = getNanoTime();
5903                    if (deadlineNs != FOREVER_NS
5904                            && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
5905                        // abort - we have a deadline we can't meet
5906                        return null;
5907                    }
5908                    holder = mAdapter.createViewHolder(RecyclerView.this, type);
5909                    if (ALLOW_THREAD_GAP_WORK) {
5910                        // only bother finding nested RV if prefetching
5911                        RecyclerView innerView = findNestedRecyclerView(holder.itemView);
5912                        if (innerView != null) {
5913                            holder.mNestedRecyclerView = new WeakReference<>(innerView);
5914                        }
5915                    }
5916
5917                    long end = getNanoTime();
5918                    mRecyclerPool.factorInCreateTime(type, end - start);
5919                    if (DEBUG) {
5920                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
5921                    }
5922                }
5923            }
5924
5925            // This is very ugly but the only place we can grab this information
5926            // before the View is rebound and returned to the LayoutManager for post layout ops.
5927            // We don't need this in pre-layout since the VH is not updated by the LM.
5928            if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
5929                    .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
5930                holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5931                if (mState.mRunSimpleAnimations) {
5932                    int changeFlags = ItemAnimator
5933                            .buildAdapterChangeFlagsForAnimations(holder);
5934                    changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
5935                    final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
5936                            holder, changeFlags, holder.getUnmodifiedPayloads());
5937                    recordAnimationInfoIfBouncedHiddenView(holder, info);
5938                }
5939            }
5940
5941            boolean bound = false;
5942            if (mState.isPreLayout() && holder.isBound()) {
5943                // do not update unless we absolutely have to.
5944                holder.mPreLayoutPosition = position;
5945            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
5946                if (DEBUG && holder.isRemoved()) {
5947                    throw new IllegalStateException("Removed holder should be bound and it should"
5948                            + " come here only in pre-layout. Holder: " + holder
5949                            + exceptionLabel());
5950                }
5951                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5952                bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
5953            }
5954
5955            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5956            final LayoutParams rvLayoutParams;
5957            if (lp == null) {
5958                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5959                holder.itemView.setLayoutParams(rvLayoutParams);
5960            } else if (!checkLayoutParams(lp)) {
5961                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5962                holder.itemView.setLayoutParams(rvLayoutParams);
5963            } else {
5964                rvLayoutParams = (LayoutParams) lp;
5965            }
5966            rvLayoutParams.mViewHolder = holder;
5967            rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
5968            return holder;
5969        }
5970
5971        private void attachAccessibilityDelegateOnBind(ViewHolder holder) {
5972            if (isAccessibilityEnabled()) {
5973                final View itemView = holder.itemView;
5974                if (ViewCompat.getImportantForAccessibility(itemView)
5975                        == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
5976                    ViewCompat.setImportantForAccessibility(itemView,
5977                            ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
5978                }
5979                if (!ViewCompat.hasAccessibilityDelegate(itemView)) {
5980                    holder.addFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
5981                    ViewCompat.setAccessibilityDelegate(itemView,
5982                            mAccessibilityDelegate.getItemDelegate());
5983                }
5984            }
5985        }
5986
5987        private void invalidateDisplayListInt(ViewHolder holder) {
5988            if (holder.itemView instanceof ViewGroup) {
5989                invalidateDisplayListInt((ViewGroup) holder.itemView, false);
5990            }
5991        }
5992
5993        private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
5994            for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
5995                final View view = viewGroup.getChildAt(i);
5996                if (view instanceof ViewGroup) {
5997                    invalidateDisplayListInt((ViewGroup) view, true);
5998                }
5999            }
6000            if (!invalidateThis) {
6001                return;
6002            }
6003            // we need to force it to become invisible
6004            if (viewGroup.getVisibility() == View.INVISIBLE) {
6005                viewGroup.setVisibility(View.VISIBLE);
6006                viewGroup.setVisibility(View.INVISIBLE);
6007            } else {
6008                final int visibility = viewGroup.getVisibility();
6009                viewGroup.setVisibility(View.INVISIBLE);
6010                viewGroup.setVisibility(visibility);
6011            }
6012        }
6013
6014        /**
6015         * Recycle a detached view. The specified view will be added to a pool of views
6016         * for later rebinding and reuse.
6017         *
6018         * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
6019         * View is scrapped, it will be removed from scrap list.</p>
6020         *
6021         * @param view Removed view for recycling
6022         * @see LayoutManager#removeAndRecycleView(View, Recycler)
6023         */
6024        public void recycleView(@NonNull View view) {
6025            // This public recycle method tries to make view recycle-able since layout manager
6026            // intended to recycle this view (e.g. even if it is in scrap or change cache)
6027            ViewHolder holder = getChildViewHolderInt(view);
6028            if (holder.isTmpDetached()) {
6029                removeDetachedView(view, false);
6030            }
6031            if (holder.isScrap()) {
6032                holder.unScrap();
6033            } else if (holder.wasReturnedFromScrap()) {
6034                holder.clearReturnedFromScrapFlag();
6035            }
6036            recycleViewHolderInternal(holder);
6037        }
6038
6039        /**
6040         * Internally, use this method instead of {@link #recycleView(android.view.View)} to
6041         * catch potential bugs.
6042         * @param view
6043         */
6044        void recycleViewInternal(View view) {
6045            recycleViewHolderInternal(getChildViewHolderInt(view));
6046        }
6047
6048        void recycleAndClearCachedViews() {
6049            final int count = mCachedViews.size();
6050            for (int i = count - 1; i >= 0; i--) {
6051                recycleCachedViewAt(i);
6052            }
6053            mCachedViews.clear();
6054            if (ALLOW_THREAD_GAP_WORK) {
6055                mPrefetchRegistry.clearPrefetchPositions();
6056            }
6057        }
6058
6059        /**
6060         * Recycles a cached view and removes the view from the list. Views are added to cache
6061         * if and only if they are recyclable, so this method does not check it again.
6062         * <p>
6063         * A small exception to this rule is when the view does not have an animator reference
6064         * but transient state is true (due to animations created outside ItemAnimator). In that
6065         * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
6066         * still recyclable since Adapter wants to do so.
6067         *
6068         * @param cachedViewIndex The index of the view in cached views list
6069         */
6070        void recycleCachedViewAt(int cachedViewIndex) {
6071            if (DEBUG) {
6072                Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
6073            }
6074            ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
6075            if (DEBUG) {
6076                Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
6077            }
6078            addViewHolderToRecycledViewPool(viewHolder, true);
6079            mCachedViews.remove(cachedViewIndex);
6080        }
6081
6082        /**
6083         * internal implementation checks if view is scrapped or attached and throws an exception
6084         * if so.
6085         * Public version un-scraps before calling recycle.
6086         */
6087        void recycleViewHolderInternal(ViewHolder holder) {
6088            if (holder.isScrap() || holder.itemView.getParent() != null) {
6089                throw new IllegalArgumentException(
6090                        "Scrapped or attached views may not be recycled. isScrap:"
6091                                + holder.isScrap() + " isAttached:"
6092                                + (holder.itemView.getParent() != null) + exceptionLabel());
6093            }
6094
6095            if (holder.isTmpDetached()) {
6096                throw new IllegalArgumentException("Tmp detached view should be removed "
6097                        + "from RecyclerView before it can be recycled: " + holder
6098                        + exceptionLabel());
6099            }
6100
6101            if (holder.shouldIgnore()) {
6102                throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
6103                        + " should first call stopIgnoringView(view) before calling recycle."
6104                        + exceptionLabel());
6105            }
6106            //noinspection unchecked
6107            final boolean transientStatePreventsRecycling = holder
6108                    .doesTransientStatePreventRecycling();
6109            final boolean forceRecycle = mAdapter != null
6110                    && transientStatePreventsRecycling
6111                    && mAdapter.onFailedToRecycleView(holder);
6112            boolean cached = false;
6113            boolean recycled = false;
6114            if (DEBUG && mCachedViews.contains(holder)) {
6115                throw new IllegalArgumentException("cached view received recycle internal? "
6116                        + holder + exceptionLabel());
6117            }
6118            if (forceRecycle || holder.isRecyclable()) {
6119                if (mViewCacheMax > 0
6120                        && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
6121                        | ViewHolder.FLAG_REMOVED
6122                        | ViewHolder.FLAG_UPDATE
6123                        | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
6124                    // Retire oldest cached view
6125                    int cachedViewSize = mCachedViews.size();
6126                    if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
6127                        recycleCachedViewAt(0);
6128                        cachedViewSize--;
6129                    }
6130
6131                    int targetCacheIndex = cachedViewSize;
6132                    if (ALLOW_THREAD_GAP_WORK
6133                            && cachedViewSize > 0
6134                            && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
6135                        // when adding the view, skip past most recently prefetched views
6136                        int cacheIndex = cachedViewSize - 1;
6137                        while (cacheIndex >= 0) {
6138                            int cachedPos = mCachedViews.get(cacheIndex).mPosition;
6139                            if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
6140                                break;
6141                            }
6142                            cacheIndex--;
6143                        }
6144                        targetCacheIndex = cacheIndex + 1;
6145                    }
6146                    mCachedViews.add(targetCacheIndex, holder);
6147                    cached = true;
6148                }
6149                if (!cached) {
6150                    addViewHolderToRecycledViewPool(holder, true);
6151                    recycled = true;
6152                }
6153            } else {
6154                // NOTE: A view can fail to be recycled when it is scrolled off while an animation
6155                // runs. In this case, the item is eventually recycled by
6156                // ItemAnimatorRestoreListener#onAnimationFinished.
6157
6158                // TODO: consider cancelling an animation when an item is removed scrollBy,
6159                // to return it to the pool faster
6160                if (DEBUG) {
6161                    Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
6162                            + "re-visit here. We are still removing it from animation lists"
6163                            + exceptionLabel());
6164                }
6165            }
6166            // even if the holder is not removed, we still call this method so that it is removed
6167            // from view holder lists.
6168            mViewInfoStore.removeViewHolder(holder);
6169            if (!cached && !recycled && transientStatePreventsRecycling) {
6170                holder.mOwnerRecyclerView = null;
6171            }
6172        }
6173
6174        /**
6175         * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
6176         *
6177         * Pass false to dispatchRecycled for views that have not been bound.
6178         *
6179         * @param holder Holder to be added to the pool.
6180         * @param dispatchRecycled True to dispatch View recycled callbacks.
6181         */
6182        void addViewHolderToRecycledViewPool(@NonNull ViewHolder holder, boolean dispatchRecycled) {
6183            clearNestedRecyclerViewIfNotNested(holder);
6184            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE)) {
6185                holder.setFlags(0, ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
6186                ViewCompat.setAccessibilityDelegate(holder.itemView, null);
6187            }
6188            if (dispatchRecycled) {
6189                dispatchViewRecycled(holder);
6190            }
6191            holder.mOwnerRecyclerView = null;
6192            getRecycledViewPool().putRecycledView(holder);
6193        }
6194
6195        /**
6196         * Used as a fast path for unscrapping and recycling a view during a bulk operation.
6197         * The caller must call {@link #clearScrap()} when it's done to update the recycler's
6198         * internal bookkeeping.
6199         */
6200        void quickRecycleScrapView(View view) {
6201            final ViewHolder holder = getChildViewHolderInt(view);
6202            holder.mScrapContainer = null;
6203            holder.mInChangeScrap = false;
6204            holder.clearReturnedFromScrapFlag();
6205            recycleViewHolderInternal(holder);
6206        }
6207
6208        /**
6209         * Mark an attached view as scrap.
6210         *
6211         * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
6212         * for rebinding and reuse. Requests for a view for a given position may return a
6213         * reused or rebound scrap view instance.</p>
6214         *
6215         * @param view View to scrap
6216         */
6217        void scrapView(View view) {
6218            final ViewHolder holder = getChildViewHolderInt(view);
6219            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
6220                    || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
6221                if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
6222                    throw new IllegalArgumentException("Called scrap view with an invalid view."
6223                            + " Invalid views cannot be reused from scrap, they should rebound from"
6224                            + " recycler pool." + exceptionLabel());
6225                }
6226                holder.setScrapContainer(this, false);
6227                mAttachedScrap.add(holder);
6228            } else {
6229                if (mChangedScrap == null) {
6230                    mChangedScrap = new ArrayList<ViewHolder>();
6231                }
6232                holder.setScrapContainer(this, true);
6233                mChangedScrap.add(holder);
6234            }
6235        }
6236
6237        /**
6238         * Remove a previously scrapped view from the pool of eligible scrap.
6239         *
6240         * <p>This view will no longer be eligible for reuse until re-scrapped or
6241         * until it is explicitly removed and recycled.</p>
6242         */
6243        void unscrapView(ViewHolder holder) {
6244            if (holder.mInChangeScrap) {
6245                mChangedScrap.remove(holder);
6246            } else {
6247                mAttachedScrap.remove(holder);
6248            }
6249            holder.mScrapContainer = null;
6250            holder.mInChangeScrap = false;
6251            holder.clearReturnedFromScrapFlag();
6252        }
6253
6254        int getScrapCount() {
6255            return mAttachedScrap.size();
6256        }
6257
6258        View getScrapViewAt(int index) {
6259            return mAttachedScrap.get(index).itemView;
6260        }
6261
6262        void clearScrap() {
6263            mAttachedScrap.clear();
6264            if (mChangedScrap != null) {
6265                mChangedScrap.clear();
6266            }
6267        }
6268
6269        ViewHolder getChangedScrapViewForPosition(int position) {
6270            // If pre-layout, check the changed scrap for an exact match.
6271            final int changedScrapSize;
6272            if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
6273                return null;
6274            }
6275            // find by position
6276            for (int i = 0; i < changedScrapSize; i++) {
6277                final ViewHolder holder = mChangedScrap.get(i);
6278                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
6279                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6280                    return holder;
6281                }
6282            }
6283            // find by id
6284            if (mAdapter.hasStableIds()) {
6285                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
6286                if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
6287                    final long id = mAdapter.getItemId(offsetPosition);
6288                    for (int i = 0; i < changedScrapSize; i++) {
6289                        final ViewHolder holder = mChangedScrap.get(i);
6290                        if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
6291                            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6292                            return holder;
6293                        }
6294                    }
6295                }
6296            }
6297            return null;
6298        }
6299
6300        /**
6301         * Returns a view for the position either from attach scrap, hidden children, or cache.
6302         *
6303         * @param position Item position
6304         * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
6305         * @return a ViewHolder that can be re-used for this position.
6306         */
6307        ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
6308            final int scrapCount = mAttachedScrap.size();
6309
6310            // Try first for an exact, non-invalid match from scrap.
6311            for (int i = 0; i < scrapCount; i++) {
6312                final ViewHolder holder = mAttachedScrap.get(i);
6313                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
6314                        && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
6315                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6316                    return holder;
6317                }
6318            }
6319
6320            if (!dryRun) {
6321                View view = mChildHelper.findHiddenNonRemovedView(position);
6322                if (view != null) {
6323                    // This View is good to be used. We just need to unhide, detach and move to the
6324                    // scrap list.
6325                    final ViewHolder vh = getChildViewHolderInt(view);
6326                    mChildHelper.unhide(view);
6327                    int layoutIndex = mChildHelper.indexOfChild(view);
6328                    if (layoutIndex == RecyclerView.NO_POSITION) {
6329                        throw new IllegalStateException("layout index should not be -1 after "
6330                                + "unhiding a view:" + vh + exceptionLabel());
6331                    }
6332                    mChildHelper.detachViewFromParent(layoutIndex);
6333                    scrapView(view);
6334                    vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
6335                            | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
6336                    return vh;
6337                }
6338            }
6339
6340            // Search in our first-level recycled view cache.
6341            final int cacheSize = mCachedViews.size();
6342            for (int i = 0; i < cacheSize; i++) {
6343                final ViewHolder holder = mCachedViews.get(i);
6344                // invalid view holders may be in cache if adapter has stable ids as they can be
6345                // retrieved via getScrapOrCachedViewForId
6346                if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
6347                    if (!dryRun) {
6348                        mCachedViews.remove(i);
6349                    }
6350                    if (DEBUG) {
6351                        Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
6352                                + ") found match in cache: " + holder);
6353                    }
6354                    return holder;
6355                }
6356            }
6357            return null;
6358        }
6359
6360        ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
6361            // Look in our attached views first
6362            final int count = mAttachedScrap.size();
6363            for (int i = count - 1; i >= 0; i--) {
6364                final ViewHolder holder = mAttachedScrap.get(i);
6365                if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
6366                    if (type == holder.getItemViewType()) {
6367                        holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6368                        if (holder.isRemoved()) {
6369                            // this might be valid in two cases:
6370                            // > item is removed but we are in pre-layout pass
6371                            // >> do nothing. return as is. make sure we don't rebind
6372                            // > item is removed then added to another position and we are in
6373                            // post layout.
6374                            // >> remove removed and invalid flags, add update flag to rebind
6375                            // because item was invisible to us and we don't know what happened in
6376                            // between.
6377                            if (!mState.isPreLayout()) {
6378                                holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
6379                                        | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
6380                            }
6381                        }
6382                        return holder;
6383                    } else if (!dryRun) {
6384                        // if we are running animations, it is actually better to keep it in scrap
6385                        // but this would force layout manager to lay it out which would be bad.
6386                        // Recycle this scrap. Type mismatch.
6387                        mAttachedScrap.remove(i);
6388                        removeDetachedView(holder.itemView, false);
6389                        quickRecycleScrapView(holder.itemView);
6390                    }
6391                }
6392            }
6393
6394            // Search the first-level cache
6395            final int cacheSize = mCachedViews.size();
6396            for (int i = cacheSize - 1; i >= 0; i--) {
6397                final ViewHolder holder = mCachedViews.get(i);
6398                if (holder.getItemId() == id) {
6399                    if (type == holder.getItemViewType()) {
6400                        if (!dryRun) {
6401                            mCachedViews.remove(i);
6402                        }
6403                        return holder;
6404                    } else if (!dryRun) {
6405                        recycleCachedViewAt(i);
6406                        return null;
6407                    }
6408                }
6409            }
6410            return null;
6411        }
6412
6413        void dispatchViewRecycled(@NonNull ViewHolder holder) {
6414            if (mRecyclerListener != null) {
6415                mRecyclerListener.onViewRecycled(holder);
6416            }
6417            if (mAdapter != null) {
6418                mAdapter.onViewRecycled(holder);
6419            }
6420            if (mState != null) {
6421                mViewInfoStore.removeViewHolder(holder);
6422            }
6423            if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
6424        }
6425
6426        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
6427                boolean compatibleWithPrevious) {
6428            clear();
6429            getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
6430        }
6431
6432        void offsetPositionRecordsForMove(int from, int to) {
6433            final int start, end, inBetweenOffset;
6434            if (from < to) {
6435                start = from;
6436                end = to;
6437                inBetweenOffset = -1;
6438            } else {
6439                start = to;
6440                end = from;
6441                inBetweenOffset = 1;
6442            }
6443            final int cachedCount = mCachedViews.size();
6444            for (int i = 0; i < cachedCount; i++) {
6445                final ViewHolder holder = mCachedViews.get(i);
6446                if (holder == null || holder.mPosition < start || holder.mPosition > end) {
6447                    continue;
6448                }
6449                if (holder.mPosition == from) {
6450                    holder.offsetPosition(to - from, false);
6451                } else {
6452                    holder.offsetPosition(inBetweenOffset, false);
6453                }
6454                if (DEBUG) {
6455                    Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
6456                            + holder);
6457                }
6458            }
6459        }
6460
6461        void offsetPositionRecordsForInsert(int insertedAt, int count) {
6462            final int cachedCount = mCachedViews.size();
6463            for (int i = 0; i < cachedCount; i++) {
6464                final ViewHolder holder = mCachedViews.get(i);
6465                if (holder != null && holder.mPosition >= insertedAt) {
6466                    if (DEBUG) {
6467                        Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder "
6468                                + holder + " now at position " + (holder.mPosition + count));
6469                    }
6470                    holder.offsetPosition(count, true);
6471                }
6472            }
6473        }
6474
6475        /**
6476         * @param removedFrom Remove start index
6477         * @param count Remove count
6478         * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
6479         *                         false, they'll be applied before the second layout pass
6480         */
6481        void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
6482            final int removedEnd = removedFrom + count;
6483            final int cachedCount = mCachedViews.size();
6484            for (int i = cachedCount - 1; i >= 0; i--) {
6485                final ViewHolder holder = mCachedViews.get(i);
6486                if (holder != null) {
6487                    if (holder.mPosition >= removedEnd) {
6488                        if (DEBUG) {
6489                            Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
6490                                    + " holder " + holder + " now at position "
6491                                    + (holder.mPosition - count));
6492                        }
6493                        holder.offsetPosition(-count, applyToPreLayout);
6494                    } else if (holder.mPosition >= removedFrom) {
6495                        // Item for this view was removed. Dump it from the cache.
6496                        holder.addFlags(ViewHolder.FLAG_REMOVED);
6497                        recycleCachedViewAt(i);
6498                    }
6499                }
6500            }
6501        }
6502
6503        void setViewCacheExtension(ViewCacheExtension extension) {
6504            mViewCacheExtension = extension;
6505        }
6506
6507        void setRecycledViewPool(RecycledViewPool pool) {
6508            if (mRecyclerPool != null) {
6509                mRecyclerPool.detach();
6510            }
6511            mRecyclerPool = pool;
6512            if (pool != null) {
6513                mRecyclerPool.attach(getAdapter());
6514            }
6515        }
6516
6517        RecycledViewPool getRecycledViewPool() {
6518            if (mRecyclerPool == null) {
6519                mRecyclerPool = new RecycledViewPool();
6520            }
6521            return mRecyclerPool;
6522        }
6523
6524        void viewRangeUpdate(int positionStart, int itemCount) {
6525            final int positionEnd = positionStart + itemCount;
6526            final int cachedCount = mCachedViews.size();
6527            for (int i = cachedCount - 1; i >= 0; i--) {
6528                final ViewHolder holder = mCachedViews.get(i);
6529                if (holder == null) {
6530                    continue;
6531                }
6532
6533                final int pos = holder.mPosition;
6534                if (pos >= positionStart && pos < positionEnd) {
6535                    holder.addFlags(ViewHolder.FLAG_UPDATE);
6536                    recycleCachedViewAt(i);
6537                    // cached views should not be flagged as changed because this will cause them
6538                    // to animate when they are returned from cache.
6539                }
6540            }
6541        }
6542
6543        void markKnownViewsInvalid() {
6544            final int cachedCount = mCachedViews.size();
6545            for (int i = 0; i < cachedCount; i++) {
6546                final ViewHolder holder = mCachedViews.get(i);
6547                if (holder != null) {
6548                    holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
6549                    holder.addChangePayload(null);
6550                }
6551            }
6552
6553            if (mAdapter == null || !mAdapter.hasStableIds()) {
6554                // we cannot re-use cached views in this case. Recycle them all
6555                recycleAndClearCachedViews();
6556            }
6557        }
6558
6559        void clearOldPositions() {
6560            final int cachedCount = mCachedViews.size();
6561            for (int i = 0; i < cachedCount; i++) {
6562                final ViewHolder holder = mCachedViews.get(i);
6563                holder.clearOldPosition();
6564            }
6565            final int scrapCount = mAttachedScrap.size();
6566            for (int i = 0; i < scrapCount; i++) {
6567                mAttachedScrap.get(i).clearOldPosition();
6568            }
6569            if (mChangedScrap != null) {
6570                final int changedScrapCount = mChangedScrap.size();
6571                for (int i = 0; i < changedScrapCount; i++) {
6572                    mChangedScrap.get(i).clearOldPosition();
6573                }
6574            }
6575        }
6576
6577        void markItemDecorInsetsDirty() {
6578            final int cachedCount = mCachedViews.size();
6579            for (int i = 0; i < cachedCount; i++) {
6580                final ViewHolder holder = mCachedViews.get(i);
6581                LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
6582                if (layoutParams != null) {
6583                    layoutParams.mInsetsDirty = true;
6584                }
6585            }
6586        }
6587    }
6588
6589    /**
6590     * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
6591     * be controlled by the developer.
6592     * <p>
6593     * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
6594     * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
6595     * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
6596     * {@link RecycledViewPool}.
6597     * <p>
6598     * Note that, Recycler never sends Views to this method to be cached. It is developers
6599     * responsibility to decide whether they want to keep their Views in this custom cache or let
6600     * the default recycling policy handle it.
6601     */
6602    public abstract static class ViewCacheExtension {
6603
6604        /**
6605         * Returns a View that can be binded to the given Adapter position.
6606         * <p>
6607         * This method should <b>not</b> create a new View. Instead, it is expected to return
6608         * an already created View that can be re-used for the given type and position.
6609         * If the View is marked as ignored, it should first call
6610         * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
6611         * <p>
6612         * RecyclerView will re-bind the returned View to the position if necessary.
6613         *
6614         * @param recycler The Recycler that can be used to bind the View
6615         * @param position The adapter position
6616         * @param type     The type of the View, defined by adapter
6617         * @return A View that is bound to the given position or NULL if there is no View to re-use
6618         * @see LayoutManager#ignoreView(View)
6619         */
6620        @Nullable
6621        public abstract View getViewForPositionAndType(@NonNull Recycler recycler, int position,
6622                int type);
6623    }
6624
6625    /**
6626     * Base class for an Adapter
6627     *
6628     * <p>Adapters provide a binding from an app-specific data set to views that are displayed
6629     * within a {@link RecyclerView}.</p>
6630     *
6631     * @param  A class that extends ViewHolder that will be used by the adapter.
6632     */
6633    public abstract static class Adapter<VH extends ViewHolder> {
6634        private final AdapterDataObservable mObservable = new AdapterDataObservable();
6635        private boolean mHasStableIds = false;
6636
6637        /**
6638         * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
6639         * an item.
6640         * <p>
6641         * This new ViewHolder should be constructed with a new View that can represent the items
6642         * of the given type. You can either create a new View manually or inflate it from an XML
6643         * layout file.
6644         * <p>
6645         * The new ViewHolder will be used to display items of the adapter using
6646         * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
6647         * different items in the data set, it is a good idea to cache references to sub views of
6648         * the View to avoid unnecessary {@link View#findViewById(int)} calls.
6649         *
6650         * @param parent The ViewGroup into which the new View will be added after it is bound to
6651         *               an adapter position.
6652         * @param viewType The view type of the new View.
6653         *
6654         * @return A new ViewHolder that holds a View of the given view type.
6655         * @see #getItemViewType(int)
6656         * @see #onBindViewHolder(ViewHolder, int)
6657         */
6658        @NonNull
6659        public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
6660
6661        /**
6662         * Called by RecyclerView to display the data at the specified position. This method should
6663         * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
6664         * position.
6665         * <p>
6666         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6667         * again if the position of the item changes in the data set unless the item itself is
6668         * invalidated or the new position cannot be determined. For this reason, you should only
6669         * use the <code>position</code> parameter while acquiring the related data item inside
6670         * this method and should not keep a copy of it. If you need the position of an item later
6671         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6672         * have the updated adapter position.
6673         *
6674         * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
6675         * handle efficient partial bind.
6676         *
6677         * @param holder The ViewHolder which should be updated to represent the contents of the
6678         *        item at the given position in the data set.
6679         * @param position The position of the item within the adapter's data set.
6680         */
6681        public abstract void onBindViewHolder(@NonNull VH holder, int position);
6682
6683        /**
6684         * Called by RecyclerView to display the data at the specified position. This method
6685         * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
6686         * the given position.
6687         * <p>
6688         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6689         * again if the position of the item changes in the data set unless the item itself is
6690         * invalidated or the new position cannot be determined. For this reason, you should only
6691         * use the <code>position</code> parameter while acquiring the related data item inside
6692         * this method and should not keep a copy of it. If you need the position of an item later
6693         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6694         * have the updated adapter position.
6695         * <p>
6696         * Partial bind vs full bind:
6697         * <p>
6698         * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
6699         * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
6700         * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
6701         * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
6702         * Adapter should not assume that the payload passed in notify methods will be received by
6703         * onBindViewHolder().  For example when the view is not attached to the screen, the
6704         * payload in notifyItemChange() will be simply dropped.
6705         *
6706         * @param holder The ViewHolder which should be updated to represent the contents of the
6707         *               item at the given position in the data set.
6708         * @param position The position of the item within the adapter's data set.
6709         * @param payloads A non-null list of merged payloads. Can be empty list if requires full
6710         *                 update.
6711         */
6712        public void onBindViewHolder(@NonNull VH holder, int position,
6713                @NonNull List<Object> payloads) {
6714            onBindViewHolder(holder, position);
6715        }
6716
6717        /**
6718         * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
6719         * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
6720         *
6721         * @see #onCreateViewHolder(ViewGroup, int)
6722         */
6723        @NonNull
6724        public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
6725            try {
6726                TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
6727                final VH holder = onCreateViewHolder(parent, viewType);
6728                if (holder.itemView.getParent() != null) {
6729                    throw new IllegalStateException("ViewHolder views must not be attached when"
6730                            + " created. Ensure that you are not passing 'true' to the attachToRoot"
6731                            + " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
6732                }
6733                holder.mItemViewType = viewType;
6734                return holder;
6735            } finally {
6736                TraceCompat.endSection();
6737            }
6738        }
6739
6740        /**
6741         * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
6742         * {@link ViewHolder} contents with the item at the given position and also sets up some
6743         * private fields to be used by RecyclerView.
6744         *
6745         * @see #onBindViewHolder(ViewHolder, int)
6746         */
6747        public final void bindViewHolder(@NonNull VH holder, int position) {
6748            holder.mPosition = position;
6749            if (hasStableIds()) {
6750                holder.mItemId = getItemId(position);
6751            }
6752            holder.setFlags(ViewHolder.FLAG_BOUND,
6753                    ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
6754                            | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
6755            TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
6756            onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
6757            holder.clearPayload();
6758            final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
6759            if (layoutParams instanceof RecyclerView.LayoutParams) {
6760                ((LayoutParams) layoutParams).mInsetsDirty = true;
6761            }
6762            TraceCompat.endSection();
6763        }
6764
6765        /**
6766         * Return the view type of the item at <code>position</code> for the purposes
6767         * of view recycling.
6768         *
6769         * <p>The default implementation of this method returns 0, making the assumption of
6770         * a single view type for the adapter. Unlike ListView adapters, types need not
6771         * be contiguous. Consider using id resources to uniquely identify item view types.
6772         *
6773         * @param position position to query
6774         * @return integer value identifying the type of the view needed to represent the item at
6775         *                 <code>position</code>. Type codes need not be contiguous.
6776         */
6777        public int getItemViewType(int position) {
6778            return 0;
6779        }
6780
6781        /**
6782         * Indicates whether each item in the data set can be represented with a unique identifier
6783         * of type {@link java.lang.Long}.
6784         *
6785         * @param hasStableIds Whether items in data set have unique identifiers or not.
6786         * @see #hasStableIds()
6787         * @see #getItemId(int)
6788         */
6789        public void setHasStableIds(boolean hasStableIds) {
6790            if (hasObservers()) {
6791                throw new IllegalStateException("Cannot change whether this adapter has "
6792                        + "stable IDs while the adapter has registered observers.");
6793            }
6794            mHasStableIds = hasStableIds;
6795        }
6796
6797        /**
6798         * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
6799         * would return false this method should return {@link #NO_ID}. The default implementation
6800         * of this method returns {@link #NO_ID}.
6801         *
6802         * @param position Adapter position to query
6803         * @return the stable ID of the item at position
6804         */
6805        public long getItemId(int position) {
6806            return NO_ID;
6807        }
6808
6809        /**
6810         * Returns the total number of items in the data set held by the adapter.
6811         *
6812         * @return The total number of items in this adapter.
6813         */
6814        public abstract int getItemCount();
6815
6816        /**
6817         * Returns true if this adapter publishes a unique <code>long</code> value that can
6818         * act as a key for the item at a given position in the data set. If that item is relocated
6819         * in the data set, the ID returned for that item should be the same.
6820         *
6821         * @return true if this adapter's items have stable IDs
6822         */
6823        public final boolean hasStableIds() {
6824            return mHasStableIds;
6825        }
6826
6827        /**
6828         * Called when a view created by this adapter has been recycled.
6829         *
6830         * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
6831         * needs to be attached to its parent {@link RecyclerView}. This can be because it has
6832         * fallen out of visibility or a set of cached views represented by views still
6833         * attached to the parent RecyclerView. If an item view has large or expensive data
6834         * bound to it such as large bitmaps, this may be a good place to release those
6835         * resources.</p>
6836         * <p>
6837         * RecyclerView calls this method right before clearing ViewHolder's internal data and
6838         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
6839         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
6840         * its adapter position.
6841         *
6842         * @param holder The ViewHolder for the view being recycled
6843         */
6844        public void onViewRecycled(@NonNull VH holder) {
6845        }
6846
6847        /**
6848         * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
6849         * due to its transient state. Upon receiving this callback, Adapter can clear the
6850         * animation(s) that effect the View's transient state and return <code>true</code> so that
6851         * the View can be recycled. Keep in mind that the View in question is already removed from
6852         * the RecyclerView.
6853         * <p>
6854         * In some cases, it is acceptable to recycle a View although it has transient state. Most
6855         * of the time, this is a case where the transient state will be cleared in
6856         * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
6857         * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
6858         * value of this method to decide whether the View should be recycled or not.
6859         * <p>
6860         * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
6861         * should never receive this callback because RecyclerView keeps those Views as children
6862         * until their animations are complete. This callback is useful when children of the item
6863         * views create animations which may not be easy to implement using an {@link ItemAnimator}.
6864         * <p>
6865         * You should <em>never</em> fix this issue by calling
6866         * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
6867         * <code>holder.itemView.setHasTransientState(true);</code>. Each
6868         * <code>View.setHasTransientState(true)</code> call must be matched by a
6869         * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
6870         * may become inconsistent. You should always prefer to end or cancel animations that are
6871         * triggering the transient state instead of handling it manually.
6872         *
6873         * @param holder The ViewHolder containing the View that could not be recycled due to its
6874         *               transient state.
6875         * @return True if the View should be recycled, false otherwise. Note that if this method
6876         * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
6877         * the View and recycle it regardless. If this method returns <code>false</code>,
6878         * RecyclerView will check the View's transient state again before giving a final decision.
6879         * Default implementation returns false.
6880         */
6881        public boolean onFailedToRecycleView(@NonNull VH holder) {
6882            return false;
6883        }
6884
6885        /**
6886         * Called when a view created by this adapter has been attached to a window.
6887         *
6888         * <p>This can be used as a reasonable signal that the view is about to be seen
6889         * by the user. If the adapter previously freed any resources in
6890         * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
6891         * those resources should be restored here.</p>
6892         *
6893         * @param holder Holder of the view being attached
6894         */
6895        public void onViewAttachedToWindow(@NonNull VH holder) {
6896        }
6897
6898        /**
6899         * Called when a view created by this adapter has been detached from its window.
6900         *
6901         * <p>Becoming detached from the window is not necessarily a permanent condition;
6902         * the consumer of an Adapter's views may choose to cache views offscreen while they
6903         * are not visible, attaching and detaching them as appropriate.</p>
6904         *
6905         * @param holder Holder of the view being detached
6906         */
6907        public void onViewDetachedFromWindow(@NonNull VH holder) {
6908        }
6909
6910        /**
6911         * Returns true if one or more observers are attached to this adapter.
6912         *
6913         * @return true if this adapter has observers
6914         */
6915        public final boolean hasObservers() {
6916            return mObservable.hasObservers();
6917        }
6918
6919        /**
6920         * Register a new observer to listen for data changes.
6921         *
6922         * <p>The adapter may publish a variety of events describing specific changes.
6923         * Not all adapters may support all change types and some may fall back to a generic
6924         * {@link RecyclerView.AdapterDataObserver#onChanged()
6925         * "something changed"} event if more specific data is not available.</p>
6926         *
6927         * <p>Components registering observers with an adapter are responsible for
6928         * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6929         * unregistering} those observers when finished.</p>
6930         *
6931         * @param observer Observer to register
6932         *
6933         * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6934         */
6935        public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
6936            mObservable.registerObserver(observer);
6937        }
6938
6939        /**
6940         * Unregister an observer currently listening for data changes.
6941         *
6942         * <p>The unregistered observer will no longer receive events about changes
6943         * to the adapter.</p>
6944         *
6945         * @param observer Observer to unregister
6946         *
6947         * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
6948         */
6949        public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {
6950            mObservable.unregisterObserver(observer);
6951        }
6952
6953        /**
6954         * Called by RecyclerView when it starts observing this Adapter.
6955         * <p>
6956         * Keep in mind that same adapter may be observed by multiple RecyclerViews.
6957         *
6958         * @param recyclerView The RecyclerView instance which started observing this adapter.
6959         * @see #onDetachedFromRecyclerView(RecyclerView)
6960         */
6961        public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
6962        }
6963
6964        /**
6965         * Called by RecyclerView when it stops observing this Adapter.
6966         *
6967         * @param recyclerView The RecyclerView instance which stopped observing this adapter.
6968         * @see #onAttachedToRecyclerView(RecyclerView)
6969         */
6970        public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
6971        }
6972
6973        /**
6974         * Notify any registered observers that the data set has changed.
6975         *
6976         * <p>There are two different classes of data change events, item changes and structural
6977         * changes. Item changes are when a single item has its data updated but no positional
6978         * changes have occurred. Structural changes are when items are inserted, removed or moved
6979         * within the data set.</p>
6980         *
6981         * <p>This event does not specify what about the data set has changed, forcing
6982         * any observers to assume that all existing items and structure may no longer be valid.
6983         * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
6984         *
6985         * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
6986         * for adapters that report that they have {@link #hasStableIds() stable IDs} when
6987         * this method is used. This can help for the purposes of animation and visual
6988         * object persistence but individual item views will still need to be rebound
6989         * and relaid out.</p>
6990         *
6991         * <p>If you are writing an adapter it will always be more efficient to use the more
6992         * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
6993         * as a last resort.</p>
6994         *
6995         * @see #notifyItemChanged(int)
6996         * @see #notifyItemInserted(int)
6997         * @see #notifyItemRemoved(int)
6998         * @see #notifyItemRangeChanged(int, int)
6999         * @see #notifyItemRangeInserted(int, int)
7000         * @see #notifyItemRangeRemoved(int, int)
7001         */
7002        public final void notifyDataSetChanged() {
7003            mObservable.notifyChanged();
7004        }
7005
7006        /**
7007         * Notify any registered observers that the item at <code>position</code> has changed.
7008         * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
7009         *
7010         * <p>This is an item change event, not a structural change event. It indicates that any
7011         * reflection of the data at <code>position</code> is out of date and should be updated.
7012         * The item at <code>position</code> retains the same identity.</p>
7013         *
7014         * @param position Position of the item that has changed
7015         *
7016         * @see #notifyItemRangeChanged(int, int)
7017         */
7018        public final void notifyItemChanged(int position) {
7019            mObservable.notifyItemRangeChanged(position, 1);
7020        }
7021
7022        /**
7023         * Notify any registered observers that the item at <code>position</code> has changed with
7024         * an optional payload object.
7025         *
7026         * <p>This is an item change event, not a structural change event. It indicates that any
7027         * reflection of the data at <code>position</code> is out of date and should be updated.
7028         * The item at <code>position</code> retains the same identity.
7029         * </p>
7030         *
7031         * <p>
7032         * Client can optionally pass a payload for partial change. These payloads will be merged
7033         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
7034         * item is already represented by a ViewHolder and it will be rebound to the same
7035         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
7036         * payloads on that item and prevent future payload until
7037         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
7038         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
7039         * attached, the payload will be simply dropped.
7040         *
7041         * @param position Position of the item that has changed
7042         * @param payload Optional parameter, use null to identify a "full" update
7043         *
7044         * @see #notifyItemRangeChanged(int, int)
7045         */
7046        public final void notifyItemChanged(int position, @Nullable Object payload) {
7047            mObservable.notifyItemRangeChanged(position, 1, payload);
7048        }
7049
7050        /**
7051         * Notify any registered observers that the <code>itemCount</code> items starting at
7052         * position <code>positionStart</code> have changed.
7053         * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
7054         *
7055         * <p>This is an item change event, not a structural change event. It indicates that
7056         * any reflection of the data in the given position range is out of date and should
7057         * be updated. The items in the given range retain the same identity.</p>
7058         *
7059         * @param positionStart Position of the first item that has changed
7060         * @param itemCount Number of items that have changed
7061         *
7062         * @see #notifyItemChanged(int)
7063         */
7064        public final void notifyItemRangeChanged(int positionStart, int itemCount) {
7065            mObservable.notifyItemRangeChanged(positionStart, itemCount);
7066        }
7067
7068        /**
7069         * Notify any registered observers that the <code>itemCount</code> items starting at
7070         * position <code>positionStart</code> have changed. An optional payload can be
7071         * passed to each changed item.
7072         *
7073         * <p>This is an item change event, not a structural change event. It indicates that any
7074         * reflection of the data in the given position range is out of date and should be updated.
7075         * The items in the given range retain the same identity.
7076         * </p>
7077         *
7078         * <p>
7079         * Client can optionally pass a payload for partial change. These payloads will be merged
7080         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
7081         * item is already represented by a ViewHolder and it will be rebound to the same
7082         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
7083         * payloads on that item and prevent future payload until
7084         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
7085         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
7086         * attached, the payload will be simply dropped.
7087         *
7088         * @param positionStart Position of the first item that has changed
7089         * @param itemCount Number of items that have changed
7090         * @param payload  Optional parameter, use null to identify a "full" update
7091         *
7092         * @see #notifyItemChanged(int)
7093         */
7094        public final void notifyItemRangeChanged(int positionStart, int itemCount,
7095                @Nullable Object payload) {
7096            mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
7097        }
7098
7099        /**
7100         * Notify any registered observers that the item reflected at <code>position</code>
7101         * has been newly inserted. The item previously at <code>position</code> is now at
7102         * position <code>position + 1</code>.
7103         *
7104         * <p>This is a structural change event. Representations of other existing items in the
7105         * data set are still considered up to date and will not be rebound, though their
7106         * positions may be altered.</p>
7107         *
7108         * @param position Position of the newly inserted item in the data set
7109         *
7110         * @see #notifyItemRangeInserted(int, int)
7111         */
7112        public final void notifyItemInserted(int position) {
7113            mObservable.notifyItemRangeInserted(position, 1);
7114        }
7115
7116        /**
7117         * Notify any registered observers that the item reflected at <code>fromPosition</code>
7118         * has been moved to <code>toPosition</code>.
7119         *
7120         * <p>This is a structural change event. Representations of other existing items in the
7121         * data set are still considered up to date and will not be rebound, though their
7122         * positions may be altered.</p>
7123         *
7124         * @param fromPosition Previous position of the item.
7125         * @param toPosition New position of the item.
7126         */
7127        public final void notifyItemMoved(int fromPosition, int toPosition) {
7128            mObservable.notifyItemMoved(fromPosition, toPosition);
7129        }
7130
7131        /**
7132         * Notify any registered observers that the currently reflected <code>itemCount</code>
7133         * items starting at <code>positionStart</code> have been newly inserted. The items
7134         * previously located at <code>positionStart</code> and beyond can now be found starting
7135         * at position <code>positionStart + itemCount</code>.
7136         *
7137         * <p>This is a structural change event. Representations of other existing items in the
7138         * data set are still considered up to date and will not be rebound, though their positions
7139         * may be altered.</p>
7140         *
7141         * @param positionStart Position of the first item that was inserted
7142         * @param itemCount Number of items inserted
7143         *
7144         * @see #notifyItemInserted(int)
7145         */
7146        public final void notifyItemRangeInserted(int positionStart, int itemCount) {
7147            mObservable.notifyItemRangeInserted(positionStart, itemCount);
7148        }
7149
7150        /**
7151         * Notify any registered observers that the item previously located at <code>position</code>
7152         * has been removed from the data set. The items previously located at and after
7153         * <code>position</code> may now be found at <code>oldPosition - 1</code>.
7154         *
7155         * <p>This is a structural change event. Representations of other existing items in the
7156         * data set are still considered up to date and will not be rebound, though their positions
7157         * may be altered.</p>
7158         *
7159         * @param position Position of the item that has now been removed
7160         *
7161         * @see #notifyItemRangeRemoved(int, int)
7162         */
7163        public final void notifyItemRemoved(int position) {
7164            mObservable.notifyItemRangeRemoved(position, 1);
7165        }
7166
7167        /**
7168         * Notify any registered observers that the <code>itemCount</code> items previously
7169         * located at <code>positionStart</code> have been removed from the data set. The items
7170         * previously located at and after <code>positionStart + itemCount</code> may now be found
7171         * at <code>oldPosition - itemCount</code>.
7172         *
7173         * <p>This is a structural change event. Representations of other existing items in the data
7174         * set are still considered up to date and will not be rebound, though their positions
7175         * may be altered.</p>
7176         *
7177         * @param positionStart Previous position of the first item that was removed
7178         * @param itemCount Number of items removed from the data set
7179         */
7180        public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
7181            mObservable.notifyItemRangeRemoved(positionStart, itemCount);
7182        }
7183    }
7184
7185    void dispatchChildDetached(View child) {
7186        final ViewHolder viewHolder = getChildViewHolderInt(child);
7187        onChildDetachedFromWindow(child);
7188        if (mAdapter != null && viewHolder != null) {
7189            mAdapter.onViewDetachedFromWindow(viewHolder);
7190        }
7191        if (mOnChildAttachStateListeners != null) {
7192            final int cnt = mOnChildAttachStateListeners.size();
7193            for (int i = cnt - 1; i >= 0; i--) {
7194                mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
7195            }
7196        }
7197    }
7198
7199    void dispatchChildAttached(View child) {
7200        final ViewHolder viewHolder = getChildViewHolderInt(child);
7201        onChildAttachedToWindow(child);
7202        if (mAdapter != null && viewHolder != null) {
7203            mAdapter.onViewAttachedToWindow(viewHolder);
7204        }
7205        if (mOnChildAttachStateListeners != null) {
7206            final int cnt = mOnChildAttachStateListeners.size();
7207            for (int i = cnt - 1; i >= 0; i--) {
7208                mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
7209            }
7210        }
7211    }
7212
7213    /**
7214     * A <code>LayoutManager</code> is responsible for measuring and positioning item views
7215     * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
7216     * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
7217     * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
7218     * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
7219     * layout managers are provided for general use.
7220     * <p/>
7221     * If the LayoutManager specifies a default constructor or one with the signature
7222     * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
7223     * instantiate and set the LayoutManager when being inflated. Most used properties can
7224     * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
7225     * a LayoutManager specifies both constructors, the non-default constructor will take
7226     * precedence.
7227     *
7228     */
7229    public abstract static class LayoutManager {
7230        ChildHelper mChildHelper;
7231        RecyclerView mRecyclerView;
7232
7233        /**
7234         * The callback used for retrieving information about a RecyclerView and its children in the
7235         * horizontal direction.
7236         */
7237        private final ViewBoundsCheck.Callback mHorizontalBoundCheckCallback =
7238                new ViewBoundsCheck.Callback() {
7239                    @Override
7240                    public int getChildCount() {
7241                        return LayoutManager.this.getChildCount();
7242                    }
7243
7244                    @Override
7245                    public View getParent() {
7246                        return mRecyclerView;
7247                    }
7248
7249                    @Override
7250                    public View getChildAt(int index) {
7251                        return LayoutManager.this.getChildAt(index);
7252                    }
7253
7254                    @Override
7255                    public int getParentStart() {
7256                        return LayoutManager.this.getPaddingLeft();
7257                    }
7258
7259                    @Override
7260                    public int getParentEnd() {
7261                        return LayoutManager.this.getWidth() - LayoutManager.this.getPaddingRight();
7262                    }
7263
7264                    @Override
7265                    public int getChildStart(View view) {
7266                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7267                                view.getLayoutParams();
7268                        return LayoutManager.this.getDecoratedLeft(view) - params.leftMargin;
7269                    }
7270
7271                    @Override
7272                    public int getChildEnd(View view) {
7273                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7274                                view.getLayoutParams();
7275                        return LayoutManager.this.getDecoratedRight(view) + params.rightMargin;
7276                    }
7277                };
7278
7279        /**
7280         * The callback used for retrieving information about a RecyclerView and its children in the
7281         * vertical direction.
7282         */
7283        private final ViewBoundsCheck.Callback mVerticalBoundCheckCallback =
7284                new ViewBoundsCheck.Callback() {
7285                    @Override
7286                    public int getChildCount() {
7287                        return LayoutManager.this.getChildCount();
7288                    }
7289
7290                    @Override
7291                    public View getParent() {
7292                        return mRecyclerView;
7293                    }
7294
7295                    @Override
7296                    public View getChildAt(int index) {
7297                        return LayoutManager.this.getChildAt(index);
7298                    }
7299
7300                    @Override
7301                    public int getParentStart() {
7302                        return LayoutManager.this.getPaddingTop();
7303                    }
7304
7305                    @Override
7306                    public int getParentEnd() {
7307                        return LayoutManager.this.getHeight()
7308                                - LayoutManager.this.getPaddingBottom();
7309                    }
7310
7311                    @Override
7312                    public int getChildStart(View view) {
7313                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7314                                view.getLayoutParams();
7315                        return LayoutManager.this.getDecoratedTop(view) - params.topMargin;
7316                    }
7317
7318                    @Override
7319                    public int getChildEnd(View view) {
7320                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7321                                view.getLayoutParams();
7322                        return LayoutManager.this.getDecoratedBottom(view) + params.bottomMargin;
7323                    }
7324                };
7325
7326        /**
7327         * Utility objects used to check the boundaries of children against their parent
7328         * RecyclerView.
7329         * @see #isViewPartiallyVisible(View, boolean, boolean),
7330         * {@link LinearLayoutManager#findOneVisibleChild(int, int, boolean, boolean)},
7331         * and {@link LinearLayoutManager#findOnePartiallyOrCompletelyInvisibleChild(int, int)}.
7332         */
7333        ViewBoundsCheck mHorizontalBoundCheck = new ViewBoundsCheck(mHorizontalBoundCheckCallback);
7334        ViewBoundsCheck mVerticalBoundCheck = new ViewBoundsCheck(mVerticalBoundCheckCallback);
7335
7336        @Nullable
7337        SmoothScroller mSmoothScroller;
7338
7339        boolean mRequestedSimpleAnimations = false;
7340
7341        boolean mIsAttachedToWindow = false;
7342
7343        /**
7344         * This field is only set via the deprecated {@link #setAutoMeasureEnabled(boolean)} and is
7345         * only accessed via {@link #isAutoMeasureEnabled()} for backwards compatability reasons.
7346         */
7347        boolean mAutoMeasure = false;
7348
7349        /**
7350         * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
7351         * if the space that will be given to it is already larger than what it has measured before.
7352         */
7353        private boolean mMeasurementCacheEnabled = true;
7354
7355        private boolean mItemPrefetchEnabled = true;
7356
7357        /**
7358         * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
7359         * requested by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)} or
7360         * {@link #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)} call.
7361         *
7362         * If expanded by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)},
7363         * will be reset upon layout to prevent initial prefetches (often large, since they're
7364         * proportional to expected child count) from expanding cache permanently.
7365         */
7366        int mPrefetchMaxCountObserved;
7367
7368        /**
7369         * If true, mPrefetchMaxCountObserved is only valid until next layout, and should be reset.
7370         */
7371        boolean mPrefetchMaxObservedInInitialPrefetch;
7372
7373        /**
7374         * These measure specs might be the measure specs that were passed into RecyclerView's
7375         * onMeasure method OR fake measure specs created by the RecyclerView.
7376         * For example, when a layout is run, RecyclerView always sets these specs to be
7377         * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
7378         * <p>
7379         * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
7380         * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
7381         * corrupt values. Older platforms have no responsibility to provide a size if they set
7382         * mode to unspecified.
7383         */
7384        private int mWidthMode, mHeightMode;
7385        private int mWidth, mHeight;
7386
7387
7388        /**
7389         * Interface for LayoutManagers to request items to be prefetched, based on position, with
7390         * specified distance from viewport, which indicates priority.
7391         *
7392         * @see LayoutManager#collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
7393         * @see LayoutManager#collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
7394         */
7395        public interface LayoutPrefetchRegistry {
7396            /**
7397             * Requests an an item to be prefetched, based on position, with a specified distance,
7398             * indicating priority.
7399             *
7400             * @param layoutPosition Position of the item to prefetch.
7401             * @param pixelDistance Distance from the current viewport to the bounds of the item,
7402             *                      must be non-negative.
7403             */
7404            void addPosition(int layoutPosition, int pixelDistance);
7405        }
7406
7407        void setRecyclerView(RecyclerView recyclerView) {
7408            if (recyclerView == null) {
7409                mRecyclerView = null;
7410                mChildHelper = null;
7411                mWidth = 0;
7412                mHeight = 0;
7413            } else {
7414                mRecyclerView = recyclerView;
7415                mChildHelper = recyclerView.mChildHelper;
7416                mWidth = recyclerView.getWidth();
7417                mHeight = recyclerView.getHeight();
7418            }
7419            mWidthMode = MeasureSpec.EXACTLY;
7420            mHeightMode = MeasureSpec.EXACTLY;
7421        }
7422
7423        void setMeasureSpecs(int wSpec, int hSpec) {
7424            mWidth = MeasureSpec.getSize(wSpec);
7425            mWidthMode = MeasureSpec.getMode(wSpec);
7426            if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
7427                mWidth = 0;
7428            }
7429
7430            mHeight = MeasureSpec.getSize(hSpec);
7431            mHeightMode = MeasureSpec.getMode(hSpec);
7432            if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
7433                mHeight = 0;
7434            }
7435        }
7436
7437        /**
7438         * Called after a layout is calculated during a measure pass when using auto-measure.
7439         * <p>
7440         * It simply traverses all children to calculate a bounding box then calls
7441         * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
7442         * if they need to handle the bounding box differently.
7443         * <p>
7444         * For example, GridLayoutManager override that method to ensure that even if a column is
7445         * empty, the GridLayoutManager still measures wide enough to include it.
7446         *
7447         * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
7448         * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
7449         */
7450        void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
7451            final int count = getChildCount();
7452            if (count == 0) {
7453                mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
7454                return;
7455            }
7456            int minX = Integer.MAX_VALUE;
7457            int minY = Integer.MAX_VALUE;
7458            int maxX = Integer.MIN_VALUE;
7459            int maxY = Integer.MIN_VALUE;
7460
7461            for (int i = 0; i < count; i++) {
7462                View child = getChildAt(i);
7463                final Rect bounds = mRecyclerView.mTempRect;
7464                getDecoratedBoundsWithMargins(child, bounds);
7465                if (bounds.left < minX) {
7466                    minX = bounds.left;
7467                }
7468                if (bounds.right > maxX) {
7469                    maxX = bounds.right;
7470                }
7471                if (bounds.top < minY) {
7472                    minY = bounds.top;
7473                }
7474                if (bounds.bottom > maxY) {
7475                    maxY = bounds.bottom;
7476                }
7477            }
7478            mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
7479            setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
7480        }
7481
7482        /**
7483         * Sets the measured dimensions from the given bounding box of the children and the
7484         * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
7485         * only called if a LayoutManager returns <code>true</code> from
7486         * {@link #isAutoMeasureEnabled()} and it is called after the RecyclerView calls
7487         * {@link LayoutManager#onLayoutChildren(Recycler, State)} in the execution of
7488         * {@link RecyclerView#onMeasure(int, int)}.
7489         * <p>
7490         * This method must call {@link #setMeasuredDimension(int, int)}.
7491         * <p>
7492         * The default implementation adds the RecyclerView's padding to the given bounding box
7493         * then caps the value to be within the given measurement specs.
7494         *
7495         * @param childrenBounds The bounding box of all children
7496         * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
7497         * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
7498         *
7499         * @see #isAutoMeasureEnabled()
7500         * @see #setMeasuredDimension(int, int)
7501         */
7502        public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
7503            int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
7504            int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
7505            int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
7506            int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
7507            setMeasuredDimension(width, height);
7508        }
7509
7510        /**
7511         * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
7512         */
7513        public void requestLayout() {
7514            if (mRecyclerView != null) {
7515                mRecyclerView.requestLayout();
7516            }
7517        }
7518
7519        /**
7520         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7521         * {@link IllegalStateException} if it <b>is not</b>.
7522         *
7523         * @param message The message for the exception. Can be null.
7524         * @see #assertNotInLayoutOrScroll(String)
7525         */
7526        public void assertInLayoutOrScroll(String message) {
7527            if (mRecyclerView != null) {
7528                mRecyclerView.assertInLayoutOrScroll(message);
7529            }
7530        }
7531
7532        /**
7533         * Chooses a size from the given specs and parameters that is closest to the desired size
7534         * and also complies with the spec.
7535         *
7536         * @param spec The measureSpec
7537         * @param desired The preferred measurement
7538         * @param min The minimum value
7539         *
7540         * @return A size that fits to the given specs
7541         */
7542        public static int chooseSize(int spec, int desired, int min) {
7543            final int mode = View.MeasureSpec.getMode(spec);
7544            final int size = View.MeasureSpec.getSize(spec);
7545            switch (mode) {
7546                case View.MeasureSpec.EXACTLY:
7547                    return size;
7548                case View.MeasureSpec.AT_MOST:
7549                    return Math.min(size, Math.max(desired, min));
7550                case View.MeasureSpec.UNSPECIFIED:
7551                default:
7552                    return Math.max(desired, min);
7553            }
7554        }
7555
7556        /**
7557         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7558         * {@link IllegalStateException} if it <b>is</b>.
7559         *
7560         * @param message The message for the exception. Can be null.
7561         * @see #assertInLayoutOrScroll(String)
7562         */
7563        public void assertNotInLayoutOrScroll(String message) {
7564            if (mRecyclerView != null) {
7565                mRecyclerView.assertNotInLayoutOrScroll(message);
7566            }
7567        }
7568
7569        /**
7570         * Defines whether the measuring pass of layout should use the AutoMeasure mechanism of
7571         * {@link RecyclerView} or if it should be done by the LayoutManager's implementation of
7572         * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
7573         *
7574         * @param enabled <code>True</code> if layout measurement should be done by the
7575         *                RecyclerView, <code>false</code> if it should be done by this
7576         *                LayoutManager.
7577         *
7578         * @see #isAutoMeasureEnabled()
7579         *
7580         * @deprecated Implementors of LayoutManager should define whether or not it uses
7581         *             AutoMeasure by overriding {@link #isAutoMeasureEnabled()}.
7582         */
7583        @Deprecated
7584        public void setAutoMeasureEnabled(boolean enabled) {
7585            mAutoMeasure = enabled;
7586        }
7587
7588        /**
7589         * Returns whether the measuring pass of layout should use the AutoMeasure mechanism of
7590         * {@link RecyclerView} or if it should be done by the LayoutManager's implementation of
7591         * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
7592         * <p>
7593         * This method returns false by default (it actually returns the value passed to the
7594         * deprecated {@link #setAutoMeasureEnabled(boolean)}) and should be overridden to return
7595         * true if a LayoutManager wants to be auto measured by the RecyclerView.
7596         * <p>
7597         * If this method is overridden to return true,
7598         * {@link LayoutManager#onMeasure(Recycler, State, int, int)} should not be overridden.
7599         * <p>
7600         * AutoMeasure is a RecyclerView mechanism that handles the measuring pass of layout in a
7601         * simple and contract satisfying way, including the wrapping of children laid out by
7602         * LayoutManager. Simply put, it handles wrapping children by calling
7603         * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a call to
7604         * {@link RecyclerView#onMeasure(int, int)}, and then calculating desired dimensions based
7605         * on children's dimensions and positions. It does this while supporting all existing
7606         * animation capabilities of the RecyclerView.
7607         * <p>
7608         * More specifically:
7609         * <ol>
7610         * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided measure
7611         * specs both have a mode of {@link View.MeasureSpec#EXACTLY}, RecyclerView will set its
7612         * measured dimensions accordingly and return, allowing layout to continue as normal
7613         * (Actually, RecyclerView will call
7614         * {@link LayoutManager#onMeasure(Recycler, State, int, int)} for backwards compatibility
7615         * reasons but it should not be overridden if AutoMeasure is being used).</li>
7616         * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
7617         * layout process. It will first process all pending Adapter updates and
7618         * then decide whether to run a predictive layout. If it decides to do so, it will first
7619         * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
7620         * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
7621         * return the width and height of the RecyclerView as of the last layout calculation.
7622         * <p>
7623         * After handling the predictive case, RecyclerView will call
7624         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7625         * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
7626         * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
7627         * {@link #getWidth()} and {@link #getWidthMode()}.</li>
7628         * <li>After the layout calculation, RecyclerView sets the measured width & height by
7629         * calculating the bounding box for the children (+ RecyclerView's padding). The
7630         * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
7631         * different values. For instance, GridLayoutManager overrides this value to handle the case
7632         * where if it is vertical and has 3 columns but only 2 items, it should still measure its
7633         * width to fit 3 items, not 2.</li>
7634         * <li>Any following calls to {@link RecyclerView#onMeasure(int, int)} will run
7635         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7636         * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
7637         * take care of which views are actually added / removed / moved / changed for animations so
7638         * that the LayoutManager should not worry about them and handle each
7639         * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.</li>
7640         * <li>When measure is complete and RecyclerView's
7641         * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
7642         * whether it already did layout calculations during the measure pass and if so, it re-uses
7643         * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
7644         * if the last measure spec was different from the final dimensions or adapter contents
7645         * have changed between the measure call and the layout call.</li>
7646         * <li>Finally, animations are calculated and run as usual.</li>
7647         * </ol>
7648         *
7649         * @return <code>True</code> if the measuring pass of layout should use the AutoMeasure
7650         * mechanism of {@link RecyclerView} or <code>False</code> if it should be done by the
7651         * LayoutManager's implementation of
7652         * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
7653         *
7654         * @see #setMeasuredDimension(Rect, int, int)
7655         * @see #onMeasure(Recycler, State, int, int)
7656         */
7657        public boolean isAutoMeasureEnabled() {
7658            return mAutoMeasure;
7659        }
7660
7661        /**
7662         * Returns whether this LayoutManager supports "predictive item animations".
7663         * <p>
7664         * "Predictive item animations" are automatically created animations that show
7665         * where items came from, and where they are going to, as items are added, removed,
7666         * or moved within a layout.
7667         * <p>
7668         * A LayoutManager wishing to support predictive item animations must override this
7669         * method to return true (the default implementation returns false) and must obey certain
7670         * behavioral contracts outlined in {@link #onLayoutChildren(Recycler, State)}.
7671         * <p>
7672         * Whether item animations actually occur in a RecyclerView is actually determined by both
7673         * the return value from this method and the
7674         * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
7675         * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
7676         * method returns false, then only "simple item animations" will be enabled in the
7677         * RecyclerView, in which views whose position are changing are simply faded in/out. If the
7678         * RecyclerView has a non-null ItemAnimator and this method returns true, then predictive
7679         * item animations will be enabled in the RecyclerView.
7680         *
7681         * @return true if this LayoutManager supports predictive item animations, false otherwise.
7682         */
7683        public boolean supportsPredictiveItemAnimations() {
7684            return false;
7685        }
7686
7687        /**
7688         * Sets whether the LayoutManager should be queried for views outside of
7689         * its viewport while the UI thread is idle between frames.
7690         *
7691         * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
7692         * view system traversals on devices running API 21 or greater. Default value is true.</p>
7693         *
7694         * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
7695         * to RenderThread and the starting up its next frame at the next VSync pulse. By
7696         * prefetching out of window views in this time period, delays from inflation and view
7697         * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
7698         *
7699         * <p>While prefetch is enabled, it will have the side effect of expanding the effective
7700         * size of the View cache to hold prefetched views.</p>
7701         *
7702         * @param enabled <code>True</code> if items should be prefetched in between traversals.
7703         *
7704         * @see #isItemPrefetchEnabled()
7705         */
7706        public final void setItemPrefetchEnabled(boolean enabled) {
7707            if (enabled != mItemPrefetchEnabled) {
7708                mItemPrefetchEnabled = enabled;
7709                mPrefetchMaxCountObserved = 0;
7710                if (mRecyclerView != null) {
7711                    mRecyclerView.mRecycler.updateViewCacheSize();
7712                }
7713            }
7714        }
7715
7716        /**
7717         * Sets whether the LayoutManager should be queried for views outside of
7718         * its viewport while the UI thread is idle between frames.
7719         *
7720         * @see #setItemPrefetchEnabled(boolean)
7721         *
7722         * @return true if item prefetch is enabled, false otherwise
7723         */
7724        public final boolean isItemPrefetchEnabled() {
7725            return mItemPrefetchEnabled;
7726        }
7727
7728        /**
7729         * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
7730         *
7731         * <p>If item prefetch is enabled, this method is called in between traversals to gather
7732         * which positions the LayoutManager will soon need, given upcoming movement in subsequent
7733         * traversals.</p>
7734         *
7735         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7736         * each item to be prepared, and these positions will have their ViewHolders created and
7737         * bound, if there is sufficient time available, in advance of being needed by a
7738         * scroll or layout.</p>
7739         *
7740         * @param dx X movement component.
7741         * @param dy Y movement component.
7742         * @param state State of RecyclerView
7743         * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7744         *
7745         * @see #isItemPrefetchEnabled()
7746         * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
7747         */
7748        public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
7749                LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7750
7751        /**
7752         * Gather all positions from the LayoutManager to be prefetched in preperation for its
7753         * RecyclerView to come on screen, due to the movement of another, containing RecyclerView.
7754         *
7755         * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
7756         *
7757         * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
7758         * LayoutManager, this method is called in between draw traversals to gather
7759         * which positions this LayoutManager will first need, once it appears on the screen.</p>
7760         *
7761         * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
7762         * vertically scrolling LayoutManager, this method would be called when the horizontal list
7763         * is about to come onscreen.</p>
7764         *
7765         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7766         * each item to be prepared, and these positions will have their ViewHolders created and
7767         * bound, if there is sufficient time available, in advance of being needed by a
7768         * scroll or layout.</p>
7769         *
7770         * @param adapterItemCount number of items in the associated adapter.
7771         * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7772         *
7773         * @see #isItemPrefetchEnabled()
7774         * @see #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
7775         */
7776        public void collectInitialPrefetchPositions(int adapterItemCount,
7777                LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7778
7779        void dispatchAttachedToWindow(RecyclerView view) {
7780            mIsAttachedToWindow = true;
7781            onAttachedToWindow(view);
7782        }
7783
7784        void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
7785            mIsAttachedToWindow = false;
7786            onDetachedFromWindow(view, recycler);
7787        }
7788
7789        /**
7790         * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
7791         * to a window.
7792         *
7793         * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
7794         * is attached to window.
7795         */
7796        public boolean isAttachedToWindow() {
7797            return mIsAttachedToWindow;
7798        }
7799
7800        /**
7801         * Causes the Runnable to execute on the next animation time step.
7802         * The runnable will be run on the user interface thread.
7803         * <p>
7804         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7805         *
7806         * @param action The Runnable that will be executed.
7807         *
7808         * @see #removeCallbacks
7809         */
7810        public void postOnAnimation(Runnable action) {
7811            if (mRecyclerView != null) {
7812                ViewCompat.postOnAnimation(mRecyclerView, action);
7813            }
7814        }
7815
7816        /**
7817         * Removes the specified Runnable from the message queue.
7818         * <p>
7819         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7820         *
7821         * @param action The Runnable to remove from the message handling queue
7822         *
7823         * @return true if RecyclerView could ask the Handler to remove the Runnable,
7824         *         false otherwise. When the returned value is true, the Runnable
7825         *         may or may not have been actually removed from the message queue
7826         *         (for instance, if the Runnable was not in the queue already.)
7827         *
7828         * @see #postOnAnimation
7829         */
7830        public boolean removeCallbacks(Runnable action) {
7831            if (mRecyclerView != null) {
7832                return mRecyclerView.removeCallbacks(action);
7833            }
7834            return false;
7835        }
7836        /**
7837         * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
7838         * is attached to a window.
7839         * <p>
7840         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7841         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7842         * not requested on the RecyclerView while it was detached.
7843         * <p>
7844         * Subclass implementations should always call through to the superclass implementation.
7845         *
7846         * @param view The RecyclerView this LayoutManager is bound to
7847         *
7848         * @see #onDetachedFromWindow(RecyclerView, Recycler)
7849         */
7850        @CallSuper
7851        public void onAttachedToWindow(RecyclerView view) {
7852        }
7853
7854        /**
7855         * @deprecated
7856         * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
7857         */
7858        @Deprecated
7859        public void onDetachedFromWindow(RecyclerView view) {
7860
7861        }
7862
7863        /**
7864         * Called when this LayoutManager is detached from its parent RecyclerView or when
7865         * its parent RecyclerView is detached from its window.
7866         * <p>
7867         * LayoutManager should clear all of its View references as another LayoutManager might be
7868         * assigned to the RecyclerView.
7869         * <p>
7870         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7871         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7872         * not requested on the RecyclerView while it was detached.
7873         * <p>
7874         * If your LayoutManager has View references that it cleans in on-detach, it should also
7875         * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
7876         * RecyclerView is re-attached.
7877         * <p>
7878         * Subclass implementations should always call through to the superclass implementation.
7879         *
7880         * @param view The RecyclerView this LayoutManager is bound to
7881         * @param recycler The recycler to use if you prefer to recycle your children instead of
7882         *                 keeping them around.
7883         *
7884         * @see #onAttachedToWindow(RecyclerView)
7885         */
7886        @CallSuper
7887        public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
7888            onDetachedFromWindow(view);
7889        }
7890
7891        /**
7892         * Check if the RecyclerView is configured to clip child views to its padding.
7893         *
7894         * @return true if this RecyclerView clips children to its padding, false otherwise
7895         */
7896        public boolean getClipToPadding() {
7897            return mRecyclerView != null && mRecyclerView.mClipToPadding;
7898        }
7899
7900        /**
7901         * Lay out all relevant child views from the given adapter.
7902         *
7903         * The LayoutManager is in charge of the behavior of item animations. By default,
7904         * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
7905         * item animations are enabled. This means that add/remove operations on the
7906         * adapter will result in animations to add new or appearing items, removed or
7907         * disappearing items, and moved items. If a LayoutManager returns false from
7908         * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
7909         * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
7910         * RecyclerView will have enough information to run those animations in a simple
7911         * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
7912         * simply fade views in and out, whether they are actually added/removed or whether
7913         * they are moved on or off the screen due to other add/remove operations.
7914         *
7915         * <p>A LayoutManager wanting a better item animation experience, where items can be
7916         * animated onto and off of the screen according to where the items exist when they
7917         * are not on screen, then the LayoutManager should return true from
7918         * {@link #supportsPredictiveItemAnimations()} and add additional logic to
7919         * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
7920         * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
7921         * once as a "pre" layout step to determine where items would have been prior to
7922         * a real layout, and again to do the "real" layout. In the pre-layout phase,
7923         * items will remember their pre-layout positions to allow them to be laid out
7924         * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
7925         * be returned from the scrap to help determine correct placement of other items.
7926         * These removed items should not be added to the child list, but should be used
7927         * to help calculate correct positioning of other views, including views that
7928         * were not previously onscreen (referred to as APPEARING views), but whose
7929         * pre-layout offscreen position can be determined given the extra
7930         * information about the pre-layout removed views.</p>
7931         *
7932         * <p>The second layout pass is the real layout in which only non-removed views
7933         * will be used. The only additional requirement during this pass is, if
7934         * {@link #supportsPredictiveItemAnimations()} returns true, to note which
7935         * views exist in the child list prior to layout and which are not there after
7936         * layout (referred to as DISAPPEARING views), and to position/layout those views
7937         * appropriately, without regard to the actual bounds of the RecyclerView. This allows
7938         * the animation system to know the location to which to animate these disappearing
7939         * views.</p>
7940         *
7941         * <p>The default LayoutManager implementations for RecyclerView handle all of these
7942         * requirements for animations already. Clients of RecyclerView can either use one
7943         * of these layout managers directly or look at their implementations of
7944         * onLayoutChildren() to see how they account for the APPEARING and
7945         * DISAPPEARING views.</p>
7946         *
7947         * @param recycler         Recycler to use for fetching potentially cached views for a
7948         *                         position
7949         * @param state            Transient state of RecyclerView
7950         */
7951        public void onLayoutChildren(Recycler recycler, State state) {
7952            Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
7953        }
7954
7955        /**
7956         * Called after a full layout calculation is finished. The layout calculation may include
7957         * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
7958         * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
7959         * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
7960         * <p>
7961         * This is a good place for the LayoutManager to do some cleanup like pending scroll
7962         * position, saved state etc.
7963         *
7964         * @param state Transient state of RecyclerView
7965         */
7966        public void onLayoutCompleted(State state) {
7967        }
7968
7969        /**
7970         * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
7971         *
7972         * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
7973         * to store extra information specific to the layout. Client code should subclass
7974         * {@link RecyclerView.LayoutParams} for this purpose.</p>
7975         *
7976         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7977         * you must also override
7978         * {@link #checkLayoutParams(LayoutParams)},
7979         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7980         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7981         *
7982         * @return A new LayoutParams for a child view
7983         */
7984        public abstract LayoutParams generateDefaultLayoutParams();
7985
7986        /**
7987         * Determines the validity of the supplied LayoutParams object.
7988         *
7989         * <p>This should check to make sure that the object is of the correct type
7990         * and all values are within acceptable ranges. The default implementation
7991         * returns <code>true</code> for non-null params.</p>
7992         *
7993         * @param lp LayoutParams object to check
7994         * @return true if this LayoutParams object is valid, false otherwise
7995         */
7996        public boolean checkLayoutParams(LayoutParams lp) {
7997            return lp != null;
7998        }
7999
8000        /**
8001         * Create a LayoutParams object suitable for this LayoutManager, copying relevant
8002         * values from the supplied LayoutParams object if possible.
8003         *
8004         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
8005         * you must also override
8006         * {@link #checkLayoutParams(LayoutParams)},
8007         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
8008         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
8009         *
8010         * @param lp Source LayoutParams object to copy values from
8011         * @return a new LayoutParams object
8012         */
8013        public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
8014            if (lp instanceof LayoutParams) {
8015                return new LayoutParams((LayoutParams) lp);
8016            } else if (lp instanceof MarginLayoutParams) {
8017                return new LayoutParams((MarginLayoutParams) lp);
8018            } else {
8019                return new LayoutParams(lp);
8020            }
8021        }
8022
8023        /**
8024         * Create a LayoutParams object suitable for this LayoutManager from
8025         * an inflated layout resource.
8026         *
8027         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
8028         * you must also override
8029         * {@link #checkLayoutParams(LayoutParams)},
8030         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
8031         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
8032         *
8033         * @param c Context for obtaining styled attributes
8034         * @param attrs AttributeSet describing the supplied arguments
8035         * @return a new LayoutParams object
8036         */
8037        public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
8038            return new LayoutParams(c, attrs);
8039        }
8040
8041        /**
8042         * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
8043         * The default implementation does nothing and returns 0.
8044         *
8045         * @param dx            distance to scroll by in pixels. X increases as scroll position
8046         *                      approaches the right.
8047         * @param recycler      Recycler to use for fetching potentially cached views for a
8048         *                      position
8049         * @param state         Transient state of RecyclerView
8050         * @return The actual distance scrolled. The return value will be negative if dx was
8051         * negative and scrolling proceeeded in that direction.
8052         * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
8053         */
8054        public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
8055            return 0;
8056        }
8057
8058        /**
8059         * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
8060         * The default implementation does nothing and returns 0.
8061         *
8062         * @param dy            distance to scroll in pixels. Y increases as scroll position
8063         *                      approaches the bottom.
8064         * @param recycler      Recycler to use for fetching potentially cached views for a
8065         *                      position
8066         * @param state         Transient state of RecyclerView
8067         * @return The actual distance scrolled. The return value will be negative if dy was
8068         * negative and scrolling proceeeded in that direction.
8069         * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
8070         */
8071        public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
8072            return 0;
8073        }
8074
8075        /**
8076         * Query if horizontal scrolling is currently supported. The default implementation
8077         * returns false.
8078         *
8079         * @return True if this LayoutManager can scroll the current contents horizontally
8080         */
8081        public boolean canScrollHorizontally() {
8082            return false;
8083        }
8084
8085        /**
8086         * Query if vertical scrolling is currently supported. The default implementation
8087         * returns false.
8088         *
8089         * @return True if this LayoutManager can scroll the current contents vertically
8090         */
8091        public boolean canScrollVertically() {
8092            return false;
8093        }
8094
8095        /**
8096         * Scroll to the specified adapter position.
8097         *
8098         * Actual position of the item on the screen depends on the LayoutManager implementation.
8099         * @param position Scroll to this adapter position.
8100         */
8101        public void scrollToPosition(int position) {
8102            if (DEBUG) {
8103                Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
8104            }
8105        }
8106
8107        /**
8108         * <p>Smooth scroll to the specified adapter position.</p>
8109         * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
8110         * instance and call {@link #startSmoothScroll(SmoothScroller)}.
8111         * </p>
8112         * @param recyclerView The RecyclerView to which this layout manager is attached
8113         * @param state    Current State of RecyclerView
8114         * @param position Scroll to this adapter position.
8115         */
8116        public void smoothScrollToPosition(RecyclerView recyclerView, State state,
8117                int position) {
8118            Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
8119        }
8120
8121        /**
8122         * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
8123         * <p>Calling this method will cancel any previous smooth scroll request.</p>
8124         * @param smoothScroller Instance which defines how smooth scroll should be animated
8125         */
8126        public void startSmoothScroll(SmoothScroller smoothScroller) {
8127            if (mSmoothScroller != null && smoothScroller != mSmoothScroller
8128                    && mSmoothScroller.isRunning()) {
8129                mSmoothScroller.stop();
8130            }
8131            mSmoothScroller = smoothScroller;
8132            mSmoothScroller.start(mRecyclerView, this);
8133        }
8134
8135        /**
8136         * @return true if RecyclerView is currently in the state of smooth scrolling.
8137         */
8138        public boolean isSmoothScrolling() {
8139            return mSmoothScroller != null && mSmoothScroller.isRunning();
8140        }
8141
8142
8143        /**
8144         * Returns the resolved layout direction for this RecyclerView.
8145         *
8146         * @return {@link androidx.core.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout
8147         * direction is RTL or returns
8148         * {@link androidx.core.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction
8149         * is not RTL.
8150         */
8151        public int getLayoutDirection() {
8152            return ViewCompat.getLayoutDirection(mRecyclerView);
8153        }
8154
8155        /**
8156         * Ends all animations on the view created by the {@link ItemAnimator}.
8157         *
8158         * @param view The View for which the animations should be ended.
8159         * @see RecyclerView.ItemAnimator#endAnimations()
8160         */
8161        public void endAnimation(View view) {
8162            if (mRecyclerView.mItemAnimator != null) {
8163                mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
8164            }
8165        }
8166
8167        /**
8168         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
8169         * to the layout that is known to be going away, either because it has been
8170         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
8171         * visible portion of the container but is being laid out in order to inform RecyclerView
8172         * in how to animate the item out of view.
8173         * <p>
8174         * Views added via this method are going to be invisible to LayoutManager after the
8175         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
8176         * or won't be included in {@link #getChildCount()} method.
8177         *
8178         * @param child View to add and then remove with animation.
8179         */
8180        public void addDisappearingView(View child) {
8181            addDisappearingView(child, -1);
8182        }
8183
8184        /**
8185         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
8186         * to the layout that is known to be going away, either because it has been
8187         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
8188         * visible portion of the container but is being laid out in order to inform RecyclerView
8189         * in how to animate the item out of view.
8190         * <p>
8191         * Views added via this method are going to be invisible to LayoutManager after the
8192         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
8193         * or won't be included in {@link #getChildCount()} method.
8194         *
8195         * @param child View to add and then remove with animation.
8196         * @param index Index of the view.
8197         */
8198        public void addDisappearingView(View child, int index) {
8199            addViewInt(child, index, true);
8200        }
8201
8202        /**
8203         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
8204         * use this method to add views obtained from a {@link Recycler} using
8205         * {@link Recycler#getViewForPosition(int)}.
8206         *
8207         * @param child View to add
8208         */
8209        public void addView(View child) {
8210            addView(child, -1);
8211        }
8212
8213        /**
8214         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
8215         * use this method to add views obtained from a {@link Recycler} using
8216         * {@link Recycler#getViewForPosition(int)}.
8217         *
8218         * @param child View to add
8219         * @param index Index to add child at
8220         */
8221        public void addView(View child, int index) {
8222            addViewInt(child, index, false);
8223        }
8224
8225        private void addViewInt(View child, int index, boolean disappearing) {
8226            final ViewHolder holder = getChildViewHolderInt(child);
8227            if (disappearing || holder.isRemoved()) {
8228                // these views will be hidden at the end of the layout pass.
8229                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
8230            } else {
8231                // This may look like unnecessary but may happen if layout manager supports
8232                // predictive layouts and adapter removed then re-added the same item.
8233                // In this case, added version will be visible in the post layout (because add is
8234                // deferred) but RV will still bind it to the same View.
8235                // So if a View re-appears in post layout pass, remove it from disappearing list.
8236                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
8237            }
8238            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8239            if (holder.wasReturnedFromScrap() || holder.isScrap()) {
8240                if (holder.isScrap()) {
8241                    holder.unScrap();
8242                } else {
8243                    holder.clearReturnedFromScrapFlag();
8244                }
8245                mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
8246                if (DISPATCH_TEMP_DETACH) {
8247                    ViewCompat.dispatchFinishTemporaryDetach(child);
8248                }
8249            } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
8250                // ensure in correct position
8251                int currentIndex = mChildHelper.indexOfChild(child);
8252                if (index == -1) {
8253                    index = mChildHelper.getChildCount();
8254                }
8255                if (currentIndex == -1) {
8256                    throw new IllegalStateException("Added View has RecyclerView as parent but"
8257                            + " view is not a real child. Unfiltered index:"
8258                            + mRecyclerView.indexOfChild(child) + mRecyclerView.exceptionLabel());
8259                }
8260                if (currentIndex != index) {
8261                    mRecyclerView.mLayout.moveView(currentIndex, index);
8262                }
8263            } else {
8264                mChildHelper.addView(child, index, false);
8265                lp.mInsetsDirty = true;
8266                if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
8267                    mSmoothScroller.onChildAttachedToWindow(child);
8268                }
8269            }
8270            if (lp.mPendingInvalidate) {
8271                if (DEBUG) {
8272                    Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
8273                }
8274                holder.itemView.invalidate();
8275                lp.mPendingInvalidate = false;
8276            }
8277        }
8278
8279        /**
8280         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
8281         * use this method to completely remove a child view that is no longer needed.
8282         * LayoutManagers should strongly consider recycling removed views using
8283         * {@link Recycler#recycleView(android.view.View)}.
8284         *
8285         * @param child View to remove
8286         */
8287        public void removeView(View child) {
8288            mChildHelper.removeView(child);
8289        }
8290
8291        /**
8292         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
8293         * use this method to completely remove a child view that is no longer needed.
8294         * LayoutManagers should strongly consider recycling removed views using
8295         * {@link Recycler#recycleView(android.view.View)}.
8296         *
8297         * @param index Index of the child view to remove
8298         */
8299        public void removeViewAt(int index) {
8300            final View child = getChildAt(index);
8301            if (child != null) {
8302                mChildHelper.removeViewAt(index);
8303            }
8304        }
8305
8306        /**
8307         * Remove all views from the currently attached RecyclerView. This will not recycle
8308         * any of the affected views; the LayoutManager is responsible for doing so if desired.
8309         */
8310        public void removeAllViews() {
8311            // Only remove non-animating views
8312            final int childCount = getChildCount();
8313            for (int i = childCount - 1; i >= 0; i--) {
8314                mChildHelper.removeViewAt(i);
8315            }
8316        }
8317
8318        /**
8319         * Returns offset of the RecyclerView's text baseline from the its top boundary.
8320         *
8321         * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
8322         * there is no baseline.
8323         */
8324        public int getBaseline() {
8325            return -1;
8326        }
8327
8328        /**
8329         * Returns the adapter position of the item represented by the given View. This does not
8330         * contain any adapter changes that might have happened after the last layout.
8331         *
8332         * @param view The view to query
8333         * @return The adapter position of the item which is rendered by this View.
8334         */
8335        public int getPosition(View view) {
8336            return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
8337        }
8338
8339        /**
8340         * Returns the View type defined by the adapter.
8341         *
8342         * @param view The view to query
8343         * @return The type of the view assigned by the adapter.
8344         */
8345        public int getItemViewType(View view) {
8346            return getChildViewHolderInt(view).getItemViewType();
8347        }
8348
8349        /**
8350         * Traverses the ancestors of the given view and returns the item view that contains it
8351         * and also a direct child of the LayoutManager.
8352         * <p>
8353         * Note that this method may return null if the view is a child of the RecyclerView but
8354         * not a child of the LayoutManager (e.g. running a disappear animation).
8355         *
8356         * @param view The view that is a descendant of the LayoutManager.
8357         *
8358         * @return The direct child of the LayoutManager which contains the given view or null if
8359         * the provided view is not a descendant of this LayoutManager.
8360         *
8361         * @see RecyclerView#getChildViewHolder(View)
8362         * @see RecyclerView#findContainingViewHolder(View)
8363         */
8364        @Nullable
8365        public View findContainingItemView(View view) {
8366            if (mRecyclerView == null) {
8367                return null;
8368            }
8369            View found = mRecyclerView.findContainingItemView(view);
8370            if (found == null) {
8371                return null;
8372            }
8373            if (mChildHelper.isHidden(found)) {
8374                return null;
8375            }
8376            return found;
8377        }
8378
8379        /**
8380         * Finds the view which represents the given adapter position.
8381         * <p>
8382         * This method traverses each child since it has no information about child order.
8383         * Override this method to improve performance if your LayoutManager keeps data about
8384         * child views.
8385         * <p>
8386         * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
8387         *
8388         * @param position Position of the item in adapter
8389         * @return The child view that represents the given position or null if the position is not
8390         * laid out
8391         */
8392        public View findViewByPosition(int position) {
8393            final int childCount = getChildCount();
8394            for (int i = 0; i < childCount; i++) {
8395                View child = getChildAt(i);
8396                ViewHolder vh = getChildViewHolderInt(child);
8397                if (vh == null) {
8398                    continue;
8399                }
8400                if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
8401                        && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
8402                    return child;
8403                }
8404            }
8405            return null;
8406        }
8407
8408        /**
8409         * Temporarily detach a child view.
8410         *
8411         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
8412         * views currently attached to the RecyclerView. Generally LayoutManager implementations
8413         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
8414         * so that the detached view may be rebound and reused.</p>
8415         *
8416         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
8417         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
8418         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
8419         * before the LayoutManager entry point method called by RecyclerView returns.</p>
8420         *
8421         * @param child Child to detach
8422         */
8423        public void detachView(View child) {
8424            final int ind = mChildHelper.indexOfChild(child);
8425            if (ind >= 0) {
8426                detachViewInternal(ind, child);
8427            }
8428        }
8429
8430        /**
8431         * Temporarily detach a child view.
8432         *
8433         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
8434         * views currently attached to the RecyclerView. Generally LayoutManager implementations
8435         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
8436         * so that the detached view may be rebound and reused.</p>
8437         *
8438         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
8439         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
8440         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
8441         * before the LayoutManager entry point method called by RecyclerView returns.</p>
8442         *
8443         * @param index Index of the child to detach
8444         */
8445        public void detachViewAt(int index) {
8446            detachViewInternal(index, getChildAt(index));
8447        }
8448
8449        private void detachViewInternal(int index, View view) {
8450            if (DISPATCH_TEMP_DETACH) {
8451                ViewCompat.dispatchStartTemporaryDetach(view);
8452            }
8453            mChildHelper.detachViewFromParent(index);
8454        }
8455
8456        /**
8457         * Reattach a previously {@link #detachView(android.view.View) detached} view.
8458         * This method should not be used to reattach views that were previously
8459         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8460         *
8461         * @param child Child to reattach
8462         * @param index Intended child index for child
8463         * @param lp LayoutParams for child
8464         */
8465        public void attachView(View child, int index, LayoutParams lp) {
8466            ViewHolder vh = getChildViewHolderInt(child);
8467            if (vh.isRemoved()) {
8468                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
8469            } else {
8470                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
8471            }
8472            mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
8473            if (DISPATCH_TEMP_DETACH)  {
8474                ViewCompat.dispatchFinishTemporaryDetach(child);
8475            }
8476        }
8477
8478        /**
8479         * Reattach a previously {@link #detachView(android.view.View) detached} view.
8480         * This method should not be used to reattach views that were previously
8481         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8482         *
8483         * @param child Child to reattach
8484         * @param index Intended child index for child
8485         */
8486        public void attachView(View child, int index) {
8487            attachView(child, index, (LayoutParams) child.getLayoutParams());
8488        }
8489
8490        /**
8491         * Reattach a previously {@link #detachView(android.view.View) detached} view.
8492         * This method should not be used to reattach views that were previously
8493         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8494         *
8495         * @param child Child to reattach
8496         */
8497        public void attachView(View child) {
8498            attachView(child, -1);
8499        }
8500
8501        /**
8502         * Finish removing a view that was previously temporarily
8503         * {@link #detachView(android.view.View) detached}.
8504         *
8505         * @param child Detached child to remove
8506         */
8507        public void removeDetachedView(View child) {
8508            mRecyclerView.removeDetachedView(child, false);
8509        }
8510
8511        /**
8512         * Moves a View from one position to another.
8513         *
8514         * @param fromIndex The View's initial index
8515         * @param toIndex The View's target index
8516         */
8517        public void moveView(int fromIndex, int toIndex) {
8518            View view = getChildAt(fromIndex);
8519            if (view == null) {
8520                throw new IllegalArgumentException("Cannot move a child from non-existing index:"
8521                        + fromIndex + mRecyclerView.toString());
8522            }
8523            detachViewAt(fromIndex);
8524            attachView(view, toIndex);
8525        }
8526
8527        /**
8528         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8529         *
8530         * <p>Scrapping a view allows it to be rebound and reused to show updated or
8531         * different data.</p>
8532         *
8533         * @param child Child to detach and scrap
8534         * @param recycler Recycler to deposit the new scrap view into
8535         */
8536        public void detachAndScrapView(View child, Recycler recycler) {
8537            int index = mChildHelper.indexOfChild(child);
8538            scrapOrRecycleView(recycler, index, child);
8539        }
8540
8541        /**
8542         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8543         *
8544         * <p>Scrapping a view allows it to be rebound and reused to show updated or
8545         * different data.</p>
8546         *
8547         * @param index Index of child to detach and scrap
8548         * @param recycler Recycler to deposit the new scrap view into
8549         */
8550        public void detachAndScrapViewAt(int index, Recycler recycler) {
8551            final View child = getChildAt(index);
8552            scrapOrRecycleView(recycler, index, child);
8553        }
8554
8555        /**
8556         * Remove a child view and recycle it using the given Recycler.
8557         *
8558         * @param child Child to remove and recycle
8559         * @param recycler Recycler to use to recycle child
8560         */
8561        public void removeAndRecycleView(View child, Recycler recycler) {
8562            removeView(child);
8563            recycler.recycleView(child);
8564        }
8565
8566        /**
8567         * Remove a child view and recycle it using the given Recycler.
8568         *
8569         * @param index Index of child to remove and recycle
8570         * @param recycler Recycler to use to recycle child
8571         */
8572        public void removeAndRecycleViewAt(int index, Recycler recycler) {
8573            final View view = getChildAt(index);
8574            removeViewAt(index);
8575            recycler.recycleView(view);
8576        }
8577
8578        /**
8579         * Return the current number of child views attached to the parent RecyclerView.
8580         * This does not include child views that were temporarily detached and/or scrapped.
8581         *
8582         * @return Number of attached children
8583         */
8584        public int getChildCount() {
8585            return mChildHelper != null ? mChildHelper.getChildCount() : 0;
8586        }
8587
8588        /**
8589         * Return the child view at the given index
8590         * @param index Index of child to return
8591         * @return Child view at index
8592         */
8593        public View getChildAt(int index) {
8594            return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
8595        }
8596
8597        /**
8598         * Return the width measurement spec mode that is currently relevant to the LayoutManager.
8599         *
8600         * <p>This value is set only if the LayoutManager opts into the AutoMeasure api via
8601         * {@link #setAutoMeasureEnabled(boolean)}.
8602         *
8603         * <p>When RecyclerView is running a layout, this value is always set to
8604         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8605         *
8606         * @return Width measure spec mode
8607         *
8608         * @see View.MeasureSpec#getMode(int)
8609         */
8610        public int getWidthMode() {
8611            return mWidthMode;
8612        }
8613
8614        /**
8615         * Return the height measurement spec mode that is currently relevant to the LayoutManager.
8616         *
8617         * <p>This value is set only if the LayoutManager opts into the AutoMeasure api via
8618         * {@link #setAutoMeasureEnabled(boolean)}.
8619         *
8620         * <p>When RecyclerView is running a layout, this value is always set to
8621         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8622         *
8623         * @return Height measure spec mode
8624         *
8625         * @see View.MeasureSpec#getMode(int)
8626         */
8627        public int getHeightMode() {
8628            return mHeightMode;
8629        }
8630
8631        /**
8632         * Returns the width that is currently relevant to the LayoutManager.
8633         *
8634         * <p>This value is usually equal to the laid out width of the {@link RecyclerView} but may
8635         * reflect the current {@link android.view.View.MeasureSpec} width if the
8636         * {@link LayoutManager} is using AutoMeasure and the RecyclerView is in the process of
8637         * measuring. The LayoutManager must always use this method to retrieve the width relevant
8638         * to it at any given time.
8639         *
8640         * @return Width in pixels
8641         */
8642        public int getWidth() {
8643            return mWidth;
8644        }
8645
8646        /**
8647         * Returns the height that is currently relevant to the LayoutManager.
8648         *
8649         * <p>This value is usually equal to the laid out height of the {@link RecyclerView} but may
8650         * reflect the current {@link android.view.View.MeasureSpec} height if the
8651         * {@link LayoutManager} is using AutoMeasure and the RecyclerView is in the process of
8652         * measuring. The LayoutManager must always use this method to retrieve the height relevant
8653         * to it at any given time.
8654         *
8655         * @return Height in pixels
8656         */
8657        public int getHeight() {
8658            return mHeight;
8659        }
8660
8661        /**
8662         * Return the left padding of the parent RecyclerView
8663         *
8664         * @return Padding in pixels
8665         */
8666        public int getPaddingLeft() {
8667            return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
8668        }
8669
8670        /**
8671         * Return the top padding of the parent RecyclerView
8672         *
8673         * @return Padding in pixels
8674         */
8675        public int getPaddingTop() {
8676            return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
8677        }
8678
8679        /**
8680         * Return the right padding of the parent RecyclerView
8681         *
8682         * @return Padding in pixels
8683         */
8684        public int getPaddingRight() {
8685            return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
8686        }
8687
8688        /**
8689         * Return the bottom padding of the parent RecyclerView
8690         *
8691         * @return Padding in pixels
8692         */
8693        public int getPaddingBottom() {
8694            return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
8695        }
8696
8697        /**
8698         * Return the start padding of the parent RecyclerView
8699         *
8700         * @return Padding in pixels
8701         */
8702        public int getPaddingStart() {
8703            return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;
8704        }
8705
8706        /**
8707         * Return the end padding of the parent RecyclerView
8708         *
8709         * @return Padding in pixels
8710         */
8711        public int getPaddingEnd() {
8712            return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;
8713        }
8714
8715        /**
8716         * Returns true if the RecyclerView this LayoutManager is bound to has focus.
8717         *
8718         * @return True if the RecyclerView has focus, false otherwise.
8719         * @see View#isFocused()
8720         */
8721        public boolean isFocused() {
8722            return mRecyclerView != null && mRecyclerView.isFocused();
8723        }
8724
8725        /**
8726         * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
8727         *
8728         * @return true if the RecyclerView has or contains focus
8729         * @see View#hasFocus()
8730         */
8731        public boolean hasFocus() {
8732            return mRecyclerView != null && mRecyclerView.hasFocus();
8733        }
8734
8735        /**
8736         * Returns the item View which has or contains focus.
8737         *
8738         * @return A direct child of RecyclerView which has focus or contains the focused child.
8739         */
8740        public View getFocusedChild() {
8741            if (mRecyclerView == null) {
8742                return null;
8743            }
8744            final View focused = mRecyclerView.getFocusedChild();
8745            if (focused == null || mChildHelper.isHidden(focused)) {
8746                return null;
8747            }
8748            return focused;
8749        }
8750
8751        /**
8752         * Returns the number of items in the adapter bound to the parent RecyclerView.
8753         * <p>
8754         * Note that this number is not necessarily equal to
8755         * {@link State#getItemCount() State#getItemCount()}. In methods where {@link State} is
8756         * available, you should use {@link State#getItemCount() State#getItemCount()} instead.
8757         * For more details, check the documentation for
8758         * {@link State#getItemCount() State#getItemCount()}.
8759         *
8760         * @return The number of items in the bound adapter
8761         * @see State#getItemCount()
8762         */
8763        public int getItemCount() {
8764            final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
8765            return a != null ? a.getItemCount() : 0;
8766        }
8767
8768        /**
8769         * Offset all child views attached to the parent RecyclerView by dx pixels along
8770         * the horizontal axis.
8771         *
8772         * @param dx Pixels to offset by
8773         */
8774        public void offsetChildrenHorizontal(int dx) {
8775            if (mRecyclerView != null) {
8776                mRecyclerView.offsetChildrenHorizontal(dx);
8777            }
8778        }
8779
8780        /**
8781         * Offset all child views attached to the parent RecyclerView by dy pixels along
8782         * the vertical axis.
8783         *
8784         * @param dy Pixels to offset by
8785         */
8786        public void offsetChildrenVertical(int dy) {
8787            if (mRecyclerView != null) {
8788                mRecyclerView.offsetChildrenVertical(dy);
8789            }
8790        }
8791
8792        /**
8793         * Flags a view so that it will not be scrapped or recycled.
8794         * <p>
8795         * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
8796         * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
8797         * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
8798         * ignore the child.
8799         * <p>
8800         * Before this child can be recycled again, you have to call
8801         * {@link #stopIgnoringView(View)}.
8802         * <p>
8803         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8804         *
8805         * @param view View to ignore.
8806         * @see #stopIgnoringView(View)
8807         */
8808        public void ignoreView(View view) {
8809            if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
8810                // checking this because calling this method on a recycled or detached view may
8811                // cause loss of state.
8812                throw new IllegalArgumentException("View should be fully attached to be ignored"
8813                        + mRecyclerView.exceptionLabel());
8814            }
8815            final ViewHolder vh = getChildViewHolderInt(view);
8816            vh.addFlags(ViewHolder.FLAG_IGNORE);
8817            mRecyclerView.mViewInfoStore.removeViewHolder(vh);
8818        }
8819
8820        /**
8821         * View can be scrapped and recycled again.
8822         * <p>
8823         * Note that calling this method removes all information in the view holder.
8824         * <p>
8825         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8826         *
8827         * @param view View to ignore.
8828         */
8829        public void stopIgnoringView(View view) {
8830            final ViewHolder vh = getChildViewHolderInt(view);
8831            vh.stopIgnoring();
8832            vh.resetInternal();
8833            vh.addFlags(ViewHolder.FLAG_INVALID);
8834        }
8835
8836        /**
8837         * Temporarily detach and scrap all currently attached child views. Views will be scrapped
8838         * into the given Recycler. The Recycler may prefer to reuse scrap views before
8839         * other views that were previously recycled.
8840         *
8841         * @param recycler Recycler to scrap views into
8842         */
8843        public void detachAndScrapAttachedViews(Recycler recycler) {
8844            final int childCount = getChildCount();
8845            for (int i = childCount - 1; i >= 0; i--) {
8846                final View v = getChildAt(i);
8847                scrapOrRecycleView(recycler, i, v);
8848            }
8849        }
8850
8851        private void scrapOrRecycleView(Recycler recycler, int index, View view) {
8852            final ViewHolder viewHolder = getChildViewHolderInt(view);
8853            if (viewHolder.shouldIgnore()) {
8854                if (DEBUG) {
8855                    Log.d(TAG, "ignoring view " + viewHolder);
8856                }
8857                return;
8858            }
8859            if (viewHolder.isInvalid() && !viewHolder.isRemoved()
8860                    && !mRecyclerView.mAdapter.hasStableIds()) {
8861                removeViewAt(index);
8862                recycler.recycleViewHolderInternal(viewHolder);
8863            } else {
8864                detachViewAt(index);
8865                recycler.scrapView(view);
8866                mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
8867            }
8868        }
8869
8870        /**
8871         * Recycles the scrapped views.
8872         * <p>
8873         * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
8874         * the expected behavior if scrapped views are used for animations. Otherwise, we need to
8875         * call remove and invalidate RecyclerView to ensure UI update.
8876         *
8877         * @param recycler Recycler
8878         */
8879        void removeAndRecycleScrapInt(Recycler recycler) {
8880            final int scrapCount = recycler.getScrapCount();
8881            // Loop backward, recycler might be changed by removeDetachedView()
8882            for (int i = scrapCount - 1; i >= 0; i--) {
8883                final View scrap = recycler.getScrapViewAt(i);
8884                final ViewHolder vh = getChildViewHolderInt(scrap);
8885                if (vh.shouldIgnore()) {
8886                    continue;
8887                }
8888                // If the scrap view is animating, we need to cancel them first. If we cancel it
8889                // here, ItemAnimator callback may recycle it which will cause double recycling.
8890                // To avoid this, we mark it as not recycleable before calling the item animator.
8891                // Since removeDetachedView calls a user API, a common mistake (ending animations on
8892                // the view) may recycle it too, so we guard it before we call user APIs.
8893                vh.setIsRecyclable(false);
8894                if (vh.isTmpDetached()) {
8895                    mRecyclerView.removeDetachedView(scrap, false);
8896                }
8897                if (mRecyclerView.mItemAnimator != null) {
8898                    mRecyclerView.mItemAnimator.endAnimation(vh);
8899                }
8900                vh.setIsRecyclable(true);
8901                recycler.quickRecycleScrapView(scrap);
8902            }
8903            recycler.clearScrap();
8904            if (scrapCount > 0) {
8905                mRecyclerView.invalidate();
8906            }
8907        }
8908
8909
8910        /**
8911         * Measure a child view using standard measurement policy, taking the padding
8912         * of the parent RecyclerView and any added item decorations into account.
8913         *
8914         * <p>If the RecyclerView can be scrolled in either dimension the caller may
8915         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8916         *
8917         * @param child Child view to measure
8918         * @param widthUsed Width in pixels currently consumed by other views, if relevant
8919         * @param heightUsed Height in pixels currently consumed by other views, if relevant
8920         */
8921        public void measureChild(View child, int widthUsed, int heightUsed) {
8922            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8923
8924            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8925            widthUsed += insets.left + insets.right;
8926            heightUsed += insets.top + insets.bottom;
8927            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8928                    getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
8929                    canScrollHorizontally());
8930            final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8931                    getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
8932                    canScrollVertically());
8933            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8934                child.measure(widthSpec, heightSpec);
8935            }
8936        }
8937
8938        /**
8939         * RecyclerView internally does its own View measurement caching which should help with
8940         * WRAP_CONTENT.
8941         * <p>
8942         * Use this method if the View is already measured once in this layout pass.
8943         */
8944        boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8945            return !mMeasurementCacheEnabled
8946                    || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
8947                    || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
8948        }
8949
8950        // we may consider making this public
8951        /**
8952         * RecyclerView internally does its own View measurement caching which should help with
8953         * WRAP_CONTENT.
8954         * <p>
8955         * Use this method if the View is not yet measured and you need to decide whether to
8956         * measure this View or not.
8957         */
8958        boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8959            return child.isLayoutRequested()
8960                    || !mMeasurementCacheEnabled
8961                    || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
8962                    || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
8963        }
8964
8965        /**
8966         * In addition to the View Framework's measurement cache, RecyclerView uses its own
8967         * additional measurement cache for its children to avoid re-measuring them when not
8968         * necessary. It is on by default but it can be turned off via
8969         * {@link #setMeasurementCacheEnabled(boolean)}.
8970         *
8971         * @return True if measurement cache is enabled, false otherwise.
8972         *
8973         * @see #setMeasurementCacheEnabled(boolean)
8974         */
8975        public boolean isMeasurementCacheEnabled() {
8976            return mMeasurementCacheEnabled;
8977        }
8978
8979        /**
8980         * Sets whether RecyclerView should use its own measurement cache for the children. This is
8981         * a more aggressive cache than the framework uses.
8982         *
8983         * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
8984         *
8985         * @see #isMeasurementCacheEnabled()
8986         */
8987        public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
8988            mMeasurementCacheEnabled = measurementCacheEnabled;
8989        }
8990
8991        private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
8992            final int specMode = MeasureSpec.getMode(spec);
8993            final int specSize = MeasureSpec.getSize(spec);
8994            if (dimension > 0 && childSize != dimension) {
8995                return false;
8996            }
8997            switch (specMode) {
8998                case MeasureSpec.UNSPECIFIED:
8999                    return true;
9000                case MeasureSpec.AT_MOST:
9001                    return specSize >= childSize;
9002                case MeasureSpec.EXACTLY:
9003                    return  specSize == childSize;
9004            }
9005            return false;
9006        }
9007
9008        /**
9009         * Measure a child view using standard measurement policy, taking the padding
9010         * of the parent RecyclerView, any added item decorations and the child margins
9011         * into account.
9012         *
9013         * <p>If the RecyclerView can be scrolled in either dimension the caller may
9014         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
9015         *
9016         * @param child Child view to measure
9017         * @param widthUsed Width in pixels currently consumed by other views, if relevant
9018         * @param heightUsed Height in pixels currently consumed by other views, if relevant
9019         */
9020        public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
9021            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
9022
9023            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
9024            widthUsed += insets.left + insets.right;
9025            heightUsed += insets.top + insets.bottom;
9026
9027            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
9028                    getPaddingLeft() + getPaddingRight()
9029                            + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
9030                    canScrollHorizontally());
9031            final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
9032                    getPaddingTop() + getPaddingBottom()
9033                            + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
9034                    canScrollVertically());
9035            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
9036                child.measure(widthSpec, heightSpec);
9037            }
9038        }
9039
9040        /**
9041         * Calculate a MeasureSpec value for measuring a child view in one dimension.
9042         *
9043         * @param parentSize Size of the parent view where the child will be placed
9044         * @param padding Total space currently consumed by other elements of the parent
9045         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
9046         *                       Generally obtained from the child view's LayoutParams
9047         * @param canScroll true if the parent RecyclerView can scroll in this dimension
9048         *
9049         * @return a MeasureSpec value for the child view
9050         * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
9051         */
9052        @Deprecated
9053        public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
9054                boolean canScroll) {
9055            int size = Math.max(0, parentSize - padding);
9056            int resultSize = 0;
9057            int resultMode = 0;
9058            if (canScroll) {
9059                if (childDimension >= 0) {
9060                    resultSize = childDimension;
9061                    resultMode = MeasureSpec.EXACTLY;
9062                } else {
9063                    // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
9064                    // instead using UNSPECIFIED.
9065                    resultSize = 0;
9066                    resultMode = MeasureSpec.UNSPECIFIED;
9067                }
9068            } else {
9069                if (childDimension >= 0) {
9070                    resultSize = childDimension;
9071                    resultMode = MeasureSpec.EXACTLY;
9072                } else if (childDimension == LayoutParams.MATCH_PARENT) {
9073                    resultSize = size;
9074                    // TODO this should be my spec.
9075                    resultMode = MeasureSpec.EXACTLY;
9076                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
9077                    resultSize = size;
9078                    resultMode = MeasureSpec.AT_MOST;
9079                }
9080            }
9081            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
9082        }
9083
9084        /**
9085         * Calculate a MeasureSpec value for measuring a child view in one dimension.
9086         *
9087         * @param parentSize Size of the parent view where the child will be placed
9088         * @param parentMode The measurement spec mode of the parent
9089         * @param padding Total space currently consumed by other elements of parent
9090         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
9091         *                       Generally obtained from the child view's LayoutParams
9092         * @param canScroll true if the parent RecyclerView can scroll in this dimension
9093         *
9094         * @return a MeasureSpec value for the child view
9095         */
9096        public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
9097                int childDimension, boolean canScroll) {
9098            int size = Math.max(0, parentSize - padding);
9099            int resultSize = 0;
9100            int resultMode = 0;
9101            if (canScroll) {
9102                if (childDimension >= 0) {
9103                    resultSize = childDimension;
9104                    resultMode = MeasureSpec.EXACTLY;
9105                } else if (childDimension == LayoutParams.MATCH_PARENT) {
9106                    switch (parentMode) {
9107                        case MeasureSpec.AT_MOST:
9108                        case MeasureSpec.EXACTLY:
9109                            resultSize = size;
9110                            resultMode = parentMode;
9111                            break;
9112                        case MeasureSpec.UNSPECIFIED:
9113                            resultSize = 0;
9114                            resultMode = MeasureSpec.UNSPECIFIED;
9115                            break;
9116                    }
9117                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
9118                    resultSize = 0;
9119                    resultMode = MeasureSpec.UNSPECIFIED;
9120                }
9121            } else {
9122                if (childDimension >= 0) {
9123                    resultSize = childDimension;
9124                    resultMode = MeasureSpec.EXACTLY;
9125                } else if (childDimension == LayoutParams.MATCH_PARENT) {
9126                    resultSize = size;
9127                    resultMode = parentMode;
9128                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
9129                    resultSize = size;
9130                    if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
9131                        resultMode = MeasureSpec.AT_MOST;
9132                    } else {
9133                        resultMode = MeasureSpec.UNSPECIFIED;
9134                    }
9135
9136                }
9137            }
9138            //noinspection WrongConstant
9139            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
9140        }
9141
9142        /**
9143         * Returns the measured width of the given child, plus the additional size of
9144         * any insets applied by {@link ItemDecoration ItemDecorations}.
9145         *
9146         * @param child Child view to query
9147         * @return child's measured width plus <code>ItemDecoration</code> insets
9148         *
9149         * @see View#getMeasuredWidth()
9150         */
9151        public int getDecoratedMeasuredWidth(View child) {
9152            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9153            return child.getMeasuredWidth() + insets.left + insets.right;
9154        }
9155
9156        /**
9157         * Returns the measured height of the given child, plus the additional size of
9158         * any insets applied by {@link ItemDecoration ItemDecorations}.
9159         *
9160         * @param child Child view to query
9161         * @return child's measured height plus <code>ItemDecoration</code> insets
9162         *
9163         * @see View#getMeasuredHeight()
9164         */
9165        public int getDecoratedMeasuredHeight(View child) {
9166            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9167            return child.getMeasuredHeight() + insets.top + insets.bottom;
9168        }
9169
9170        /**
9171         * Lay out the given child view within the RecyclerView using coordinates that
9172         * include any current {@link ItemDecoration ItemDecorations}.
9173         *
9174         * <p>LayoutManagers should prefer working in sizes and coordinates that include
9175         * item decoration insets whenever possible. This allows the LayoutManager to effectively
9176         * ignore decoration insets within measurement and layout code. See the following
9177         * methods:</p>
9178         * <ul>
9179         *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
9180         *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
9181         *     <li>{@link #measureChild(View, int, int)}</li>
9182         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
9183         *     <li>{@link #getDecoratedLeft(View)}</li>
9184         *     <li>{@link #getDecoratedTop(View)}</li>
9185         *     <li>{@link #getDecoratedRight(View)}</li>
9186         *     <li>{@link #getDecoratedBottom(View)}</li>
9187         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
9188         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
9189         * </ul>
9190         *
9191         * @param child Child to lay out
9192         * @param left Left edge, with item decoration insets included
9193         * @param top Top edge, with item decoration insets included
9194         * @param right Right edge, with item decoration insets included
9195         * @param bottom Bottom edge, with item decoration insets included
9196         *
9197         * @see View#layout(int, int, int, int)
9198         * @see #layoutDecoratedWithMargins(View, int, int, int, int)
9199         */
9200        public void layoutDecorated(View child, int left, int top, int right, int bottom) {
9201            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9202            child.layout(left + insets.left, top + insets.top, right - insets.right,
9203                    bottom - insets.bottom);
9204        }
9205
9206        /**
9207         * Lay out the given child view within the RecyclerView using coordinates that
9208         * include any current {@link ItemDecoration ItemDecorations} and margins.
9209         *
9210         * <p>LayoutManagers should prefer working in sizes and coordinates that include
9211         * item decoration insets whenever possible. This allows the LayoutManager to effectively
9212         * ignore decoration insets within measurement and layout code. See the following
9213         * methods:</p>
9214         * <ul>
9215         *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
9216         *     <li>{@link #measureChild(View, int, int)}</li>
9217         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
9218         *     <li>{@link #getDecoratedLeft(View)}</li>
9219         *     <li>{@link #getDecoratedTop(View)}</li>
9220         *     <li>{@link #getDecoratedRight(View)}</li>
9221         *     <li>{@link #getDecoratedBottom(View)}</li>
9222         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
9223         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
9224         * </ul>
9225         *
9226         * @param child Child to lay out
9227         * @param left Left edge, with item decoration insets and left margin included
9228         * @param top Top edge, with item decoration insets and top margin included
9229         * @param right Right edge, with item decoration insets and right margin included
9230         * @param bottom Bottom edge, with item decoration insets and bottom margin included
9231         *
9232         * @see View#layout(int, int, int, int)
9233         * @see #layoutDecorated(View, int, int, int, int)
9234         */
9235        public void layoutDecoratedWithMargins(View child, int left, int top, int right,
9236                int bottom) {
9237            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
9238            final Rect insets = lp.mDecorInsets;
9239            child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
9240                    right - insets.right - lp.rightMargin,
9241                    bottom - insets.bottom - lp.bottomMargin);
9242        }
9243
9244        /**
9245         * Calculates the bounding box of the View while taking into account its matrix changes
9246         * (translation, scale etc) with respect to the RecyclerView.
9247         * <p>
9248         * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
9249         * the View's matrix so that the decor offsets also go through the same transformation.
9250         *
9251         * @param child The ItemView whose bounding box should be calculated.
9252         * @param includeDecorInsets True if the decor insets should be included in the bounding box
9253         * @param out The rectangle into which the output will be written.
9254         */
9255        public void getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out) {
9256            if (includeDecorInsets) {
9257                Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
9258                out.set(-insets.left, -insets.top,
9259                        child.getWidth() + insets.right, child.getHeight() + insets.bottom);
9260            } else {
9261                out.set(0, 0, child.getWidth(), child.getHeight());
9262            }
9263
9264            if (mRecyclerView != null) {
9265                final Matrix childMatrix = child.getMatrix();
9266                if (childMatrix != null && !childMatrix.isIdentity()) {
9267                    final RectF tempRectF = mRecyclerView.mTempRectF;
9268                    tempRectF.set(out);
9269                    childMatrix.mapRect(tempRectF);
9270                    out.set(
9271                            (int) Math.floor(tempRectF.left),
9272                            (int) Math.floor(tempRectF.top),
9273                            (int) Math.ceil(tempRectF.right),
9274                            (int) Math.ceil(tempRectF.bottom)
9275                    );
9276                }
9277            }
9278            out.offset(child.getLeft(), child.getTop());
9279        }
9280
9281        /**
9282         * Returns the bounds of the view including its decoration and margins.
9283         *
9284         * @param view The view element to check
9285         * @param outBounds A rect that will receive the bounds of the element including its
9286         *                  decoration and margins.
9287         */
9288        public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
9289            RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
9290        }
9291
9292        /**
9293         * Returns the left edge of the given child view within its parent, offset by any applied
9294         * {@link ItemDecoration ItemDecorations}.
9295         *
9296         * @param child Child to query
9297         * @return Child left edge with offsets applied
9298         * @see #getLeftDecorationWidth(View)
9299         */
9300        public int getDecoratedLeft(View child) {
9301            return child.getLeft() - getLeftDecorationWidth(child);
9302        }
9303
9304        /**
9305         * Returns the top edge of the given child view within its parent, offset by any applied
9306         * {@link ItemDecoration ItemDecorations}.
9307         *
9308         * @param child Child to query
9309         * @return Child top edge with offsets applied
9310         * @see #getTopDecorationHeight(View)
9311         */
9312        public int getDecoratedTop(View child) {
9313            return child.getTop() - getTopDecorationHeight(child);
9314        }
9315
9316        /**
9317         * Returns the right edge of the given child view within its parent, offset by any applied
9318         * {@link ItemDecoration ItemDecorations}.
9319         *
9320         * @param child Child to query
9321         * @return Child right edge with offsets applied
9322         * @see #getRightDecorationWidth(View)
9323         */
9324        public int getDecoratedRight(View child) {
9325            return child.getRight() + getRightDecorationWidth(child);
9326        }
9327
9328        /**
9329         * Returns the bottom edge of the given child view within its parent, offset by any applied
9330         * {@link ItemDecoration ItemDecorations}.
9331         *
9332         * @param child Child to query
9333         * @return Child bottom edge with offsets applied
9334         * @see #getBottomDecorationHeight(View)
9335         */
9336        public int getDecoratedBottom(View child) {
9337            return child.getBottom() + getBottomDecorationHeight(child);
9338        }
9339
9340        /**
9341         * Calculates the item decor insets applied to the given child and updates the provided
9342         * Rect instance with the inset values.
9343         * <ul>
9344         *     <li>The Rect's left is set to the total width of left decorations.</li>
9345         *     <li>The Rect's top is set to the total height of top decorations.</li>
9346         *     <li>The Rect's right is set to the total width of right decorations.</li>
9347         *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
9348         * </ul>
9349         * <p>
9350         * Note that item decorations are automatically calculated when one of the LayoutManager's
9351         * measure child methods is called. If you need to measure the child with custom specs via
9352         * {@link View#measure(int, int)}, you can use this method to get decorations.
9353         *
9354         * @param child The child view whose decorations should be calculated
9355         * @param outRect The Rect to hold result values
9356         */
9357        public void calculateItemDecorationsForChild(View child, Rect outRect) {
9358            if (mRecyclerView == null) {
9359                outRect.set(0, 0, 0, 0);
9360                return;
9361            }
9362            Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
9363            outRect.set(insets);
9364        }
9365
9366        /**
9367         * Returns the total height of item decorations applied to child's top.
9368         * <p>
9369         * Note that this value is not updated until the View is measured or
9370         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9371         *
9372         * @param child Child to query
9373         * @return The total height of item decorations applied to the child's top.
9374         * @see #getDecoratedTop(View)
9375         * @see #calculateItemDecorationsForChild(View, Rect)
9376         */
9377        public int getTopDecorationHeight(View child) {
9378            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
9379        }
9380
9381        /**
9382         * Returns the total height of item decorations applied to child's bottom.
9383         * <p>
9384         * Note that this value is not updated until the View is measured or
9385         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9386         *
9387         * @param child Child to query
9388         * @return The total height of item decorations applied to the child's bottom.
9389         * @see #getDecoratedBottom(View)
9390         * @see #calculateItemDecorationsForChild(View, Rect)
9391         */
9392        public int getBottomDecorationHeight(View child) {
9393            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
9394        }
9395
9396        /**
9397         * Returns the total width of item decorations applied to child's left.
9398         * <p>
9399         * Note that this value is not updated until the View is measured or
9400         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9401         *
9402         * @param child Child to query
9403         * @return The total width of item decorations applied to the child's left.
9404         * @see #getDecoratedLeft(View)
9405         * @see #calculateItemDecorationsForChild(View, Rect)
9406         */
9407        public int getLeftDecorationWidth(View child) {
9408            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
9409        }
9410
9411        /**
9412         * Returns the total width of item decorations applied to child's right.
9413         * <p>
9414         * Note that this value is not updated until the View is measured or
9415         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9416         *
9417         * @param child Child to query
9418         * @return The total width of item decorations applied to the child's right.
9419         * @see #getDecoratedRight(View)
9420         * @see #calculateItemDecorationsForChild(View, Rect)
9421         */
9422        public int getRightDecorationWidth(View child) {
9423            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
9424        }
9425
9426        /**
9427         * Called when searching for a focusable view in the given direction has failed
9428         * for the current content of the RecyclerView.
9429         *
9430         * <p>This is the LayoutManager's opportunity to populate views in the given direction
9431         * to fulfill the request if it can. The LayoutManager should attach and return
9432         * the view to be focused, if a focusable view in the given direction is found.
9433         * Otherwise, if all the existing (or the newly populated views) are unfocusable, it returns
9434         * the next unfocusable view to become visible on the screen. This unfocusable view is
9435         * typically the first view that's either partially or fully out of RV's padded bounded
9436         * area in the given direction. The default implementation returns null.</p>
9437         *
9438         * @param focused   The currently focused view
9439         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9440         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9441         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9442         *                  or 0 for not applicable
9443         * @param recycler  The recycler to use for obtaining views for currently offscreen items
9444         * @param state     Transient state of RecyclerView
9445         * @return The chosen view to be focused if a focusable view is found, otherwise an
9446         * unfocusable view to become visible onto the screen, else null.
9447         */
9448        @Nullable
9449        public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
9450                State state) {
9451            return null;
9452        }
9453
9454        /**
9455         * This method gives a LayoutManager an opportunity to intercept the initial focus search
9456         * before the default behavior of {@link FocusFinder} is used. If this method returns
9457         * null FocusFinder will attempt to find a focusable child view. If it fails
9458         * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
9459         * will be called to give the LayoutManager an opportunity to add new views for items
9460         * that did not have attached views representing them. The LayoutManager should not add
9461         * or remove views from this method.
9462         *
9463         * @param focused The currently focused view
9464         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9465         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9466         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9467         * @return A descendant view to focus or null to fall back to default behavior.
9468         *         The default implementation returns null.
9469         */
9470        public View onInterceptFocusSearch(View focused, int direction) {
9471            return null;
9472        }
9473
9474        /**
9475         * Returns the scroll amount that brings the given rect in child's coordinate system within
9476         * the padded area of RecyclerView.
9477         * @param parent The parent RecyclerView.
9478         * @param child The direct child making the request.
9479         * @param rect The rectangle in the child's coordinates the child
9480         *             wishes to be on the screen.
9481         * @param immediate True to forbid animated or delayed scrolling,
9482         *                  false otherwise
9483         * @return The array containing the scroll amount in x and y directions that brings the
9484         * given rect into RV's padded area.
9485         */
9486        private int[] getChildRectangleOnScreenScrollAmount(RecyclerView parent, View child,
9487                Rect rect, boolean immediate) {
9488            int[] out = new int[2];
9489            final int parentLeft = getPaddingLeft();
9490            final int parentTop = getPaddingTop();
9491            final int parentRight = getWidth() - getPaddingRight();
9492            final int parentBottom = getHeight() - getPaddingBottom();
9493            final int childLeft = child.getLeft() + rect.left - child.getScrollX();
9494            final int childTop = child.getTop() + rect.top - child.getScrollY();
9495            final int childRight = childLeft + rect.width();
9496            final int childBottom = childTop + rect.height();
9497
9498            final int offScreenLeft = Math.min(0, childLeft - parentLeft);
9499            final int offScreenTop = Math.min(0, childTop - parentTop);
9500            final int offScreenRight = Math.max(0, childRight - parentRight);
9501            final int offScreenBottom = Math.max(0, childBottom - parentBottom);
9502
9503            // Favor the "start" layout direction over the end when bringing one side or the other
9504            // of a large rect into view. If we decide to bring in end because start is already
9505            // visible, limit the scroll such that start won't go out of bounds.
9506            final int dx;
9507            if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) {
9508                dx = offScreenRight != 0 ? offScreenRight
9509                        : Math.max(offScreenLeft, childRight - parentRight);
9510            } else {
9511                dx = offScreenLeft != 0 ? offScreenLeft
9512                        : Math.min(childLeft - parentLeft, offScreenRight);
9513            }
9514
9515            // Favor bringing the top into view over the bottom. If top is already visible and
9516            // we should scroll to make bottom visible, make sure top does not go out of bounds.
9517            final int dy = offScreenTop != 0 ? offScreenTop
9518                    : Math.min(childTop - parentTop, offScreenBottom);
9519            out[0] = dx;
9520            out[1] = dy;
9521            return out;
9522        }
9523        /**
9524         * Called when a child of the RecyclerView wants a particular rectangle to be positioned
9525         * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
9526         * android.graphics.Rect, boolean)} for more details.
9527         *
9528         * <p>The base implementation will attempt to perform a standard programmatic scroll
9529         * to bring the given rect into view, within the padded area of the RecyclerView.</p>
9530         *
9531         * @param child The direct child making the request.
9532         * @param rect  The rectangle in the child's coordinates the child
9533         *              wishes to be on the screen.
9534         * @param immediate True to forbid animated or delayed scrolling,
9535         *                  false otherwise
9536         * @return Whether the group scrolled to handle the operation
9537         */
9538        public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
9539                boolean immediate) {
9540            return requestChildRectangleOnScreen(parent, child, rect, immediate, false);
9541        }
9542
9543        /**
9544         * Requests that the given child of the RecyclerView be positioned onto the screen. This
9545         * method can be called for both unfocusable and focusable child views. For unfocusable
9546         * child views, focusedChildVisible is typically true in which case, layout manager
9547         * makes the child view visible only if the currently focused child stays in-bounds of RV.
9548         * @param parent The parent RecyclerView.
9549         * @param child The direct child making the request.
9550         * @param rect The rectangle in the child's coordinates the child
9551         *              wishes to be on the screen.
9552         * @param immediate True to forbid animated or delayed scrolling,
9553         *                  false otherwise
9554         * @param focusedChildVisible Whether the currently focused view must stay visible.
9555         * @return Whether the group scrolled to handle the operation
9556         */
9557        public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
9558                boolean immediate,
9559                boolean focusedChildVisible) {
9560            int[] scrollAmount = getChildRectangleOnScreenScrollAmount(parent, child, rect,
9561                    immediate);
9562            int dx = scrollAmount[0];
9563            int dy = scrollAmount[1];
9564            if (!focusedChildVisible || isFocusedChildVisibleAfterScrolling(parent, dx, dy)) {
9565                if (dx != 0 || dy != 0) {
9566                    if (immediate) {
9567                        parent.scrollBy(dx, dy);
9568                    } else {
9569                        parent.smoothScrollBy(dx, dy);
9570                    }
9571                    return true;
9572                }
9573            }
9574            return false;
9575        }
9576
9577        /**
9578         * Returns whether the given child view is partially or fully visible within the padded
9579         * bounded area of RecyclerView, depending on the input parameters.
9580         * A view is partially visible if it has non-zero overlap with RV's padded bounded area.
9581         * If acceptEndPointInclusion flag is set to true, it's also considered partially
9582         * visible if it's located outside RV's bounds and it's hitting either RV's start or end
9583         * bounds.
9584         *
9585         * @param child The child view to be examined.
9586         * @param completelyVisible If true, the method returns true if and only if the child is
9587         *                          completely visible. If false, the method returns true if and
9588         *                          only if the child is only partially visible (that is it will
9589         *                          return false if the child is either completely visible or out
9590         *                          of RV's bounds).
9591         * @param acceptEndPointInclusion If the view's endpoint intersection with RV's start of end
9592         *                                bounds is enough to consider it partially visible,
9593         *                                false otherwise.
9594         * @return True if the given child is partially or fully visible, false otherwise.
9595         */
9596        public boolean isViewPartiallyVisible(@NonNull View child, boolean completelyVisible,
9597                boolean acceptEndPointInclusion) {
9598            int boundsFlag = (ViewBoundsCheck.FLAG_CVS_GT_PVS | ViewBoundsCheck.FLAG_CVS_EQ_PVS
9599                    | ViewBoundsCheck.FLAG_CVE_LT_PVE | ViewBoundsCheck.FLAG_CVE_EQ_PVE);
9600            boolean isViewFullyVisible = mHorizontalBoundCheck.isViewWithinBoundFlags(child,
9601                    boundsFlag)
9602                    && mVerticalBoundCheck.isViewWithinBoundFlags(child, boundsFlag);
9603            if (completelyVisible) {
9604                return isViewFullyVisible;
9605            } else {
9606                return !isViewFullyVisible;
9607            }
9608        }
9609
9610        /**
9611         * Returns whether the currently focused child stays within RV's bounds with the given
9612         * amount of scrolling.
9613         * @param parent The parent RecyclerView.
9614         * @param dx The scrolling in x-axis direction to be performed.
9615         * @param dy The scrolling in y-axis direction to be performed.
9616         * @return {@code false} if the focused child is not at least partially visible after
9617         *         scrolling or no focused child exists, {@code true} otherwise.
9618         */
9619        private boolean isFocusedChildVisibleAfterScrolling(RecyclerView parent, int dx, int dy) {
9620            final View focusedChild = parent.getFocusedChild();
9621            if (focusedChild == null) {
9622                return false;
9623            }
9624            final int parentLeft = getPaddingLeft();
9625            final int parentTop = getPaddingTop();
9626            final int parentRight = getWidth() - getPaddingRight();
9627            final int parentBottom = getHeight() - getPaddingBottom();
9628            final Rect bounds = mRecyclerView.mTempRect;
9629            getDecoratedBoundsWithMargins(focusedChild, bounds);
9630
9631            if (bounds.left - dx >= parentRight || bounds.right - dx <= parentLeft
9632                    || bounds.top - dy >= parentBottom || bounds.bottom - dy <= parentTop) {
9633                return false;
9634            }
9635            return true;
9636        }
9637
9638        /**
9639         * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
9640         */
9641        @Deprecated
9642        public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
9643            // eat the request if we are in the middle of a scroll or layout
9644            return isSmoothScrolling() || parent.isComputingLayout();
9645        }
9646
9647        /**
9648         * Called when a descendant view of the RecyclerView requests focus.
9649         *
9650         * <p>A LayoutManager wishing to keep focused views aligned in a specific
9651         * portion of the view may implement that behavior in an override of this method.</p>
9652         *
9653         * <p>If the LayoutManager executes different behavior that should override the default
9654         * behavior of scrolling the focused child on screen instead of running alongside it,
9655         * this method should return true.</p>
9656         *
9657         * @param parent  The RecyclerView hosting this LayoutManager
9658         * @param state   Current state of RecyclerView
9659         * @param child   Direct child of the RecyclerView containing the newly focused view
9660         * @param focused The newly focused view. This may be the same view as child or it may be
9661         *                null
9662         * @return true if the default scroll behavior should be suppressed
9663         */
9664        public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
9665                View focused) {
9666            return onRequestChildFocus(parent, child, focused);
9667        }
9668
9669        /**
9670         * Called if the RecyclerView this LayoutManager is bound to has a different adapter set via
9671         * {@link RecyclerView#setAdapter(Adapter)} or
9672         * {@link RecyclerView#swapAdapter(Adapter, boolean)}. The LayoutManager may use this
9673         * opportunity to clear caches and configure state such that it can relayout appropriately
9674         * with the new data and potentially new view types.
9675         *
9676         * <p>The default implementation removes all currently attached views.</p>
9677         *
9678         * @param oldAdapter The previous adapter instance. Will be null if there was previously no
9679         *                   adapter.
9680         * @param newAdapter The new adapter instance. Might be null if
9681         *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
9682         */
9683        public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
9684        }
9685
9686        /**
9687         * Called to populate focusable views within the RecyclerView.
9688         *
9689         * <p>The LayoutManager implementation should return <code>true</code> if the default
9690         * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
9691         * suppressed.</p>
9692         *
9693         * <p>The default implementation returns <code>false</code> to trigger RecyclerView
9694         * to fall back to the default ViewGroup behavior.</p>
9695         *
9696         * @param recyclerView The RecyclerView hosting this LayoutManager
9697         * @param views List of output views. This method should add valid focusable views
9698         *              to this list.
9699         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9700         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9701         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9702         * @param focusableMode The type of focusables to be added.
9703         *
9704         * @return true to suppress the default behavior, false to add default focusables after
9705         *         this method returns.
9706         *
9707         * @see #FOCUSABLES_ALL
9708         * @see #FOCUSABLES_TOUCH_MODE
9709         */
9710        public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,
9711                int direction, int focusableMode) {
9712            return false;
9713        }
9714
9715        /**
9716         * Called in response to a call to {@link Adapter#notifyDataSetChanged()} or
9717         * {@link RecyclerView#swapAdapter(Adapter, boolean)} ()} and signals that the the entire
9718         * data set has changed.
9719         *
9720         * @param recyclerView
9721         */
9722        public void onItemsChanged(RecyclerView recyclerView) {
9723        }
9724
9725        /**
9726         * Called when items have been added to the adapter. The LayoutManager may choose to
9727         * requestLayout if the inserted items would require refreshing the currently visible set
9728         * of child views. (e.g. currently empty space would be filled by appended items, etc.)
9729         *
9730         * @param recyclerView
9731         * @param positionStart
9732         * @param itemCount
9733         */
9734        public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
9735        }
9736
9737        /**
9738         * Called when items have been removed from the adapter.
9739         *
9740         * @param recyclerView
9741         * @param positionStart
9742         * @param itemCount
9743         */
9744        public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
9745        }
9746
9747        /**
9748         * Called when items have been changed in the adapter.
9749         * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
9750         * instead, then this callback will not be invoked.
9751         *
9752         * @param recyclerView
9753         * @param positionStart
9754         * @param itemCount
9755         */
9756        public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
9757        }
9758
9759        /**
9760         * Called when items have been changed in the adapter and with optional payload.
9761         * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
9762         *
9763         * @param recyclerView
9764         * @param positionStart
9765         * @param itemCount
9766         * @param payload
9767         */
9768        public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
9769                Object payload) {
9770            onItemsUpdated(recyclerView, positionStart, itemCount);
9771        }
9772
9773        /**
9774         * Called when an item is moved withing the adapter.
9775         * <p>
9776         * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
9777         * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
9778         * is called.
9779         *
9780         * @param recyclerView
9781         * @param from
9782         * @param to
9783         * @param itemCount
9784         */
9785        public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
9786
9787        }
9788
9789
9790        /**
9791         * <p>Override this method if you want to support scroll bars.</p>
9792         *
9793         * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
9794         *
9795         * <p>Default implementation returns 0.</p>
9796         *
9797         * @param state Current state of RecyclerView
9798         * @return The horizontal extent of the scrollbar's thumb
9799         * @see RecyclerView#computeHorizontalScrollExtent()
9800         */
9801        public int computeHorizontalScrollExtent(State state) {
9802            return 0;
9803        }
9804
9805        /**
9806         * <p>Override this method if you want to support scroll bars.</p>
9807         *
9808         * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
9809         *
9810         * <p>Default implementation returns 0.</p>
9811         *
9812         * @param state Current State of RecyclerView where you can find total item count
9813         * @return The horizontal offset of the scrollbar's thumb
9814         * @see RecyclerView#computeHorizontalScrollOffset()
9815         */
9816        public int computeHorizontalScrollOffset(State state) {
9817            return 0;
9818        }
9819
9820        /**
9821         * <p>Override this method if you want to support scroll bars.</p>
9822         *
9823         * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
9824         *
9825         * <p>Default implementation returns 0.</p>
9826         *
9827         * @param state Current State of RecyclerView where you can find total item count
9828         * @return The total horizontal range represented by the vertical scrollbar
9829         * @see RecyclerView#computeHorizontalScrollRange()
9830         */
9831        public int computeHorizontalScrollRange(State state) {
9832            return 0;
9833        }
9834
9835        /**
9836         * <p>Override this method if you want to support scroll bars.</p>
9837         *
9838         * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
9839         *
9840         * <p>Default implementation returns 0.</p>
9841         *
9842         * @param state Current state of RecyclerView
9843         * @return The vertical extent of the scrollbar's thumb
9844         * @see RecyclerView#computeVerticalScrollExtent()
9845         */
9846        public int computeVerticalScrollExtent(State state) {
9847            return 0;
9848        }
9849
9850        /**
9851         * <p>Override this method if you want to support scroll bars.</p>
9852         *
9853         * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
9854         *
9855         * <p>Default implementation returns 0.</p>
9856         *
9857         * @param state Current State of RecyclerView where you can find total item count
9858         * @return The vertical offset of the scrollbar's thumb
9859         * @see RecyclerView#computeVerticalScrollOffset()
9860         */
9861        public int computeVerticalScrollOffset(State state) {
9862            return 0;
9863        }
9864
9865        /**
9866         * <p>Override this method if you want to support scroll bars.</p>
9867         *
9868         * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
9869         *
9870         * <p>Default implementation returns 0.</p>
9871         *
9872         * @param state Current State of RecyclerView where you can find total item count
9873         * @return The total vertical range represented by the vertical scrollbar
9874         * @see RecyclerView#computeVerticalScrollRange()
9875         */
9876        public int computeVerticalScrollRange(State state) {
9877            return 0;
9878        }
9879
9880        /**
9881         * Measure the attached RecyclerView. Implementations must call
9882         * {@link #setMeasuredDimension(int, int)} before returning.
9883         * <p>
9884         * It is strongly advised to use the AutoMeasure mechanism by overriding
9885         * {@link #isAutoMeasureEnabled()} to return true as AutoMeasure handles all the standard
9886         * measure cases including when the RecyclerView's layout_width or layout_height have been
9887         * set to wrap_content.  If {@link #isAutoMeasureEnabled()} is overridden to return true,
9888         * this method should not be overridden.
9889         * <p>
9890         * The default implementation will handle EXACTLY measurements and respect
9891         * the minimum width and height properties of the host RecyclerView if measured
9892         * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
9893         * will consume all available space.
9894         *
9895         * @param recycler Recycler
9896         * @param state Transient state of RecyclerView
9897         * @param widthSpec Width {@link android.view.View.MeasureSpec}
9898         * @param heightSpec Height {@link android.view.View.MeasureSpec}
9899         *
9900         * @see #isAutoMeasureEnabled()
9901         * @see #setMeasuredDimension(int, int)
9902         */
9903        public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
9904            mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
9905        }
9906
9907        /**
9908         * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
9909         * host RecyclerView.
9910         *
9911         * @param widthSize Measured width
9912         * @param heightSize Measured height
9913         */
9914        public void setMeasuredDimension(int widthSize, int heightSize) {
9915            mRecyclerView.setMeasuredDimension(widthSize, heightSize);
9916        }
9917
9918        /**
9919         * @return The host RecyclerView's {@link View#getMinimumWidth()}
9920         */
9921        public int getMinimumWidth() {
9922            return ViewCompat.getMinimumWidth(mRecyclerView);
9923        }
9924
9925        /**
9926         * @return The host RecyclerView's {@link View#getMinimumHeight()}
9927         */
9928        public int getMinimumHeight() {
9929            return ViewCompat.getMinimumHeight(mRecyclerView);
9930        }
9931        /**
9932         * <p>Called when the LayoutManager should save its state. This is a good time to save your
9933         * scroll position, configuration and anything else that may be required to restore the same
9934         * layout state if the LayoutManager is recreated.</p>
9935         * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
9936         * restore. This will let you share information between your LayoutManagers but it is also
9937         * your responsibility to make sure they use the same parcelable class.</p>
9938         *
9939         * @return Necessary information for LayoutManager to be able to restore its state
9940         */
9941        public Parcelable onSaveInstanceState() {
9942            return null;
9943        }
9944
9945
9946        public void onRestoreInstanceState(Parcelable state) {
9947
9948        }
9949
9950        void stopSmoothScroller() {
9951            if (mSmoothScroller != null) {
9952                mSmoothScroller.stop();
9953            }
9954        }
9955
9956        private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
9957            if (mSmoothScroller == smoothScroller) {
9958                mSmoothScroller = null;
9959            }
9960        }
9961
9962        /**
9963         * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
9964         *
9965         * @param state The new scroll state for RecyclerView
9966         */
9967        public void onScrollStateChanged(int state) {
9968        }
9969
9970        /**
9971         * Removes all views and recycles them using the given recycler.
9972         * <p>
9973         * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
9974         * <p>
9975         * If a View is marked as "ignored", it is not removed nor recycled.
9976         *
9977         * @param recycler Recycler to use to recycle children
9978         * @see #removeAndRecycleView(View, Recycler)
9979         * @see #removeAndRecycleViewAt(int, Recycler)
9980         * @see #ignoreView(View)
9981         */
9982        public void removeAndRecycleAllViews(Recycler recycler) {
9983            for (int i = getChildCount() - 1; i >= 0; i--) {
9984                final View view = getChildAt(i);
9985                if (!getChildViewHolderInt(view).shouldIgnore()) {
9986                    removeAndRecycleViewAt(i, recycler);
9987                }
9988            }
9989        }
9990
9991        // called by accessibility delegate
9992        void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
9993            onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
9994        }
9995
9996        /**
9997         * Called by the AccessibilityDelegate when the information about the current layout should
9998         * be populated.
9999         * <p>
10000         * Default implementation adds a {@link
10001         * androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}.
10002         * <p>
10003         * You should override
10004         * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
10005         * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
10006         * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
10007         * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
10008         * more accurate accessibility information.
10009         *
10010         * @param recycler The Recycler that can be used to convert view positions into adapter
10011         *                 positions
10012         * @param state    The current state of RecyclerView
10013         * @param info     The info that should be filled by the LayoutManager
10014         * @see View#onInitializeAccessibilityNodeInfo(
10015         *android.view.accessibility.AccessibilityNodeInfo)
10016         * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
10017         * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
10018         * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
10019         * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
10020         */
10021        public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
10022                AccessibilityNodeInfoCompat info) {
10023            if (mRecyclerView.canScrollVertically(-1) || mRecyclerView.canScrollHorizontally(-1)) {
10024                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
10025                info.setScrollable(true);
10026            }
10027            if (mRecyclerView.canScrollVertically(1) || mRecyclerView.canScrollHorizontally(1)) {
10028                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
10029                info.setScrollable(true);
10030            }
10031            final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo =
10032                    AccessibilityNodeInfoCompat.CollectionInfoCompat
10033                            .obtain(getRowCountForAccessibility(recycler, state),
10034                                    getColumnCountForAccessibility(recycler, state),
10035                                    isLayoutHierarchical(recycler, state),
10036                                    getSelectionModeForAccessibility(recycler, state));
10037            info.setCollectionInfo(collectionInfo);
10038        }
10039
10040        // called by accessibility delegate
10041        public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
10042            onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
10043        }
10044
10045        /**
10046         * Called by the accessibility delegate to initialize an accessibility event.
10047         * <p>
10048         * Default implementation adds item count and scroll information to the event.
10049         *
10050         * @param recycler The Recycler that can be used to convert view positions into adapter
10051         *                 positions
10052         * @param state    The current state of RecyclerView
10053         * @param event    The event instance to initialize
10054         * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
10055         */
10056        public void onInitializeAccessibilityEvent(Recycler recycler, State state,
10057                AccessibilityEvent event) {
10058            if (mRecyclerView == null || event == null) {
10059                return;
10060            }
10061            event.setScrollable(mRecyclerView.canScrollVertically(1)
10062                    || mRecyclerView.canScrollVertically(-1)
10063                    || mRecyclerView.canScrollHorizontally(-1)
10064                    || mRecyclerView.canScrollHorizontally(1));
10065
10066            if (mRecyclerView.mAdapter != null) {
10067                event.setItemCount(mRecyclerView.mAdapter.getItemCount());
10068            }
10069        }
10070
10071        // called by accessibility delegate
10072        void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) {
10073            final ViewHolder vh = getChildViewHolderInt(host);
10074            // avoid trying to create accessibility node info for removed children
10075            if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
10076                onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
10077                        mRecyclerView.mState, host, info);
10078            }
10079        }
10080
10081        /**
10082         * Called by the AccessibilityDelegate when the accessibility information for a specific
10083         * item should be populated.
10084         * <p>
10085         * Default implementation adds basic positioning information about the item.
10086         *
10087         * @param recycler The Recycler that can be used to convert view positions into adapter
10088         *                 positions
10089         * @param state    The current state of RecyclerView
10090         * @param host     The child for which accessibility node info should be populated
10091         * @param info     The info to fill out about the item
10092         * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
10093         * android.view.accessibility.AccessibilityNodeInfo)
10094         */
10095        public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
10096                View host, AccessibilityNodeInfoCompat info) {
10097            int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
10098            int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
10099            final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo =
10100                    AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
10101                            columnIndexGuess, 1, false, false);
10102            info.setCollectionItemInfo(itemInfo);
10103        }
10104
10105        /**
10106         * A LayoutManager can call this method to force RecyclerView to run simple animations in
10107         * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
10108         * change).
10109         * <p>
10110         * Note that, calling this method will not guarantee that RecyclerView will run animations
10111         * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
10112         * not run any animations but will still clear this flag after the layout is complete.
10113         *
10114         */
10115        public void requestSimpleAnimationsInNextLayout() {
10116            mRequestedSimpleAnimations = true;
10117        }
10118
10119        /**
10120         * Returns the selection mode for accessibility. Should be
10121         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE},
10122         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or
10123         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}.
10124         * <p>
10125         * Default implementation returns
10126         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
10127         *
10128         * @param recycler The Recycler that can be used to convert view positions into adapter
10129         *                 positions
10130         * @param state    The current state of RecyclerView
10131         * @return Selection mode for accessibility. Default implementation returns
10132         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
10133         */
10134        public int getSelectionModeForAccessibility(Recycler recycler, State state) {
10135            return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
10136        }
10137
10138        /**
10139         * Returns the number of rows for accessibility.
10140         * <p>
10141         * Default implementation returns the number of items in the adapter if LayoutManager
10142         * supports vertical scrolling or 1 if LayoutManager does not support vertical
10143         * scrolling.
10144         *
10145         * @param recycler The Recycler that can be used to convert view positions into adapter
10146         *                 positions
10147         * @param state    The current state of RecyclerView
10148         * @return The number of rows in LayoutManager for accessibility.
10149         */
10150        public int getRowCountForAccessibility(Recycler recycler, State state) {
10151            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
10152                return 1;
10153            }
10154            return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
10155        }
10156
10157        /**
10158         * Returns the number of columns for accessibility.
10159         * <p>
10160         * Default implementation returns the number of items in the adapter if LayoutManager
10161         * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
10162         * scrolling.
10163         *
10164         * @param recycler The Recycler that can be used to convert view positions into adapter
10165         *                 positions
10166         * @param state    The current state of RecyclerView
10167         * @return The number of rows in LayoutManager for accessibility.
10168         */
10169        public int getColumnCountForAccessibility(Recycler recycler, State state) {
10170            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
10171                return 1;
10172            }
10173            return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
10174        }
10175
10176        /**
10177         * Returns whether layout is hierarchical or not to be used for accessibility.
10178         * <p>
10179         * Default implementation returns false.
10180         *
10181         * @param recycler The Recycler that can be used to convert view positions into adapter
10182         *                 positions
10183         * @param state    The current state of RecyclerView
10184         * @return True if layout is hierarchical.
10185         */
10186        public boolean isLayoutHierarchical(Recycler recycler, State state) {
10187            return false;
10188        }
10189
10190        // called by accessibility delegate
10191        boolean performAccessibilityAction(int action, Bundle args) {
10192            return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
10193                    action, args);
10194        }
10195
10196        /**
10197         * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
10198         *
10199         * @param recycler  The Recycler that can be used to convert view positions into adapter
10200         *                  positions
10201         * @param state     The current state of RecyclerView
10202         * @param action    The action to perform
10203         * @param args      Optional action arguments
10204         * @see View#performAccessibilityAction(int, android.os.Bundle)
10205         */
10206        public boolean performAccessibilityAction(Recycler recycler, State state, int action,
10207                Bundle args) {
10208            if (mRecyclerView == null) {
10209                return false;
10210            }
10211            int vScroll = 0, hScroll = 0;
10212            switch (action) {
10213                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
10214                    if (mRecyclerView.canScrollVertically(-1)) {
10215                        vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
10216                    }
10217                    if (mRecyclerView.canScrollHorizontally(-1)) {
10218                        hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
10219                    }
10220                    break;
10221                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
10222                    if (mRecyclerView.canScrollVertically(1)) {
10223                        vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
10224                    }
10225                    if (mRecyclerView.canScrollHorizontally(1)) {
10226                        hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
10227                    }
10228                    break;
10229            }
10230            if (vScroll == 0 && hScroll == 0) {
10231                return false;
10232            }
10233            mRecyclerView.smoothScrollBy(hScroll, vScroll);
10234            return true;
10235        }
10236
10237        // called by accessibility delegate
10238        boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
10239            return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
10240                    view, action, args);
10241        }
10242
10243        /**
10244         * Called by AccessibilityDelegate when an accessibility action is requested on one of the
10245         * children of LayoutManager.
10246         * <p>
10247         * Default implementation does not do anything.
10248         *
10249         * @param recycler The Recycler that can be used to convert view positions into adapter
10250         *                 positions
10251         * @param state    The current state of RecyclerView
10252         * @param view     The child view on which the action is performed
10253         * @param action   The action to perform
10254         * @param args     Optional action arguments
10255         * @return true if action is handled
10256         * @see View#performAccessibilityAction(int, android.os.Bundle)
10257         */
10258        public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
10259                int action, Bundle args) {
10260            return false;
10261        }
10262
10263        /**
10264         * Parse the xml attributes to get the most common properties used by layout managers.
10265         *
10266         * @attr ref androidx.recyclerview.R.styleable#RecyclerView_android_orientation
10267         * @attr ref androidx.recyclerview.R.styleable#RecyclerView_spanCount
10268         * @attr ref androidx.recyclerview.R.styleable#RecyclerView_reverseLayout
10269         * @attr ref androidx.recyclerview.R.styleable#RecyclerView_stackFromEnd
10270         *
10271         * @return an object containing the properties as specified in the attrs.
10272         */
10273        public static Properties getProperties(Context context, AttributeSet attrs,
10274                int defStyleAttr, int defStyleRes) {
10275            Properties properties = new Properties();
10276            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
10277                    defStyleAttr, defStyleRes);
10278            properties.orientation = a.getInt(R.styleable.RecyclerView_android_orientation,
10279                    DEFAULT_ORIENTATION);
10280            properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
10281            properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
10282            properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
10283            a.recycle();
10284            return properties;
10285        }
10286
10287        void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
10288            setMeasureSpecs(
10289                    MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
10290                    MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
10291            );
10292        }
10293
10294        /**
10295         * Internal API to allow LayoutManagers to be measured twice.
10296         * <p>
10297         * This is not public because LayoutManagers should be able to handle their layouts in one
10298         * pass but it is very convenient to make existing LayoutManagers support wrapping content
10299         * when both orientations are undefined.
10300         * <p>
10301         * This API will be removed after default LayoutManagers properly implement wrap content in
10302         * non-scroll orientation.
10303         */
10304        boolean shouldMeasureTwice() {
10305            return false;
10306        }
10307
10308        boolean hasFlexibleChildInBothOrientations() {
10309            final int childCount = getChildCount();
10310            for (int i = 0; i < childCount; i++) {
10311                final View child = getChildAt(i);
10312                final ViewGroup.LayoutParams lp = child.getLayoutParams();
10313                if (lp.width < 0 && lp.height < 0) {
10314                    return true;
10315                }
10316            }
10317            return false;
10318        }
10319
10320        /**
10321         * Some general properties that a LayoutManager may want to use.
10322         */
10323        public static class Properties {
10324            /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_android_orientation */
10325            public int orientation;
10326            /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_spanCount */
10327            public int spanCount;
10328            /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_reverseLayout */
10329            public boolean reverseLayout;
10330            /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_stackFromEnd */
10331            public boolean stackFromEnd;
10332        }
10333    }
10334
10335    /**
10336     * An ItemDecoration allows the application to add a special drawing and layout offset
10337     * to specific item views from the adapter's data set. This can be useful for drawing dividers
10338     * between items, highlights, visual grouping boundaries and more.
10339     *
10340     * <p>All ItemDecorations are drawn in the order they were added, before the item
10341     * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
10342     * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
10343     * RecyclerView.State)}.</p>
10344     */
10345    public abstract static class ItemDecoration {
10346        /**
10347         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
10348         * Any content drawn by this method will be drawn before the item views are drawn,
10349         * and will thus appear underneath the views.
10350         *
10351         * @param c Canvas to draw into
10352         * @param parent RecyclerView this ItemDecoration is drawing into
10353         * @param state The current state of RecyclerView
10354         */
10355        public void onDraw(Canvas c, RecyclerView parent, State state) {
10356            onDraw(c, parent);
10357        }
10358
10359        /**
10360         * @deprecated
10361         * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
10362         */
10363        @Deprecated
10364        public void onDraw(Canvas c, RecyclerView parent) {
10365        }
10366
10367        /**
10368         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
10369         * Any content drawn by this method will be drawn after the item views are drawn
10370         * and will thus appear over the views.
10371         *
10372         * @param c Canvas to draw into
10373         * @param parent RecyclerView this ItemDecoration is drawing into
10374         * @param state The current state of RecyclerView.
10375         */
10376        public void onDrawOver(Canvas c, RecyclerView parent, State state) {
10377            onDrawOver(c, parent);
10378        }
10379
10380        /**
10381         * @deprecated
10382         * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
10383         */
10384        @Deprecated
10385        public void onDrawOver(Canvas c, RecyclerView parent) {
10386        }
10387
10388
10389        /**
10390         * @deprecated
10391         * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
10392         */
10393        @Deprecated
10394        public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
10395            outRect.set(0, 0, 0, 0);
10396        }
10397
10398        /**
10399         * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
10400         * the number of pixels that the item view should be inset by, similar to padding or margin.
10401         * The default implementation sets the bounds of outRect to 0 and returns.
10402         *
10403         * <p>
10404         * If this ItemDecoration does not affect the positioning of item views, it should set
10405         * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
10406         * before returning.
10407         *
10408         * <p>
10409         * If you need to access Adapter for additional data, you can call
10410         * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
10411         * View.
10412         *
10413         * @param outRect Rect to receive the output.
10414         * @param view    The child view to decorate
10415         * @param parent  RecyclerView this ItemDecoration is decorating
10416         * @param state   The current state of RecyclerView.
10417         */
10418        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
10419            getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
10420                    parent);
10421        }
10422    }
10423
10424    /**
10425     * An OnItemTouchListener allows the application to intercept touch events in progress at the
10426     * view hierarchy level of the RecyclerView before those touch events are considered for
10427     * RecyclerView's own scrolling behavior.
10428     *
10429     * <p>This can be useful for applications that wish to implement various forms of gestural
10430     * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
10431     * a touch interaction already in progress even if the RecyclerView is already handling that
10432     * gesture stream itself for the purposes of scrolling.</p>
10433     *
10434     * @see SimpleOnItemTouchListener
10435     */
10436    public interface OnItemTouchListener {
10437        /**
10438         * Silently observe and/or take over touch events sent to the RecyclerView
10439         * before they are handled by either the RecyclerView itself or its child views.
10440         *
10441         * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
10442         * in the order in which each listener was added, before any other touch processing
10443         * by the RecyclerView itself or child views occurs.</p>
10444         *
10445         * @param e MotionEvent describing the touch event. All coordinates are in
10446         *          the RecyclerView's coordinate system.
10447         * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
10448         *         to continue with the current behavior and continue observing future events in
10449         *         the gesture.
10450         */
10451        boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
10452
10453        /**
10454         * Process a touch event as part of a gesture that was claimed by returning true from
10455         * a previous call to {@link #onInterceptTouchEvent}.
10456         *
10457         * @param e MotionEvent describing the touch event. All coordinates are in
10458         *          the RecyclerView's coordinate system.
10459         */
10460        void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
10461
10462        /**
10463         * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
10464         * intercept touch events with
10465         * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
10466         *
10467         * @param disallowIntercept True if the child does not want the parent to
10468         *            intercept touch events.
10469         * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
10470         */
10471        void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
10472    }
10473
10474    /**
10475     * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies
10476     * and default return values.
10477     * <p>
10478     * You may prefer to extend this class if you don't need to override all methods. Another
10479     * benefit of using this class is future compatibility. As the interface may change, we'll
10480     * always provide a default implementation on this class so that your code won't break when
10481     * you update to a new version of the support library.
10482     */
10483    public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
10484        @Override
10485        public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
10486            return false;
10487        }
10488
10489        @Override
10490        public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
10491        }
10492
10493        @Override
10494        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
10495        }
10496    }
10497
10498
10499    /**
10500     * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
10501     * has occurred on that RecyclerView.
10502     * <p>
10503     * @see RecyclerView#addOnScrollListener(OnScrollListener)
10504     * @see RecyclerView#clearOnChildAttachStateChangeListeners()
10505     *
10506     */
10507    public abstract static class OnScrollListener {
10508        /**
10509         * Callback method to be invoked when RecyclerView's scroll state changes.
10510         *
10511         * @param recyclerView The RecyclerView whose scroll state has changed.
10512         * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
10513         *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
10514         */
10515        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState){}
10516
10517        /**
10518         * Callback method to be invoked when the RecyclerView has been scrolled. This will be
10519         * called after the scroll has completed.
10520         * <p>
10521         * This callback will also be called if visible item range changes after a layout
10522         * calculation. In that case, dx and dy will be 0.
10523         *
10524         * @param recyclerView The RecyclerView which scrolled.
10525         * @param dx The amount of horizontal scroll.
10526         * @param dy The amount of vertical scroll.
10527         */
10528        public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){}
10529    }
10530
10531    /**
10532     * A RecyclerListener can be set on a RecyclerView to receive messages whenever
10533     * a view is recycled.
10534     *
10535     * @see RecyclerView#setRecyclerListener(RecyclerListener)
10536     */
10537    public interface RecyclerListener {
10538
10539        /**
10540         * This method is called whenever the view in the ViewHolder is recycled.
10541         *
10542         * RecyclerView calls this method right before clearing ViewHolder's internal data and
10543         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
10544         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
10545         * its adapter position.
10546         *
10547         * @param holder The ViewHolder containing the view that was recycled
10548         */
10549        void onViewRecycled(@NonNull ViewHolder holder);
10550    }
10551
10552    /**
10553     * A Listener interface that can be attached to a RecylcerView to get notified
10554     * whenever a ViewHolder is attached to or detached from RecyclerView.
10555     */
10556    public interface OnChildAttachStateChangeListener {
10557
10558        /**
10559         * Called when a view is attached to the RecyclerView.
10560         *
10561         * @param view The View which is attached to the RecyclerView
10562         */
10563        void onChildViewAttachedToWindow(@NonNull View view);
10564
10565        /**
10566         * Called when a view is detached from RecyclerView.
10567         *
10568         * @param view The View which is being detached from the RecyclerView
10569         */
10570        void onChildViewDetachedFromWindow(@NonNull View view);
10571    }
10572
10573    /**
10574     * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
10575     *
10576     * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
10577     * potentially expensive {@link View#findViewById(int)} results.</p>
10578     *
10579     * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
10580     * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
10581     * their own custom ViewHolder implementations to store data that makes binding view contents
10582     * easier. Implementations should assume that individual item views will hold strong references
10583     * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
10584     * strong references to extra off-screen item views for caching purposes</p>
10585     */
10586    public abstract static class ViewHolder {
10587        @NonNull
10588        public final View itemView;
10589        WeakReference<RecyclerView> mNestedRecyclerView;
10590        int mPosition = NO_POSITION;
10591        int mOldPosition = NO_POSITION;
10592        long mItemId = NO_ID;
10593        int mItemViewType = INVALID_TYPE;
10594        int mPreLayoutPosition = NO_POSITION;
10595
10596        // The item that this holder is shadowing during an item change event/animation
10597        ViewHolder mShadowedHolder = null;
10598        // The item that is shadowing this holder during an item change event/animation
10599        ViewHolder mShadowingHolder = null;
10600
10601        /**
10602         * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
10603         * are all valid.
10604         */
10605        static final int FLAG_BOUND = 1 << 0;
10606
10607        /**
10608         * The data this ViewHolder's view reflects is stale and needs to be rebound
10609         * by the adapter. mPosition and mItemId are consistent.
10610         */
10611        static final int FLAG_UPDATE = 1 << 1;
10612
10613        /**
10614         * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
10615         * are not to be trusted and may no longer match the item view type.
10616         * This ViewHolder must be fully rebound to different data.
10617         */
10618        static final int FLAG_INVALID = 1 << 2;
10619
10620        /**
10621         * This ViewHolder points at data that represents an item previously removed from the
10622         * data set. Its view may still be used for things like outgoing animations.
10623         */
10624        static final int FLAG_REMOVED = 1 << 3;
10625
10626        /**
10627         * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
10628         * and is intended to keep views around during animations.
10629         */
10630        static final int FLAG_NOT_RECYCLABLE = 1 << 4;
10631
10632        /**
10633         * This ViewHolder is returned from scrap which means we are expecting an addView call
10634         * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
10635         * the end of the layout pass and then recycled by RecyclerView if it is not added back to
10636         * the RecyclerView.
10637         */
10638        static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
10639
10640        /**
10641         * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
10642         * it unless LayoutManager is replaced.
10643         * It is still fully visible to the LayoutManager.
10644         */
10645        static final int FLAG_IGNORE = 1 << 7;
10646
10647        /**
10648         * When the View is detached form the parent, we set this flag so that we can take correct
10649         * action when we need to remove it or add it back.
10650         */
10651        static final int FLAG_TMP_DETACHED = 1 << 8;
10652
10653        /**
10654         * Set when we can no longer determine the adapter position of this ViewHolder until it is
10655         * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
10656         * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
10657         * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
10658         * re-calculated.
10659         */
10660        static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
10661
10662        /**
10663         * Set when a addChangePayload(null) is called
10664         */
10665        static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
10666
10667        /**
10668         * Used by ItemAnimator when a ViewHolder's position changes
10669         */
10670        static final int FLAG_MOVED = 1 << 11;
10671
10672        /**
10673         * Used by ItemAnimator when a ViewHolder appears in pre-layout
10674         */
10675        static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
10676
10677        static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
10678
10679        /**
10680         * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
10681         * hidden list (as if it was scrap) without being recycled in between.
10682         *
10683         * When a ViewHolder is hidden, there are 2 paths it can be re-used:
10684         *   a) Animation ends, view is recycled and used from the recycle pool.
10685         *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
10686         *
10687         * This flag is used to represent "case b" where the ViewHolder is reused without being
10688         * recycled (thus "bounced" from the hidden list). This state requires special handling
10689         * because the ViewHolder must be added to pre layout maps for animations as if it was
10690         * already there.
10691         */
10692        static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
10693
10694        /**
10695         * Flags that RecyclerView assigned {@link RecyclerViewAccessibilityDelegate
10696         * #getItemDelegate()} in onBindView when app does not provide a delegate.
10697         */
10698        static final int FLAG_SET_A11Y_ITEM_DELEGATE = 1 << 14;
10699
10700        private int mFlags;
10701
10702        private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
10703
10704        List<Object> mPayloads = null;
10705        List<Object> mUnmodifiedPayloads = null;
10706
10707        private int mIsRecyclableCount = 0;
10708
10709        // If non-null, view is currently considered scrap and may be reused for other data by the
10710        // scrap container.
10711        private Recycler mScrapContainer = null;
10712        // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
10713        private boolean mInChangeScrap = false;
10714
10715        // Saves isImportantForAccessibility value for the view item while it's in hidden state and
10716        // marked as unimportant for accessibility.
10717        private int mWasImportantForAccessibilityBeforeHidden =
10718                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10719        // set if we defer the accessibility state change of the view holder
10720        @VisibleForTesting
10721        int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
10722
10723        /**
10724         * Is set when VH is bound from the adapter and cleaned right before it is sent to
10725         * {@link RecycledViewPool}.
10726         */
10727        RecyclerView mOwnerRecyclerView;
10728
10729        public ViewHolder(@NonNull View itemView) {
10730            if (itemView == null) {
10731                throw new IllegalArgumentException("itemView may not be null");
10732            }
10733            this.itemView = itemView;
10734        }
10735
10736        void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
10737            addFlags(ViewHolder.FLAG_REMOVED);
10738            offsetPosition(offset, applyToPreLayout);
10739            mPosition = mNewPosition;
10740        }
10741
10742        void offsetPosition(int offset, boolean applyToPreLayout) {
10743            if (mOldPosition == NO_POSITION) {
10744                mOldPosition = mPosition;
10745            }
10746            if (mPreLayoutPosition == NO_POSITION) {
10747                mPreLayoutPosition = mPosition;
10748            }
10749            if (applyToPreLayout) {
10750                mPreLayoutPosition += offset;
10751            }
10752            mPosition += offset;
10753            if (itemView.getLayoutParams() != null) {
10754                ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
10755            }
10756        }
10757
10758        void clearOldPosition() {
10759            mOldPosition = NO_POSITION;
10760            mPreLayoutPosition = NO_POSITION;
10761        }
10762
10763        void saveOldPosition() {
10764            if (mOldPosition == NO_POSITION) {
10765                mOldPosition = mPosition;
10766            }
10767        }
10768
10769        boolean shouldIgnore() {
10770            return (mFlags & FLAG_IGNORE) != 0;
10771        }
10772
10773        /**
10774         * @deprecated This method is deprecated because its meaning is ambiguous due to the async
10775         * handling of adapter updates. Please use {@link #getLayoutPosition()} or
10776         * {@link #getAdapterPosition()} depending on your use case.
10777         *
10778         * @see #getLayoutPosition()
10779         * @see #getAdapterPosition()
10780         */
10781        @Deprecated
10782        public final int getPosition() {
10783            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10784        }
10785
10786        /**
10787         * Returns the position of the ViewHolder in terms of the latest layout pass.
10788         * <p>
10789         * This position is mostly used by RecyclerView components to be consistent while
10790         * RecyclerView lazily processes adapter updates.
10791         * <p>
10792         * For performance and animation reasons, RecyclerView batches all adapter updates until the
10793         * next layout pass. This may cause mismatches between the Adapter position of the item and
10794         * the position it had in the latest layout calculations.
10795         * <p>
10796         * LayoutManagers should always call this method while doing calculations based on item
10797         * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
10798         * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
10799         * of the item.
10800         * <p>
10801         * If LayoutManager needs to call an external method that requires the adapter position of
10802         * the item, it can use {@link #getAdapterPosition()} or
10803         * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
10804         *
10805         * @return Returns the adapter position of the ViewHolder in the latest layout pass.
10806         * @see #getAdapterPosition()
10807         */
10808        public final int getLayoutPosition() {
10809            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10810        }
10811
10812        /**
10813         * Returns the Adapter position of the item represented by this ViewHolder.
10814         * <p>
10815         * Note that this might be different than the {@link #getLayoutPosition()} if there are
10816         * pending adapter updates but a new layout pass has not happened yet.
10817         * <p>
10818         * RecyclerView does not handle any adapter updates until the next layout traversal. This
10819         * may create temporary inconsistencies between what user sees on the screen and what
10820         * adapter contents have. This inconsistency is not important since it will be less than
10821         * 16ms but it might be a problem if you want to use ViewHolder position to access the
10822         * adapter. Sometimes, you may need to get the exact adapter position to do
10823         * some actions in response to user events. In that case, you should use this method which
10824         * will calculate the Adapter position of the ViewHolder.
10825         * <p>
10826         * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
10827         * next layout pass, the return value of this method will be {@link #NO_POSITION}.
10828         *
10829         * @return The adapter position of the item if it still exists in the adapter.
10830         * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
10831         * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
10832         * layout pass or the ViewHolder has already been recycled.
10833         */
10834        public final int getAdapterPosition() {
10835            if (mOwnerRecyclerView == null) {
10836                return NO_POSITION;
10837            }
10838            return mOwnerRecyclerView.getAdapterPositionFor(this);
10839        }
10840
10841        /**
10842         * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
10843         * to perform animations.
10844         * <p>
10845         * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
10846         * adapter index in the previous layout.
10847         *
10848         * @return The previous adapter index of the Item represented by this ViewHolder or
10849         * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
10850         * complete).
10851         */
10852        public final int getOldPosition() {
10853            return mOldPosition;
10854        }
10855
10856        /**
10857         * Returns The itemId represented by this ViewHolder.
10858         *
10859         * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
10860         * otherwise
10861         */
10862        public final long getItemId() {
10863            return mItemId;
10864        }
10865
10866        /**
10867         * @return The view type of this ViewHolder.
10868         */
10869        public final int getItemViewType() {
10870            return mItemViewType;
10871        }
10872
10873        boolean isScrap() {
10874            return mScrapContainer != null;
10875        }
10876
10877        void unScrap() {
10878            mScrapContainer.unscrapView(this);
10879        }
10880
10881        boolean wasReturnedFromScrap() {
10882            return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
10883        }
10884
10885        void clearReturnedFromScrapFlag() {
10886            mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
10887        }
10888
10889        void clearTmpDetachFlag() {
10890            mFlags = mFlags & ~FLAG_TMP_DETACHED;
10891        }
10892
10893        void stopIgnoring() {
10894            mFlags = mFlags & ~FLAG_IGNORE;
10895        }
10896
10897        void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
10898            mScrapContainer = recycler;
10899            mInChangeScrap = isChangeScrap;
10900        }
10901
10902        boolean isInvalid() {
10903            return (mFlags & FLAG_INVALID) != 0;
10904        }
10905
10906        boolean needsUpdate() {
10907            return (mFlags & FLAG_UPDATE) != 0;
10908        }
10909
10910        boolean isBound() {
10911            return (mFlags & FLAG_BOUND) != 0;
10912        }
10913
10914        boolean isRemoved() {
10915            return (mFlags & FLAG_REMOVED) != 0;
10916        }
10917
10918        boolean hasAnyOfTheFlags(int flags) {
10919            return (mFlags & flags) != 0;
10920        }
10921
10922        boolean isTmpDetached() {
10923            return (mFlags & FLAG_TMP_DETACHED) != 0;
10924        }
10925
10926        boolean isAdapterPositionUnknown() {
10927            return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
10928        }
10929
10930        void setFlags(int flags, int mask) {
10931            mFlags = (mFlags & ~mask) | (flags & mask);
10932        }
10933
10934        void addFlags(int flags) {
10935            mFlags |= flags;
10936        }
10937
10938        void addChangePayload(Object payload) {
10939            if (payload == null) {
10940                addFlags(FLAG_ADAPTER_FULLUPDATE);
10941            } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10942                createPayloadsIfNeeded();
10943                mPayloads.add(payload);
10944            }
10945        }
10946
10947        private void createPayloadsIfNeeded() {
10948            if (mPayloads == null) {
10949                mPayloads = new ArrayList<Object>();
10950                mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
10951            }
10952        }
10953
10954        void clearPayload() {
10955            if (mPayloads != null) {
10956                mPayloads.clear();
10957            }
10958            mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
10959        }
10960
10961        List<Object> getUnmodifiedPayloads() {
10962            if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10963                if (mPayloads == null || mPayloads.size() == 0) {
10964                    // Initial state,  no update being called.
10965                    return FULLUPDATE_PAYLOADS;
10966                }
10967                // there are none-null payloads
10968                return mUnmodifiedPayloads;
10969            } else {
10970                // a full update has been called.
10971                return FULLUPDATE_PAYLOADS;
10972            }
10973        }
10974
10975        void resetInternal() {
10976            mFlags = 0;
10977            mPosition = NO_POSITION;
10978            mOldPosition = NO_POSITION;
10979            mItemId = NO_ID;
10980            mPreLayoutPosition = NO_POSITION;
10981            mIsRecyclableCount = 0;
10982            mShadowedHolder = null;
10983            mShadowingHolder = null;
10984            clearPayload();
10985            mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10986            mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
10987            clearNestedRecyclerViewIfNotNested(this);
10988        }
10989
10990        /**
10991         * Called when the child view enters the hidden state
10992         */
10993        private void onEnteredHiddenState(RecyclerView parent) {
10994            // While the view item is in hidden state, make it invisible for the accessibility.
10995            if (mPendingAccessibilityState != PENDING_ACCESSIBILITY_STATE_NOT_SET) {
10996                mWasImportantForAccessibilityBeforeHidden = mPendingAccessibilityState;
10997            } else {
10998                mWasImportantForAccessibilityBeforeHidden =
10999                        ViewCompat.getImportantForAccessibility(itemView);
11000            }
11001            parent.setChildImportantForAccessibilityInternal(this,
11002                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
11003        }
11004
11005        /**
11006         * Called when the child view leaves the hidden state
11007         */
11008        private void onLeftHiddenState(RecyclerView parent) {
11009            parent.setChildImportantForAccessibilityInternal(this,
11010                    mWasImportantForAccessibilityBeforeHidden);
11011            mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
11012        }
11013
11014        @Override
11015        public String toString() {
11016            final StringBuilder sb = new StringBuilder("ViewHolder{"
11017                    + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
11018                    + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
11019            if (isScrap()) {
11020                sb.append(" scrap ")
11021                        .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
11022            }
11023            if (isInvalid()) sb.append(" invalid");
11024            if (!isBound()) sb.append(" unbound");
11025            if (needsUpdate()) sb.append(" update");
11026            if (isRemoved()) sb.append(" removed");
11027            if (shouldIgnore()) sb.append(" ignored");
11028            if (isTmpDetached()) sb.append(" tmpDetached");
11029            if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
11030            if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
11031
11032            if (itemView.getParent() == null) sb.append(" no parent");
11033            sb.append("}");
11034            return sb.toString();
11035        }
11036
11037        /**
11038         * Informs the recycler whether this item can be recycled. Views which are not
11039         * recyclable will not be reused for other items until setIsRecyclable() is
11040         * later set to true. Calls to setIsRecyclable() should always be paired (one
11041         * call to setIsRecyclabe(false) should always be matched with a later call to
11042         * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
11043         * reference-counted.
11044         *
11045         * @param recyclable Whether this item is available to be recycled. Default value
11046         * is true.
11047         *
11048         * @see #isRecyclable()
11049         */
11050        public final void setIsRecyclable(boolean recyclable) {
11051            mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
11052            if (mIsRecyclableCount < 0) {
11053                mIsRecyclableCount = 0;
11054                if (DEBUG) {
11055                    throw new RuntimeException("isRecyclable decremented below 0: "
11056                            + "unmatched pair of setIsRecyable() calls for " + this);
11057                }
11058                Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: "
11059                        + "unmatched pair of setIsRecyable() calls for " + this);
11060            } else if (!recyclable && mIsRecyclableCount == 1) {
11061                mFlags |= FLAG_NOT_RECYCLABLE;
11062            } else if (recyclable && mIsRecyclableCount == 0) {
11063                mFlags &= ~FLAG_NOT_RECYCLABLE;
11064            }
11065            if (DEBUG) {
11066                Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
11067            }
11068        }
11069
11070        /**
11071         * @return true if this item is available to be recycled, false otherwise.
11072         *
11073         * @see #setIsRecyclable(boolean)
11074         */
11075        public final boolean isRecyclable() {
11076            return (mFlags & FLAG_NOT_RECYCLABLE) == 0
11077                    && !ViewCompat.hasTransientState(itemView);
11078        }
11079
11080        /**
11081         * Returns whether we have animations referring to this view holder or not.
11082         * This is similar to isRecyclable flag but does not check transient state.
11083         */
11084        private boolean shouldBeKeptAsChild() {
11085            return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
11086        }
11087
11088        /**
11089         * @return True if ViewHolder is not referenced by RecyclerView animations but has
11090         * transient state which will prevent it from being recycled.
11091         */
11092        private boolean doesTransientStatePreventRecycling() {
11093            return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView);
11094        }
11095
11096        boolean isUpdated() {
11097            return (mFlags & FLAG_UPDATE) != 0;
11098        }
11099    }
11100
11101    /**
11102     * This method is here so that we can control the important for a11y changes and test it.
11103     */
11104    @VisibleForTesting
11105    boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
11106            int importantForAccessibility) {
11107        if (isComputingLayout()) {
11108            viewHolder.mPendingAccessibilityState = importantForAccessibility;
11109            mPendingAccessibilityImportanceChange.add(viewHolder);
11110            return false;
11111        }
11112        ViewCompat.setImportantForAccessibility(viewHolder.itemView, importantForAccessibility);
11113        return true;
11114    }
11115
11116    void dispatchPendingImportantForAccessibilityChanges() {
11117        for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
11118            ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
11119            if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
11120                continue;
11121            }
11122            int state = viewHolder.mPendingAccessibilityState;
11123            if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
11124                //noinspection WrongConstant
11125                ViewCompat.setImportantForAccessibility(viewHolder.itemView, state);
11126                viewHolder.mPendingAccessibilityState =
11127                        ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
11128            }
11129        }
11130        mPendingAccessibilityImportanceChange.clear();
11131    }
11132
11133    int getAdapterPositionFor(ViewHolder viewHolder) {
11134        if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
11135                | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
11136                || !viewHolder.isBound()) {
11137            return RecyclerView.NO_POSITION;
11138        }
11139        return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
11140    }
11141
11142    @VisibleForTesting
11143    void initFastScroller(StateListDrawable verticalThumbDrawable,
11144            Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable,
11145            Drawable horizontalTrackDrawable) {
11146        if (verticalThumbDrawable == null || verticalTrackDrawable == null
11147                || horizontalThumbDrawable == null || horizontalTrackDrawable == null) {
11148            throw new IllegalArgumentException(
11149                "Trying to set fast scroller without both required drawables." + exceptionLabel());
11150        }
11151
11152        Resources resources = getContext().getResources();
11153        new FastScroller(this, verticalThumbDrawable, verticalTrackDrawable,
11154                horizontalThumbDrawable, horizontalTrackDrawable,
11155                resources.getDimensionPixelSize(R.dimen.fastscroll_default_thickness),
11156                resources.getDimensionPixelSize(R.dimen.fastscroll_minimum_range),
11157                resources.getDimensionPixelOffset(R.dimen.fastscroll_margin));
11158    }
11159
11160    // NestedScrollingChild
11161
11162    @Override
11163    public void setNestedScrollingEnabled(boolean enabled) {
11164        getScrollingChildHelper().setNestedScrollingEnabled(enabled);
11165    }
11166
11167    @Override
11168    public boolean isNestedScrollingEnabled() {
11169        return getScrollingChildHelper().isNestedScrollingEnabled();
11170    }
11171
11172    @Override
11173    public boolean startNestedScroll(int axes) {
11174        return getScrollingChildHelper().startNestedScroll(axes);
11175    }
11176
11177    @Override
11178    public boolean startNestedScroll(int axes, int type) {
11179        return getScrollingChildHelper().startNestedScroll(axes, type);
11180    }
11181
11182    @Override
11183    public void stopNestedScroll() {
11184        getScrollingChildHelper().stopNestedScroll();
11185    }
11186
11187    @Override
11188    public void stopNestedScroll(int type) {
11189        getScrollingChildHelper().stopNestedScroll(type);
11190    }
11191
11192    @Override
11193    public boolean hasNestedScrollingParent() {
11194        return getScrollingChildHelper().hasNestedScrollingParent();
11195    }
11196
11197    @Override
11198    public boolean hasNestedScrollingParent(int type) {
11199        return getScrollingChildHelper().hasNestedScrollingParent(type);
11200    }
11201
11202    @Override
11203    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
11204            int dyUnconsumed, int[] offsetInWindow) {
11205        return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
11206                dxUnconsumed, dyUnconsumed, offsetInWindow);
11207    }
11208
11209    @Override
11210    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
11211            int dyUnconsumed, int[] offsetInWindow, int type) {
11212        return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
11213                dxUnconsumed, dyUnconsumed, offsetInWindow, type);
11214    }
11215
11216    @Override
11217    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
11218        return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
11219    }
11220
11221    @Override
11222    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
11223            int type) {
11224        return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow,
11225                type);
11226    }
11227
11228    @Override
11229    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
11230        return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
11231    }
11232
11233    @Override
11234    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
11235        return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
11236    }
11237
11238    /**
11239     * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
11240     * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
11241     * to create their own subclass of this <code>LayoutParams</code> class
11242     * to store any additional required per-child view metadata about the layout.
11243     */
11244    public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
11245        ViewHolder mViewHolder;
11246        final Rect mDecorInsets = new Rect();
11247        boolean mInsetsDirty = true;
11248        // Flag is set to true if the view is bound while it is detached from RV.
11249        // In this case, we need to manually call invalidate after view is added to guarantee that
11250        // invalidation is populated through the View hierarchy
11251        boolean mPendingInvalidate = false;
11252
11253        public LayoutParams(Context c, AttributeSet attrs) {
11254            super(c, attrs);
11255        }
11256
11257        public LayoutParams(int width, int height) {
11258            super(width, height);
11259        }
11260
11261        public LayoutParams(MarginLayoutParams source) {
11262            super(source);
11263        }
11264
11265        public LayoutParams(ViewGroup.LayoutParams source) {
11266            super(source);
11267        }
11268
11269        public LayoutParams(LayoutParams source) {
11270            super((ViewGroup.LayoutParams) source);
11271        }
11272
11273        /**
11274         * Returns true if the view this LayoutParams is attached to needs to have its content
11275         * updated from the corresponding adapter.
11276         *
11277         * @return true if the view should have its content updated
11278         */
11279        public boolean viewNeedsUpdate() {
11280            return mViewHolder.needsUpdate();
11281        }
11282
11283        /**
11284         * Returns true if the view this LayoutParams is attached to is now representing
11285         * potentially invalid data. A LayoutManager should scrap/recycle it.
11286         *
11287         * @return true if the view is invalid
11288         */
11289        public boolean isViewInvalid() {
11290            return mViewHolder.isInvalid();
11291        }
11292
11293        /**
11294         * Returns true if the adapter data item corresponding to the view this LayoutParams
11295         * is attached to has been removed from the data set. A LayoutManager may choose to
11296         * treat it differently in order to animate its outgoing or disappearing state.
11297         *
11298         * @return true if the item the view corresponds to was removed from the data set
11299         */
11300        public boolean isItemRemoved() {
11301            return mViewHolder.isRemoved();
11302        }
11303
11304        /**
11305         * Returns true if the adapter data item corresponding to the view this LayoutParams
11306         * is attached to has been changed in the data set. A LayoutManager may choose to
11307         * treat it differently in order to animate its changing state.
11308         *
11309         * @return true if the item the view corresponds to was changed in the data set
11310         */
11311        public boolean isItemChanged() {
11312            return mViewHolder.isUpdated();
11313        }
11314
11315        /**
11316         * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
11317         */
11318        @Deprecated
11319        public int getViewPosition() {
11320            return mViewHolder.getPosition();
11321        }
11322
11323        /**
11324         * Returns the adapter position that the view this LayoutParams is attached to corresponds
11325         * to as of latest layout calculation.
11326         *
11327         * @return the adapter position this view as of latest layout pass
11328         */
11329        public int getViewLayoutPosition() {
11330            return mViewHolder.getLayoutPosition();
11331        }
11332
11333        /**
11334         * Returns the up-to-date adapter position that the view this LayoutParams is attached to
11335         * corresponds to.
11336         *
11337         * @return the up-to-date adapter position this view. It may return
11338         * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
11339         * its up-to-date position cannot be calculated.
11340         */
11341        public int getViewAdapterPosition() {
11342            return mViewHolder.getAdapterPosition();
11343        }
11344    }
11345
11346    /**
11347     * Observer base class for watching changes to an {@link Adapter}.
11348     * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
11349     */
11350    public abstract static class AdapterDataObserver {
11351        public void onChanged() {
11352            // Do nothing
11353        }
11354
11355        public void onItemRangeChanged(int positionStart, int itemCount) {
11356            // do nothing
11357        }
11358
11359        public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
11360            // fallback to onItemRangeChanged(positionStart, itemCount) if app
11361            // does not override this method.
11362            onItemRangeChanged(positionStart, itemCount);
11363        }
11364
11365        public void onItemRangeInserted(int positionStart, int itemCount) {
11366            // do nothing
11367        }
11368
11369        public void onItemRangeRemoved(int positionStart, int itemCount) {
11370            // do nothing
11371        }
11372
11373        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
11374            // do nothing
11375        }
11376    }
11377
11378    /**
11379     * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
11380     * provides methods to trigger a programmatic scroll.</p>
11381     *
11382     * @see LinearSmoothScroller
11383     */
11384    public abstract static class SmoothScroller {
11385
11386        private int mTargetPosition = RecyclerView.NO_POSITION;
11387
11388        private RecyclerView mRecyclerView;
11389
11390        private LayoutManager mLayoutManager;
11391
11392        private boolean mPendingInitialRun;
11393
11394        private boolean mRunning;
11395
11396        private View mTargetView;
11397
11398        private final Action mRecyclingAction;
11399
11400        public SmoothScroller() {
11401            mRecyclingAction = new Action(0, 0);
11402        }
11403
11404        /**
11405         * Starts a smooth scroll for the given target position.
11406         * <p>In each animation step, {@link RecyclerView} will check
11407         * for the target view and call either
11408         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
11409         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
11410         * SmoothScroller is stopped.</p>
11411         *
11412         * <p>Note that if RecyclerView finds the target view, it will automatically stop the
11413         * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
11414         * stop calling SmoothScroller in each animation step.</p>
11415         */
11416        void start(RecyclerView recyclerView, LayoutManager layoutManager) {
11417            mRecyclerView = recyclerView;
11418            mLayoutManager = layoutManager;
11419            if (mTargetPosition == RecyclerView.NO_POSITION) {
11420                throw new IllegalArgumentException("Invalid target position");
11421            }
11422            mRecyclerView.mState.mTargetPosition = mTargetPosition;
11423            mRunning = true;
11424            mPendingInitialRun = true;
11425            mTargetView = findViewByPosition(getTargetPosition());
11426            onStart();
11427            mRecyclerView.mViewFlinger.postOnAnimation();
11428        }
11429
11430        public void setTargetPosition(int targetPosition) {
11431            mTargetPosition = targetPosition;
11432        }
11433
11434        /**
11435         * Compute the scroll vector for a given target position.
11436         * <p>
11437         * This method can return null if the layout manager cannot calculate a scroll vector
11438         * for the given position (e.g. it has no current scroll position).
11439         *
11440         * @param targetPosition the position to which the scroller is scrolling
11441         *
11442         * @return the scroll vector for a given target position
11443         */
11444        @Nullable
11445        public PointF computeScrollVectorForPosition(int targetPosition) {
11446            LayoutManager layoutManager = getLayoutManager();
11447            if (layoutManager instanceof ScrollVectorProvider) {
11448                return ((ScrollVectorProvider) layoutManager)
11449                        .computeScrollVectorForPosition(targetPosition);
11450            }
11451            Log.w(TAG, "You should override computeScrollVectorForPosition when the LayoutManager"
11452                    + " does not implement " + ScrollVectorProvider.class.getCanonicalName());
11453            return null;
11454        }
11455
11456        /**
11457         * @return The LayoutManager to which this SmoothScroller is attached. Will return
11458         * <code>null</code> after the SmoothScroller is stopped.
11459         */
11460        @Nullable
11461        public LayoutManager getLayoutManager() {
11462            return mLayoutManager;
11463        }
11464
11465        /**
11466         * Stops running the SmoothScroller in each animation callback. Note that this does not
11467         * cancel any existing {@link Action} updated by
11468         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
11469         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
11470         */
11471        protected final void stop() {
11472            if (!mRunning) {
11473                return;
11474            }
11475            onStop();
11476            mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
11477            mTargetView = null;
11478            mTargetPosition = RecyclerView.NO_POSITION;
11479            mPendingInitialRun = false;
11480            mRunning = false;
11481            // trigger a cleanup
11482            mLayoutManager.onSmoothScrollerStopped(this);
11483            // clear references to avoid any potential leak by a custom smooth scroller
11484            mLayoutManager = null;
11485            mRecyclerView = null;
11486        }
11487
11488        /**
11489         * Returns true if SmoothScroller has been started but has not received the first
11490         * animation
11491         * callback yet.
11492         *
11493         * @return True if this SmoothScroller is waiting to start
11494         */
11495        public boolean isPendingInitialRun() {
11496            return mPendingInitialRun;
11497        }
11498
11499
11500        /**
11501         * @return True if SmoothScroller is currently active
11502         */
11503        public boolean isRunning() {
11504            return mRunning;
11505        }
11506
11507        /**
11508         * Returns the adapter position of the target item
11509         *
11510         * @return Adapter position of the target item or
11511         * {@link RecyclerView#NO_POSITION} if no target view is set.
11512         */
11513        public int getTargetPosition() {
11514            return mTargetPosition;
11515        }
11516
11517        private void onAnimation(int dx, int dy) {
11518            // TODO(b/72745539): If mRunning is false, we call stop, which is a no op if mRunning
11519            // is false. Also, if recyclerView is null, we call stop, and stop assumes recyclerView
11520            // is not null (as does the code following this block).  This should be cleaned up.
11521            final RecyclerView recyclerView = mRecyclerView;
11522            if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
11523                stop();
11524            }
11525
11526            // The following if block exists to have the LayoutManager scroll 1 pixel in the correct
11527            // direction in order to cause the LayoutManager to draw two pages worth of views so
11528            // that the target view may be found before scrolling any further.  This is done to
11529            // prevent an initial scroll distance from scrolling past the view, which causes a
11530            // jittery looking animation. (This block also necessarily sets mPendingInitialRun to
11531            // false if it was true).
11532            if (mPendingInitialRun && mTargetView == null && mLayoutManager != null) {
11533                PointF pointF = computeScrollVectorForPosition(mTargetPosition);
11534                if (pointF != null && (pointF.x != 0 || pointF.y != 0)) {
11535                    recyclerView.scrollStep(
11536                            (int) Math.signum(pointF.x),
11537                            (int) Math.signum(pointF.y),
11538                            null);
11539                }
11540            }
11541
11542            mPendingInitialRun = false;
11543
11544            if (mTargetView != null) {
11545                // verify target position
11546                if (getChildPosition(mTargetView) == mTargetPosition) {
11547                    onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
11548                    mRecyclingAction.runIfNecessary(recyclerView);
11549                    stop();
11550                } else {
11551                    Log.e(TAG, "Passed over target position while smooth scrolling.");
11552                    mTargetView = null;
11553                }
11554            }
11555            if (mRunning) {
11556                onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
11557                boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
11558                mRecyclingAction.runIfNecessary(recyclerView);
11559                if (hadJumpTarget) {
11560                    // It is not stopped so needs to be restarted
11561                    if (mRunning) {
11562                        mPendingInitialRun = true;
11563                        recyclerView.mViewFlinger.postOnAnimation();
11564                    } else {
11565                        // TODO(b/72745539): stop() is a no-op if mRunning is false, so this can be
11566                        // removed.
11567                        stop(); // done
11568                    }
11569                }
11570            }
11571        }
11572
11573        /**
11574         * @see RecyclerView#getChildLayoutPosition(android.view.View)
11575         */
11576        public int getChildPosition(View view) {
11577            return mRecyclerView.getChildLayoutPosition(view);
11578        }
11579
11580        /**
11581         * @see RecyclerView.LayoutManager#getChildCount()
11582         */
11583        public int getChildCount() {
11584            return mRecyclerView.mLayout.getChildCount();
11585        }
11586
11587        /**
11588         * @see RecyclerView.LayoutManager#findViewByPosition(int)
11589         */
11590        public View findViewByPosition(int position) {
11591            return mRecyclerView.mLayout.findViewByPosition(position);
11592        }
11593
11594        /**
11595         * @see RecyclerView#scrollToPosition(int)
11596         * @deprecated Use {@link Action#jumpTo(int)}.
11597         */
11598        @Deprecated
11599        public void instantScrollToPosition(int position) {
11600            mRecyclerView.scrollToPosition(position);
11601        }
11602
11603        protected void onChildAttachedToWindow(View child) {
11604            if (getChildPosition(child) == getTargetPosition()) {
11605                mTargetView = child;
11606                if (DEBUG) {
11607                    Log.d(TAG, "smooth scroll target view has been attached");
11608                }
11609            }
11610        }
11611
11612        /**
11613         * Normalizes the vector.
11614         * @param scrollVector The vector that points to the target scroll position
11615         */
11616        protected void normalize(PointF scrollVector) {
11617            final float magnitude = (float) Math.sqrt(scrollVector.x * scrollVector.x
11618                    + scrollVector.y * scrollVector.y);
11619            scrollVector.x /= magnitude;
11620            scrollVector.y /= magnitude;
11621        }
11622
11623        /**
11624         * Called when smooth scroll is started. This might be a good time to do setup.
11625         */
11626        protected abstract void onStart();
11627
11628        /**
11629         * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
11630         * @see #stop()
11631         */
11632        protected abstract void onStop();
11633
11634        /**
11635         * <p>RecyclerView will call this method each time it scrolls until it can find the target
11636         * position in the layout.</p>
11637         * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
11638         * provided {@link Action} to define the next scroll.</p>
11639         *
11640         * @param dx        Last scroll amount horizontally
11641         * @param dy        Last scroll amount vertically
11642         * @param state     Transient state of RecyclerView
11643         * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
11644         *                  update this object.
11645         */
11646        protected abstract void onSeekTargetStep(@Px int dx, @Px int dy, State state,
11647                Action action);
11648
11649        /**
11650         * Called when the target position is laid out. This is the last callback SmoothScroller
11651         * will receive and it should update the provided {@link Action} to define the scroll
11652         * details towards the target view.
11653         * @param targetView    The view element which render the target position.
11654         * @param state         Transient state of RecyclerView
11655         * @param action        Action instance that you should update to define final scroll action
11656         *                      towards the targetView
11657         */
11658        protected abstract void onTargetFound(View targetView, State state, Action action);
11659
11660        /**
11661         * Holds information about a smooth scroll request by a {@link SmoothScroller}.
11662         */
11663        public static class Action {
11664
11665            public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
11666
11667            private int mDx;
11668
11669            private int mDy;
11670
11671            private int mDuration;
11672
11673            private int mJumpToPosition = NO_POSITION;
11674
11675            private Interpolator mInterpolator;
11676
11677            private boolean mChanged = false;
11678
11679            // we track this variable to inform custom implementer if they are updating the action
11680            // in every animation callback
11681            private int mConsecutiveUpdates = 0;
11682
11683            /**
11684             * @param dx Pixels to scroll horizontally
11685             * @param dy Pixels to scroll vertically
11686             */
11687            public Action(@Px int dx, @Px int dy) {
11688                this(dx, dy, UNDEFINED_DURATION, null);
11689            }
11690
11691            /**
11692             * @param dx       Pixels to scroll horizontally
11693             * @param dy       Pixels to scroll vertically
11694             * @param duration Duration of the animation in milliseconds
11695             */
11696            public Action(@Px int dx, @Px int dy, int duration) {
11697                this(dx, dy, duration, null);
11698            }
11699
11700            /**
11701             * @param dx           Pixels to scroll horizontally
11702             * @param dy           Pixels to scroll vertically
11703             * @param duration     Duration of the animation in milliseconds
11704             * @param interpolator Interpolator to be used when calculating scroll position in each
11705             *                     animation step
11706             */
11707            public Action(@Px int dx, @Px int dy, int duration,
11708                    @Nullable Interpolator interpolator) {
11709                mDx = dx;
11710                mDy = dy;
11711                mDuration = duration;
11712                mInterpolator = interpolator;
11713            }
11714
11715            /**
11716             * Instead of specifying pixels to scroll, use the target position to jump using
11717             * {@link RecyclerView#scrollToPosition(int)}.
11718             * <p>
11719             * You may prefer using this method if scroll target is really far away and you prefer
11720             * to jump to a location and smooth scroll afterwards.
11721             * <p>
11722             * Note that calling this method takes priority over other update methods such as
11723             * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
11724             * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
11725             * {@link #jumpTo(int)}, the other changes will not be considered for this animation
11726             * frame.
11727             *
11728             * @param targetPosition The target item position to scroll to using instant scrolling.
11729             */
11730            public void jumpTo(int targetPosition) {
11731                mJumpToPosition = targetPosition;
11732            }
11733
11734            boolean hasJumpTarget() {
11735                return mJumpToPosition >= 0;
11736            }
11737
11738            void runIfNecessary(RecyclerView recyclerView) {
11739                if (mJumpToPosition >= 0) {
11740                    final int position = mJumpToPosition;
11741                    mJumpToPosition = NO_POSITION;
11742                    recyclerView.jumpToPositionForSmoothScroller(position);
11743                    mChanged = false;
11744                    return;
11745                }
11746                if (mChanged) {
11747                    validate();
11748                    if (mInterpolator == null) {
11749                        if (mDuration == UNDEFINED_DURATION) {
11750                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
11751                        } else {
11752                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
11753                        }
11754                    } else {
11755                        recyclerView.mViewFlinger.smoothScrollBy(
11756                                mDx, mDy, mDuration, mInterpolator);
11757                    }
11758                    mConsecutiveUpdates++;
11759                    if (mConsecutiveUpdates > 10) {
11760                        // A new action is being set in every animation step. This looks like a bad
11761                        // implementation. Inform developer.
11762                        Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
11763                                + " you are not changing it unless necessary");
11764                    }
11765                    mChanged = false;
11766                } else {
11767                    mConsecutiveUpdates = 0;
11768                }
11769            }
11770
11771            private void validate() {
11772                if (mInterpolator != null && mDuration < 1) {
11773                    throw new IllegalStateException("If you provide an interpolator, you must"
11774                            + " set a positive duration");
11775                } else if (mDuration < 1) {
11776                    throw new IllegalStateException("Scroll duration must be a positive number");
11777                }
11778            }
11779
11780            @Px
11781            public int getDx() {
11782                return mDx;
11783            }
11784
11785            public void setDx(@Px int dx) {
11786                mChanged = true;
11787                mDx = dx;
11788            }
11789
11790            @Px
11791            public int getDy() {
11792                return mDy;
11793            }
11794
11795            public void setDy(@Px int dy) {
11796                mChanged = true;
11797                mDy = dy;
11798            }
11799
11800            public int getDuration() {
11801                return mDuration;
11802            }
11803
11804            public void setDuration(int duration) {
11805                mChanged = true;
11806                mDuration = duration;
11807            }
11808
11809            @Nullable
11810            public Interpolator getInterpolator() {
11811                return mInterpolator;
11812            }
11813
11814            /**
11815             * Sets the interpolator to calculate scroll steps
11816             * @param interpolator The interpolator to use. If you specify an interpolator, you must
11817             *                     also set the duration.
11818             * @see #setDuration(int)
11819             */
11820            public void setInterpolator(@Nullable Interpolator interpolator) {
11821                mChanged = true;
11822                mInterpolator = interpolator;
11823            }
11824
11825            /**
11826             * Updates the action with given parameters.
11827             * @param dx Pixels to scroll horizontally
11828             * @param dy Pixels to scroll vertically
11829             * @param duration Duration of the animation in milliseconds
11830             * @param interpolator Interpolator to be used when calculating scroll position in each
11831             *                     animation step
11832             */
11833            public void update(@Px int dx, @Px int dy, int duration,
11834                    @Nullable Interpolator interpolator) {
11835                mDx = dx;
11836                mDy = dy;
11837                mDuration = duration;
11838                mInterpolator = interpolator;
11839                mChanged = true;
11840            }
11841        }
11842
11843        /**
11844         * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
11845         * to provide a hint to a {@link SmoothScroller} about the location of the target position.
11846         */
11847        public interface ScrollVectorProvider {
11848            /**
11849             * Should calculate the vector that points to the direction where the target position
11850             * can be found.
11851             * <p>
11852             * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
11853             * the target position.
11854             * <p>
11855             * The magnitude of the vector is not important. It is always normalized before being
11856             * used by the {@link LinearSmoothScroller}.
11857             * <p>
11858             * LayoutManager should not check whether the position exists in the adapter or not.
11859             *
11860             * @param targetPosition the target position to which the returned vector should point
11861             *
11862             * @return the scroll vector for a given position.
11863             */
11864            PointF computeScrollVectorForPosition(int targetPosition);
11865        }
11866    }
11867
11868    static class AdapterDataObservable extends Observable<AdapterDataObserver> {
11869        public boolean hasObservers() {
11870            return !mObservers.isEmpty();
11871        }
11872
11873        public void notifyChanged() {
11874            // since onChanged() is implemented by the app, it could do anything, including
11875            // removing itself from {@link mObservers} - and that could cause problems if
11876            // an iterator is used on the ArrayList {@link mObservers}.
11877            // to avoid such problems, just march thru the list in the reverse order.
11878            for (int i = mObservers.size() - 1; i >= 0; i--) {
11879                mObservers.get(i).onChanged();
11880            }
11881        }
11882
11883        public void notifyItemRangeChanged(int positionStart, int itemCount) {
11884            notifyItemRangeChanged(positionStart, itemCount, null);
11885        }
11886
11887        public void notifyItemRangeChanged(int positionStart, int itemCount,
11888                @Nullable Object payload) {
11889            // since onItemRangeChanged() is implemented by the app, it could do anything, including
11890            // removing itself from {@link mObservers} - and that could cause problems if
11891            // an iterator is used on the ArrayList {@link mObservers}.
11892            // to avoid such problems, just march thru the list in the reverse order.
11893            for (int i = mObservers.size() - 1; i >= 0; i--) {
11894                mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
11895            }
11896        }
11897
11898        public void notifyItemRangeInserted(int positionStart, int itemCount) {
11899            // since onItemRangeInserted() is implemented by the app, it could do anything,
11900            // including removing itself from {@link mObservers} - and that could cause problems if
11901            // an iterator is used on the ArrayList {@link mObservers}.
11902            // to avoid such problems, just march thru the list in the reverse order.
11903            for (int i = mObservers.size() - 1; i >= 0; i--) {
11904                mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
11905            }
11906        }
11907
11908        public void notifyItemRangeRemoved(int positionStart, int itemCount) {
11909            // since onItemRangeRemoved() is implemented by the app, it could do anything, including
11910            // removing itself from {@link mObservers} - and that could cause problems if
11911            // an iterator is used on the ArrayList {@link mObservers}.
11912            // to avoid such problems, just march thru the list in the reverse order.
11913            for (int i = mObservers.size() - 1; i >= 0; i--) {
11914                mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
11915            }
11916        }
11917
11918        public void notifyItemMoved(int fromPosition, int toPosition) {
11919            for (int i = mObservers.size() - 1; i >= 0; i--) {
11920                mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
11921            }
11922        }
11923    }
11924
11925    /**
11926     * This is public so that the CREATOR can be accessed on cold launch.
11927     * @hide
11928     */
11929    @RestrictTo(LIBRARY_GROUP)
11930    public static class SavedState extends AbsSavedState {
11931
11932        Parcelable mLayoutState;
11933
11934        /**
11935         * called by CREATOR
11936         */
11937        SavedState(Parcel in, ClassLoader loader) {
11938            super(in, loader);
11939            mLayoutState = in.readParcelable(
11940                    loader != null ? loader : LayoutManager.class.getClassLoader());
11941        }
11942
11943        /**
11944         * Called by onSaveInstanceState
11945         */
11946        SavedState(Parcelable superState) {
11947            super(superState);
11948        }
11949
11950        @Override
11951        public void writeToParcel(Parcel dest, int flags) {
11952            super.writeToParcel(dest, flags);
11953            dest.writeParcelable(mLayoutState, 0);
11954        }
11955
11956        void copyFrom(SavedState other) {
11957            mLayoutState = other.mLayoutState;
11958        }
11959
11960        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
11961            @Override
11962            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
11963                return new SavedState(in, loader);
11964            }
11965
11966            @Override
11967            public SavedState createFromParcel(Parcel in) {
11968                return new SavedState(in, null);
11969            }
11970
11971            @Override
11972            public SavedState[] newArray(int size) {
11973                return new SavedState[size];
11974            }
11975        };
11976    }
11977
11978    /**
11979     * <p>Contains useful information about the current RecyclerView state like target scroll
11980     * position or view focus. State object can also keep arbitrary data, identified by resource
11981     * ids.</p>
11982     * <p>Often times, RecyclerView components will need to pass information between each other.
11983     * To provide a well defined data bus between components, RecyclerView passes the same State
11984     * object to component callbacks and these components can use it to exchange data.</p>
11985     * <p>If you implement custom components, you can use State's put/get/remove methods to pass
11986     * data between your components without needing to manage their lifecycles.</p>
11987     */
11988    public static class State {
11989        static final int STEP_START = 1;
11990        static final int STEP_LAYOUT = 1 << 1;
11991        static final int STEP_ANIMATIONS = 1 << 2;
11992
11993        void assertLayoutStep(int accepted) {
11994            if ((accepted & mLayoutStep) == 0) {
11995                throw new IllegalStateException("Layout state should be one of "
11996                        + Integer.toBinaryString(accepted) + " but it is "
11997                        + Integer.toBinaryString(mLayoutStep));
11998            }
11999        }
12000
12001
12002        /** Owned by SmoothScroller */
12003        private int mTargetPosition = RecyclerView.NO_POSITION;
12004
12005        private SparseArray<Object> mData;
12006
12007        ////////////////////////////////////////////////////////////////////////////////////////////
12008        // Fields below are carried from one layout pass to the next
12009        ////////////////////////////////////////////////////////////////////////////////////////////
12010
12011        /**
12012         * Number of items adapter had in the previous layout.
12013         */
12014        int mPreviousLayoutItemCount = 0;
12015
12016        /**
12017         * Number of items that were NOT laid out but has been deleted from the adapter after the
12018         * previous layout.
12019         */
12020        int mDeletedInvisibleItemCountSincePreviousLayout = 0;
12021
12022        ////////////////////////////////////////////////////////////////////////////////////////////
12023        // Fields below must be updated or cleared before they are used (generally before a pass)
12024        ////////////////////////////////////////////////////////////////////////////////////////////
12025
12026        @IntDef(flag = true, value = {
12027                STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
12028        })
12029        @Retention(RetentionPolicy.SOURCE)
12030        @interface LayoutState {}
12031
12032        @LayoutState
12033        int mLayoutStep = STEP_START;
12034
12035        /**
12036         * Number of items adapter has.
12037         */
12038        int mItemCount = 0;
12039
12040        boolean mStructureChanged = false;
12041
12042        /**
12043         * True if the associated {@link RecyclerView} is in the pre-layout step where it is having
12044         * its {@link LayoutManager} layout items where they will be at the beginning of a set of
12045         * predictive item animations.
12046         */
12047        boolean mInPreLayout = false;
12048
12049        boolean mTrackOldChangeHolders = false;
12050
12051        boolean mIsMeasuring = false;
12052
12053        ////////////////////////////////////////////////////////////////////////////////////////////
12054        // Fields below are always reset outside of the pass (or passes) that use them
12055        ////////////////////////////////////////////////////////////////////////////////////////////
12056
12057        boolean mRunSimpleAnimations = false;
12058
12059        boolean mRunPredictiveAnimations = false;
12060
12061        /**
12062         * This data is saved before a layout calculation happens. After the layout is finished,
12063         * if the previously focused view has been replaced with another view for the same item, we
12064         * move the focus to the new item automatically.
12065         */
12066        int mFocusedItemPosition;
12067        long mFocusedItemId;
12068        // when a sub child has focus, record its id and see if we can directly request focus on
12069        // that one instead
12070        int mFocusedSubChildId;
12071
12072        int mRemainingScrollHorizontal;
12073        int mRemainingScrollVertical;
12074
12075        ////////////////////////////////////////////////////////////////////////////////////////////
12076
12077        State reset() {
12078            mTargetPosition = RecyclerView.NO_POSITION;
12079            if (mData != null) {
12080                mData.clear();
12081            }
12082            mItemCount = 0;
12083            mStructureChanged = false;
12084            mIsMeasuring = false;
12085            return this;
12086        }
12087
12088        /**
12089         * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
12090         * prior to any layout passes.
12091         *
12092         * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
12093         * that Recycler#getViewForPosition() can function safely.</p>
12094         */
12095        void prepareForNestedPrefetch(Adapter adapter) {
12096            mLayoutStep = STEP_START;
12097            mItemCount = adapter.getItemCount();
12098            mInPreLayout = false;
12099            mTrackOldChangeHolders = false;
12100            mIsMeasuring = false;
12101        }
12102
12103        /**
12104         * Returns true if the RecyclerView is currently measuring the layout. This value is
12105         * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
12106         * has non-exact measurement specs.
12107         * <p>
12108         * Note that if the LayoutManager supports predictive animations and it is calculating the
12109         * pre-layout step, this value will be {@code false} even if the RecyclerView is in
12110         * {@code onMeasure} call. This is because pre-layout means the previous state of the
12111         * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
12112         * LayoutManager is always guaranteed to receive another call to
12113         * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
12114         *
12115         * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
12116         */
12117        public boolean isMeasuring() {
12118            return mIsMeasuring;
12119        }
12120
12121        /**
12122         * Returns true if the {@link RecyclerView} is in the pre-layout step where it is having its
12123         * {@link LayoutManager} layout items where they will be at the beginning of a set of
12124         * predictive item animations.
12125         */
12126        public boolean isPreLayout() {
12127            return mInPreLayout;
12128        }
12129
12130        /**
12131         * Returns whether RecyclerView will run predictive animations in this layout pass
12132         * or not.
12133         *
12134         * @return true if RecyclerView is calculating predictive animations to be run at the end
12135         *         of the layout pass.
12136         */
12137        public boolean willRunPredictiveAnimations() {
12138            return mRunPredictiveAnimations;
12139        }
12140
12141        /**
12142         * Returns whether RecyclerView will run simple animations in this layout pass
12143         * or not.
12144         *
12145         * @return true if RecyclerView is calculating simple animations to be run at the end of
12146         *         the layout pass.
12147         */
12148        public boolean willRunSimpleAnimations() {
12149            return mRunSimpleAnimations;
12150        }
12151
12152        /**
12153         * Removes the mapping from the specified id, if there was any.
12154         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
12155         *                   preserve cross functionality and avoid conflicts.
12156         */
12157        public void remove(int resourceId) {
12158            if (mData == null) {
12159                return;
12160            }
12161            mData.remove(resourceId);
12162        }
12163
12164        /**
12165         * Gets the Object mapped from the specified id, or <code>null</code>
12166         * if no such data exists.
12167         *
12168         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
12169         *                   to
12170         *                   preserve cross functionality and avoid conflicts.
12171         */
12172        @SuppressWarnings("TypeParameterUnusedInFormals")
12173        public <T> T get(int resourceId) {
12174            if (mData == null) {
12175                return null;
12176            }
12177            return (T) mData.get(resourceId);
12178        }
12179
12180        /**
12181         * Adds a mapping from the specified id to the specified value, replacing the previous
12182         * mapping from the specified key if there was one.
12183         *
12184         * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
12185         *                   preserve cross functionality and avoid conflicts.
12186         * @param data       The data you want to associate with the resourceId.
12187         */
12188        public void put(int resourceId, Object data) {
12189            if (mData == null) {
12190                mData = new SparseArray<Object>();
12191            }
12192            mData.put(resourceId, data);
12193        }
12194
12195        /**
12196         * If scroll is triggered to make a certain item visible, this value will return the
12197         * adapter index of that item.
12198         * @return Adapter index of the target item or
12199         * {@link RecyclerView#NO_POSITION} if there is no target
12200         * position.
12201         */
12202        public int getTargetScrollPosition() {
12203            return mTargetPosition;
12204        }
12205
12206        /**
12207         * Returns if current scroll has a target position.
12208         * @return true if scroll is being triggered to make a certain position visible
12209         * @see #getTargetScrollPosition()
12210         */
12211        public boolean hasTargetScrollPosition() {
12212            return mTargetPosition != RecyclerView.NO_POSITION;
12213        }
12214
12215        /**
12216         * @return true if the structure of the data set has changed since the last call to
12217         *         onLayoutChildren, false otherwise
12218         */
12219        public boolean didStructureChange() {
12220            return mStructureChanged;
12221        }
12222
12223        /**
12224         * Returns the total number of items that can be laid out. Note that this number is not
12225         * necessarily equal to the number of items in the adapter, so you should always use this
12226         * number for your position calculations and never access the adapter directly.
12227         * <p>
12228         * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
12229         * data changes on existing Views. These calculations are used to decide which animations
12230         * should be run.
12231         * <p>
12232         * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
12233         * present the correct state to LayoutManager in pre-layout pass.
12234         * <p>
12235         * For example, a newly added item is not included in pre-layout item count because
12236         * pre-layout reflects the contents of the adapter before the item is added. Behind the
12237         * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
12238         * LayoutManager does not know about the new item's existence in pre-layout. The item will
12239         * be available in second layout pass and will be included in the item count. Similar
12240         * adjustments are made for moved and removed items as well.
12241         * <p>
12242         * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
12243         *
12244         * @return The number of items currently available
12245         * @see LayoutManager#getItemCount()
12246         */
12247        public int getItemCount() {
12248            return mInPreLayout
12249                    ? (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout)
12250                    : mItemCount;
12251        }
12252
12253        /**
12254         * Returns remaining horizontal scroll distance of an ongoing scroll animation(fling/
12255         * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
12256         * other than {@link #SCROLL_STATE_SETTLING}.
12257         *
12258         * @return Remaining horizontal scroll distance
12259         */
12260        public int getRemainingScrollHorizontal() {
12261            return mRemainingScrollHorizontal;
12262        }
12263
12264        /**
12265         * Returns remaining vertical scroll distance of an ongoing scroll animation(fling/
12266         * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
12267         * other than {@link #SCROLL_STATE_SETTLING}.
12268         *
12269         * @return Remaining vertical scroll distance
12270         */
12271        public int getRemainingScrollVertical() {
12272            return mRemainingScrollVertical;
12273        }
12274
12275        @Override
12276        public String toString() {
12277            return "State{"
12278                    + "mTargetPosition=" + mTargetPosition
12279                    + ", mData=" + mData
12280                    + ", mItemCount=" + mItemCount
12281                    + ", mIsMeasuring=" + mIsMeasuring
12282                    + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
12283                    + ", mDeletedInvisibleItemCountSincePreviousLayout="
12284                    + mDeletedInvisibleItemCountSincePreviousLayout
12285                    + ", mStructureChanged=" + mStructureChanged
12286                    + ", mInPreLayout=" + mInPreLayout
12287                    + ", mRunSimpleAnimations=" + mRunSimpleAnimations
12288                    + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
12289                    + '}';
12290        }
12291    }
12292
12293    /**
12294     * This class defines the behavior of fling if the developer wishes to handle it.
12295     * <p>
12296     * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
12297     *
12298     * @see #setOnFlingListener(OnFlingListener)
12299     */
12300    public abstract static class OnFlingListener {
12301
12302        /**
12303         * Override this to handle a fling given the velocities in both x and y directions.
12304         * Note that this method will only be called if the associated {@link LayoutManager}
12305         * supports scrolling and the fling is not handled by nested scrolls first.
12306         *
12307         * @param velocityX the fling velocity on the X axis
12308         * @param velocityY the fling velocity on the Y axis
12309         *
12310         * @return true if the fling was handled, false otherwise.
12311         */
12312        public abstract boolean onFling(int velocityX, int velocityY);
12313    }
12314
12315    /**
12316     * Internal listener that manages items after animations finish. This is how items are
12317     * retained (not recycled) during animations, but allowed to be recycled afterwards.
12318     * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
12319     * method on the animator's listener when it is done animating any item.
12320     */
12321    private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
12322
12323        ItemAnimatorRestoreListener() {
12324        }
12325
12326        @Override
12327        public void onAnimationFinished(ViewHolder item) {
12328            item.setIsRecyclable(true);
12329            if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
12330                item.mShadowedHolder = null;
12331            }
12332            // always null this because an OldViewHolder can never become NewViewHolder w/o being
12333            // recycled.
12334            item.mShadowingHolder = null;
12335            if (!item.shouldBeKeptAsChild()) {
12336                if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
12337                    removeDetachedView(item.itemView, false);
12338                }
12339            }
12340        }
12341    }
12342
12343    /**
12344     * This class defines the animations that take place on items as changes are made
12345     * to the adapter.
12346     *
12347     * Subclasses of ItemAnimator can be used to implement custom animations for actions on
12348     * ViewHolder items. The RecyclerView will manage retaining these items while they
12349     * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
12350     * when a ViewHolder's animation is finished. In other words, there must be a matching
12351     * {@link #dispatchAnimationFinished(ViewHolder)} call for each
12352     * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
12353     * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12354     * animateChange()}
12355     * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
12356     * and
12357     * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12358     * animateDisappearance()} call.
12359     *
12360     * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
12361     *
12362     * @see #setItemAnimator(ItemAnimator)
12363     */
12364    @SuppressWarnings("UnusedParameters")
12365    public abstract static class ItemAnimator {
12366
12367        /**
12368         * The Item represented by this ViewHolder is updated.
12369         * <p>
12370         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12371         */
12372        public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
12373
12374        /**
12375         * The Item represented by this ViewHolder is removed from the adapter.
12376         * <p>
12377         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12378         */
12379        public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
12380
12381        /**
12382         * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
12383         * represented by this ViewHolder is invalid.
12384         * <p>
12385         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12386         */
12387        public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
12388
12389        /**
12390         * The position of the Item represented by this ViewHolder has been changed. This flag is
12391         * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
12392         * any adapter change that may have a side effect on this item. (e.g. The item before this
12393         * one has been removed from the Adapter).
12394         * <p>
12395         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12396         */
12397        public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
12398
12399        /**
12400         * This ViewHolder was not laid out but has been added to the layout in pre-layout state
12401         * by the {@link LayoutManager}. This means that the item was already in the Adapter but
12402         * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
12403         * to add new items in pre-layout to specify their virtual location when they are invisible
12404         * (e.g. to specify the item should <i>animate in</i> from below the visible area).
12405         * <p>
12406         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12407         */
12408        public static final int FLAG_APPEARED_IN_PRE_LAYOUT =
12409                ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
12410
12411        /**
12412         * The set of flags that might be passed to
12413         * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12414         */
12415        @IntDef(flag = true, value = {
12416                FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
12417                FLAG_APPEARED_IN_PRE_LAYOUT
12418        })
12419        @Retention(RetentionPolicy.SOURCE)
12420        public @interface AdapterChanges {}
12421        private ItemAnimatorListener mListener = null;
12422        private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
12423                new ArrayList<ItemAnimatorFinishedListener>();
12424
12425        private long mAddDuration = 120;
12426        private long mRemoveDuration = 120;
12427        private long mMoveDuration = 250;
12428        private long mChangeDuration = 250;
12429
12430        /**
12431         * Gets the current duration for which all move animations will run.
12432         *
12433         * @return The current move duration
12434         */
12435        public long getMoveDuration() {
12436            return mMoveDuration;
12437        }
12438
12439        /**
12440         * Sets the duration for which all move animations will run.
12441         *
12442         * @param moveDuration The move duration
12443         */
12444        public void setMoveDuration(long moveDuration) {
12445            mMoveDuration = moveDuration;
12446        }
12447
12448        /**
12449         * Gets the current duration for which all add animations will run.
12450         *
12451         * @return The current add duration
12452         */
12453        public long getAddDuration() {
12454            return mAddDuration;
12455        }
12456
12457        /**
12458         * Sets the duration for which all add animations will run.
12459         *
12460         * @param addDuration The add duration
12461         */
12462        public void setAddDuration(long addDuration) {
12463            mAddDuration = addDuration;
12464        }
12465
12466        /**
12467         * Gets the current duration for which all remove animations will run.
12468         *
12469         * @return The current remove duration
12470         */
12471        public long getRemoveDuration() {
12472            return mRemoveDuration;
12473        }
12474
12475        /**
12476         * Sets the duration for which all remove animations will run.
12477         *
12478         * @param removeDuration The remove duration
12479         */
12480        public void setRemoveDuration(long removeDuration) {
12481            mRemoveDuration = removeDuration;
12482        }
12483
12484        /**
12485         * Gets the current duration for which all change animations will run.
12486         *
12487         * @return The current change duration
12488         */
12489        public long getChangeDuration() {
12490            return mChangeDuration;
12491        }
12492
12493        /**
12494         * Sets the duration for which all change animations will run.
12495         *
12496         * @param changeDuration The change duration
12497         */
12498        public void setChangeDuration(long changeDuration) {
12499            mChangeDuration = changeDuration;
12500        }
12501
12502        /**
12503         * Internal only:
12504         * Sets the listener that must be called when the animator is finished
12505         * animating the item (or immediately if no animation happens). This is set
12506         * internally and is not intended to be set by external code.
12507         *
12508         * @param listener The listener that must be called.
12509         */
12510        void setListener(ItemAnimatorListener listener) {
12511            mListener = listener;
12512        }
12513
12514        /**
12515         * Called by the RecyclerView before the layout begins. Item animator should record
12516         * necessary information about the View before it is potentially rebound, moved or removed.
12517         * <p>
12518         * The data returned from this method will be passed to the related <code>animate**</code>
12519         * methods.
12520         * <p>
12521         * Note that this method may be called after pre-layout phase if LayoutManager adds new
12522         * Views to the layout in pre-layout pass.
12523         * <p>
12524         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
12525         * the View and the adapter change flags.
12526         *
12527         * @param state       The current State of RecyclerView which includes some useful data
12528         *                    about the layout that will be calculated.
12529         * @param viewHolder  The ViewHolder whose information should be recorded.
12530         * @param changeFlags Additional information about what changes happened in the Adapter
12531         *                    about the Item represented by this ViewHolder. For instance, if
12532         *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
12533         * @param payloads    The payload list that was previously passed to
12534         *                    {@link Adapter#notifyItemChanged(int, Object)} or
12535         *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
12536         *
12537         * @return An ItemHolderInfo instance that preserves necessary information about the
12538         * ViewHolder. This object will be passed back to related <code>animate**</code> methods
12539         * after layout is complete.
12540         *
12541         * @see #recordPostLayoutInformation(State, ViewHolder)
12542         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12543         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12544         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12545         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12546         */
12547        public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
12548                @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
12549                @NonNull List<Object> payloads) {
12550            return obtainHolderInfo().setFrom(viewHolder);
12551        }
12552
12553        /**
12554         * Called by the RecyclerView after the layout is complete. Item animator should record
12555         * necessary information about the View's final state.
12556         * <p>
12557         * The data returned from this method will be passed to the related <code>animate**</code>
12558         * methods.
12559         * <p>
12560         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
12561         * the View.
12562         *
12563         * @param state      The current State of RecyclerView which includes some useful data about
12564         *                   the layout that will be calculated.
12565         * @param viewHolder The ViewHolder whose information should be recorded.
12566         *
12567         * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
12568         * This object will be passed back to related <code>animate**</code> methods when
12569         * RecyclerView decides how items should be animated.
12570         *
12571         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12572         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12573         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12574         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12575         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12576         */
12577        public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
12578                @NonNull ViewHolder viewHolder) {
12579            return obtainHolderInfo().setFrom(viewHolder);
12580        }
12581
12582        /**
12583         * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
12584         * <p>
12585         * This means that the View was a child of the LayoutManager when layout started but has
12586         * been removed by the LayoutManager. It might have been removed from the adapter or simply
12587         * become invisible due to other factors. You can distinguish these two cases by checking
12588         * the change flags that were passed to
12589         * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12590         * <p>
12591         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
12592         * animation callback method which will be called by the RecyclerView depends on the
12593         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
12594         * LayoutManager's decision whether to layout the changed version of a disappearing
12595         * ViewHolder or not. RecyclerView will call
12596         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12597         * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
12598         * returns {@code false} from
12599         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
12600         * LayoutManager lays out a new disappearing view that holds the updated information.
12601         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
12602         * <p>
12603         * If LayoutManager supports predictive animations, it might provide a target disappear
12604         * location for the View by laying it out in that location. When that happens,
12605         * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
12606         * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
12607         * <p>
12608         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12609         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12610         * decides not to animate the view).
12611         *
12612         * @param viewHolder    The ViewHolder which should be animated
12613         * @param preLayoutInfo The information that was returned from
12614         *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12615         * @param postLayoutInfo The information that was returned from
12616         *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
12617         *                       null if the LayoutManager did not layout the item.
12618         *
12619         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12620         * false otherwise.
12621         */
12622        public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
12623                @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
12624
12625        /**
12626         * Called by the RecyclerView when a ViewHolder is added to the layout.
12627         * <p>
12628         * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
12629         * but has  been added by the LayoutManager. It might be newly added to the adapter or
12630         * simply become visible due to other factors.
12631         * <p>
12632         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12633         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12634         * decides not to animate the view).
12635         *
12636         * @param viewHolder     The ViewHolder which should be animated
12637         * @param preLayoutInfo  The information that was returned from
12638         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12639         *                       Might be null if Item was just added to the adapter or
12640         *                       LayoutManager does not support predictive animations or it could
12641         *                       not predict that this ViewHolder will become visible.
12642         * @param postLayoutInfo The information that was returned from {@link
12643         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12644         *
12645         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12646         * false otherwise.
12647         */
12648        public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
12649                @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12650
12651        /**
12652         * Called by the RecyclerView when a ViewHolder is present in both before and after the
12653         * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
12654         * for it or a {@link Adapter#notifyDataSetChanged()} call.
12655         * <p>
12656         * This ViewHolder still represents the same data that it was representing when the layout
12657         * started but its position / size may be changed by the LayoutManager.
12658         * <p>
12659         * If the Item's layout position didn't change, RecyclerView still calls this method because
12660         * it does not track this information (or does not necessarily know that an animation is
12661         * not required). Your ItemAnimator should handle this case and if there is nothing to
12662         * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
12663         * <code>false</code>.
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         * @param postLayoutInfo The information that was returned from {@link
12673         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12674         *
12675         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12676         * false otherwise.
12677         */
12678        public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
12679                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12680
12681        /**
12682         * Called by the RecyclerView when an adapter item is present both before and after the
12683         * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
12684         * for it. This method may also be called when
12685         * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
12686         * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
12687         * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
12688         * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
12689         * called for the new ViewHolder and the old one will be recycled.
12690         * <p>
12691         * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
12692         * a good possibility that item contents didn't really change but it is rebound from the
12693         * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
12694         * screen didn't change and your animator should handle this case as well and avoid creating
12695         * unnecessary animations.
12696         * <p>
12697         * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
12698         * previous presentation of the item as-is and supply a new ViewHolder for the updated
12699         * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
12700         * This is useful if you don't know the contents of the Item and would like
12701         * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
12702         * <p>
12703         * When you are writing a custom item animator for your layout, it might be more performant
12704         * and elegant to re-use the same ViewHolder and animate the content changes manually.
12705         * <p>
12706         * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
12707         * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
12708         * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
12709         * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
12710         * which represent the same Item. In that case, only the new ViewHolder is visible
12711         * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
12712         * <p>
12713         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
12714         * ViewHolder when their animation is complete
12715         * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
12716         * animate the view).
12717         * <p>
12718         *  If oldHolder and newHolder are the same instance, you should call
12719         * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
12720         * <p>
12721         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
12722         * animation callback method which will be called by the RecyclerView depends on the
12723         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
12724         * LayoutManager's decision whether to layout the changed version of a disappearing
12725         * ViewHolder or not. RecyclerView will call
12726         * {@code animateChange} instead of
12727         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12728         * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
12729         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
12730         * LayoutManager lays out a new disappearing view that holds the updated information.
12731         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
12732         *
12733         * @param oldHolder     The ViewHolder before the layout is started, might be the same
12734         *                      instance with newHolder.
12735         * @param newHolder     The ViewHolder after the layout is finished, might be the same
12736         *                      instance with oldHolder.
12737         * @param preLayoutInfo  The information that was returned from
12738         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12739         * @param postLayoutInfo The information that was returned from {@link
12740         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12741         *
12742         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12743         * false otherwise.
12744         */
12745        public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
12746                @NonNull ViewHolder newHolder,
12747                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12748
12749        @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
12750            int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
12751            if (viewHolder.isInvalid()) {
12752                return FLAG_INVALIDATED;
12753            }
12754            if ((flags & FLAG_INVALIDATED) == 0) {
12755                final int oldPos = viewHolder.getOldPosition();
12756                final int pos = viewHolder.getAdapterPosition();
12757                if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
12758                    flags |= FLAG_MOVED;
12759                }
12760            }
12761            return flags;
12762        }
12763
12764        /**
12765         * Called when there are pending animations waiting to be started. This state
12766         * is governed by the return values from
12767         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12768         * animateAppearance()},
12769         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12770         * animateChange()}
12771         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12772         * animatePersistence()}, and
12773         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12774         * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
12775         * called later to start the associated animations. runPendingAnimations() will be scheduled
12776         * to be run on the next frame.
12777         */
12778        public abstract void runPendingAnimations();
12779
12780        /**
12781         * Method called when an animation on a view should be ended immediately.
12782         * This could happen when other events, like scrolling, occur, so that
12783         * animating views can be quickly put into their proper end locations.
12784         * Implementations should ensure that any animations running on the item
12785         * are canceled and affected properties are set to their end values.
12786         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
12787         * animation since the animations are effectively done when this method is called.
12788         *
12789         * @param item The item for which an animation should be stopped.
12790         */
12791        public abstract void endAnimation(ViewHolder item);
12792
12793        /**
12794         * Method called when all item animations should be ended immediately.
12795         * This could happen when other events, like scrolling, occur, so that
12796         * animating views can be quickly put into their proper end locations.
12797         * Implementations should ensure that any animations running on any items
12798         * are canceled and affected properties are set to their end values.
12799         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
12800         * animation since the animations are effectively done when this method is called.
12801         */
12802        public abstract void endAnimations();
12803
12804        /**
12805         * Method which returns whether there are any item animations currently running.
12806         * This method can be used to determine whether to delay other actions until
12807         * animations end.
12808         *
12809         * @return true if there are any item animations currently running, false otherwise.
12810         */
12811        public abstract boolean isRunning();
12812
12813        /**
12814         * Method to be called by subclasses when an animation is finished.
12815         * <p>
12816         * For each call RecyclerView makes to
12817         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12818         * animateAppearance()},
12819         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12820         * animatePersistence()}, or
12821         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12822         * animateDisappearance()}, there
12823         * should
12824         * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
12825         * <p>
12826         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12827         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
12828         * and <code>newHolder</code>  (if they are not the same instance).
12829         *
12830         * @param viewHolder The ViewHolder whose animation is finished.
12831         * @see #onAnimationFinished(ViewHolder)
12832         */
12833        public final void dispatchAnimationFinished(ViewHolder viewHolder) {
12834            onAnimationFinished(viewHolder);
12835            if (mListener != null) {
12836                mListener.onAnimationFinished(viewHolder);
12837            }
12838        }
12839
12840        /**
12841         * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
12842         * ItemAnimator.
12843         *
12844         * @param viewHolder The ViewHolder whose animation is finished. There might still be other
12845         *                   animations running on this ViewHolder.
12846         * @see #dispatchAnimationFinished(ViewHolder)
12847         */
12848        public void onAnimationFinished(ViewHolder viewHolder) {
12849        }
12850
12851        /**
12852         * Method to be called by subclasses when an animation is started.
12853         * <p>
12854         * For each call RecyclerView makes to
12855         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12856         * animateAppearance()},
12857         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12858         * animatePersistence()}, or
12859         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12860         * animateDisappearance()}, there should be a matching
12861         * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
12862         * <p>
12863         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12864         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
12865         * and <code>newHolder</code> (if they are not the same instance).
12866         * <p>
12867         * If your ItemAnimator decides not to animate a ViewHolder, it should call
12868         * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
12869         * {@link #dispatchAnimationStarted(ViewHolder)}.
12870         *
12871         * @param viewHolder The ViewHolder whose animation is starting.
12872         * @see #onAnimationStarted(ViewHolder)
12873         */
12874        public final void dispatchAnimationStarted(ViewHolder viewHolder) {
12875            onAnimationStarted(viewHolder);
12876        }
12877
12878        /**
12879         * Called when a new animation is started on the given ViewHolder.
12880         *
12881         * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
12882         *                   might already be animating and this might be another animation.
12883         * @see #dispatchAnimationStarted(ViewHolder)
12884         */
12885        public void onAnimationStarted(ViewHolder viewHolder) {
12886
12887        }
12888
12889        /**
12890         * Like {@link #isRunning()}, this method returns whether there are any item
12891         * animations currently running. Additionally, the listener passed in will be called
12892         * when there are no item animations running, either immediately (before the method
12893         * returns) if no animations are currently running, or when the currently running
12894         * animations are {@link #dispatchAnimationsFinished() finished}.
12895         *
12896         * <p>Note that the listener is transient - it is either called immediately and not
12897         * stored at all, or stored only until it is called when running animations
12898         * are finished sometime later.</p>
12899         *
12900         * @param listener A listener to be called immediately if no animations are running
12901         * or later when currently-running animations have finished. A null listener is
12902         * equivalent to calling {@link #isRunning()}.
12903         * @return true if there are any item animations currently running, false otherwise.
12904         */
12905        public final boolean isRunning(ItemAnimatorFinishedListener listener) {
12906            boolean running = isRunning();
12907            if (listener != null) {
12908                if (!running) {
12909                    listener.onAnimationsFinished();
12910                } else {
12911                    mFinishedListeners.add(listener);
12912                }
12913            }
12914            return running;
12915        }
12916
12917        /**
12918         * When an item is changed, ItemAnimator can decide whether it wants to re-use
12919         * the same ViewHolder for animations or RecyclerView should create a copy of the
12920         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12921         * <p>
12922         * Note that this method will only be called if the {@link ViewHolder} still has the same
12923         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12924         * both {@link ViewHolder}s in the
12925         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12926         * <p>
12927         * If your application is using change payloads, you can override
12928         * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
12929         *
12930         * @param viewHolder The ViewHolder which represents the changed item's old content.
12931         *
12932         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
12933         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
12934         *         ItemAnimator to animate. Default implementation returns <code>true</code>.
12935         *
12936         * @see #canReuseUpdatedViewHolder(ViewHolder, List)
12937         */
12938        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
12939            return true;
12940        }
12941
12942        /**
12943         * When an item is changed, ItemAnimator can decide whether it wants to re-use
12944         * the same ViewHolder for animations or RecyclerView should create a copy of the
12945         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12946         * <p>
12947         * Note that this method will only be called if the {@link ViewHolder} still has the same
12948         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12949         * both {@link ViewHolder}s in the
12950         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12951         *
12952         * @param viewHolder The ViewHolder which represents the changed item's old content.
12953         * @param payloads A non-null list of merged payloads that were sent with change
12954         *                 notifications. Can be empty if the adapter is invalidated via
12955         *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
12956         *                 payloads will be passed into
12957         *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
12958         *                 method <b>if</b> this method returns <code>true</code>.
12959         *
12960         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
12961         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
12962         *         ItemAnimator to animate. Default implementation calls
12963         *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
12964         *
12965         * @see #canReuseUpdatedViewHolder(ViewHolder)
12966         */
12967        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
12968                @NonNull List<Object> payloads) {
12969            return canReuseUpdatedViewHolder(viewHolder);
12970        }
12971
12972        /**
12973         * This method should be called by ItemAnimator implementations to notify
12974         * any listeners that all pending and active item animations are finished.
12975         */
12976        public final void dispatchAnimationsFinished() {
12977            final int count = mFinishedListeners.size();
12978            for (int i = 0; i < count; ++i) {
12979                mFinishedListeners.get(i).onAnimationsFinished();
12980            }
12981            mFinishedListeners.clear();
12982        }
12983
12984        /**
12985         * Returns a new {@link ItemHolderInfo} which will be used to store information about the
12986         * ViewHolder. This information will later be passed into <code>animate**</code> methods.
12987         * <p>
12988         * You can override this method if you want to extend {@link ItemHolderInfo} and provide
12989         * your own instances.
12990         *
12991         * @return A new {@link ItemHolderInfo}.
12992         */
12993        public ItemHolderInfo obtainHolderInfo() {
12994            return new ItemHolderInfo();
12995        }
12996
12997        /**
12998         * The interface to be implemented by listeners to animation events from this
12999         * ItemAnimator. This is used internally and is not intended for developers to
13000         * create directly.
13001         */
13002        interface ItemAnimatorListener {
13003            void onAnimationFinished(ViewHolder item);
13004        }
13005
13006        /**
13007         * This interface is used to inform listeners when all pending or running animations
13008         * in an ItemAnimator are finished. This can be used, for example, to delay an action
13009         * in a data set until currently-running animations are complete.
13010         *
13011         * @see #isRunning(ItemAnimatorFinishedListener)
13012         */
13013        public interface ItemAnimatorFinishedListener {
13014            /**
13015             * Notifies when all pending or running animations in an ItemAnimator are finished.
13016             */
13017            void onAnimationsFinished();
13018        }
13019
13020        /**
13021         * A simple data structure that holds information about an item's bounds.
13022         * This information is used in calculating item animations. Default implementation of
13023         * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
13024         * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
13025         * structure. You can extend this class if you would like to keep more information about
13026         * the Views.
13027         * <p>
13028         * If you want to provide your own implementation but still use `super` methods to record
13029         * basic information, you can override {@link #obtainHolderInfo()} to provide your own
13030         * instances.
13031         */
13032        public static class ItemHolderInfo {
13033
13034            /**
13035             * The left edge of the View (excluding decorations)
13036             */
13037            public int left;
13038
13039            /**
13040             * The top edge of the View (excluding decorations)
13041             */
13042            public int top;
13043
13044            /**
13045             * The right edge of the View (excluding decorations)
13046             */
13047            public int right;
13048
13049            /**
13050             * The bottom edge of the View (excluding decorations)
13051             */
13052            public int bottom;
13053
13054            /**
13055             * The change flags that were passed to
13056             * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
13057             */
13058            @AdapterChanges
13059            public int changeFlags;
13060
13061            public ItemHolderInfo() {
13062            }
13063
13064            /**
13065             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
13066             * the given ViewHolder. Clears all {@link #changeFlags}.
13067             *
13068             * @param holder The ViewHolder whose bounds should be copied.
13069             * @return This {@link ItemHolderInfo}
13070             */
13071            public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder) {
13072                return setFrom(holder, 0);
13073            }
13074
13075            /**
13076             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
13077             * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
13078             *
13079             * @param holder The ViewHolder whose bounds should be copied.
13080             * @param flags  The adapter change flags that were passed into
13081             *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
13082             *               List)}.
13083             * @return This {@link ItemHolderInfo}
13084             */
13085            public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder,
13086                    @AdapterChanges int flags) {
13087                final View view = holder.itemView;
13088                this.left = view.getLeft();
13089                this.top = view.getTop();
13090                this.right = view.getRight();
13091                this.bottom = view.getBottom();
13092                return this;
13093            }
13094        }
13095    }
13096
13097    @Override
13098    protected int getChildDrawingOrder(int childCount, int i) {
13099        if (mChildDrawingOrderCallback == null) {
13100            return super.getChildDrawingOrder(childCount, i);
13101        } else {
13102            return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
13103        }
13104    }
13105
13106    /**
13107     * A callback interface that can be used to alter the drawing order of RecyclerView children.
13108     * <p>
13109     * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
13110     * that applies to that method also applies to this callback. For example, changing the drawing
13111     * order of two views will not have any effect if their elevation values are different since
13112     * elevation overrides the result of this callback.
13113     */
13114    public interface ChildDrawingOrderCallback {
13115        /**
13116         * Returns the index of the child to draw for this iteration. Override this
13117         * if you want to change the drawing order of children. By default, it
13118         * returns i.
13119         *
13120         * @param i The current iteration.
13121         * @return The index of the child to draw this iteration.
13122         *
13123         * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
13124         */
13125        int onGetChildDrawingOrder(int childCount, int i);
13126    }
13127
13128    private NestedScrollingChildHelper getScrollingChildHelper() {
13129        if (mScrollingChildHelper == null) {
13130            mScrollingChildHelper = new NestedScrollingChildHelper(this);
13131        }
13132        return mScrollingChildHelper;
13133    }
13134}
13135