1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18package android.support.v7.widget;
19
20import android.content.Context;
21import android.content.res.TypedArray;
22import android.database.Observable;
23import android.graphics.Canvas;
24import android.graphics.Matrix;
25import android.graphics.PointF;
26import android.graphics.Rect;
27import android.graphics.RectF;
28import android.os.Build;
29import android.os.Bundle;
30import android.os.Parcel;
31import android.os.Parcelable;
32import android.os.SystemClock;
33import android.support.annotation.CallSuper;
34import android.support.annotation.IntDef;
35import android.support.annotation.NonNull;
36import android.support.annotation.Nullable;
37import android.support.annotation.RestrictTo;
38import android.support.annotation.VisibleForTesting;
39import android.support.v4.os.ParcelableCompat;
40import android.support.v4.os.ParcelableCompatCreatorCallbacks;
41import android.support.v4.os.TraceCompat;
42import android.support.v4.view.AbsSavedState;
43import android.support.v4.view.InputDeviceCompat;
44import android.support.v4.view.MotionEventCompat;
45import android.support.v4.view.NestedScrollingChild;
46import android.support.v4.view.NestedScrollingChildHelper;
47import android.support.v4.view.ScrollingView;
48import android.support.v4.view.VelocityTrackerCompat;
49import android.support.v4.view.ViewCompat;
50import android.support.v4.view.accessibility.AccessibilityEventCompat;
51import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
52import android.support.v4.view.accessibility.AccessibilityRecordCompat;
53import android.support.v4.widget.EdgeEffectCompat;
54import android.support.v4.widget.ScrollerCompat;
55import android.support.v7.recyclerview.R;
56import android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
57import android.util.AttributeSet;
58import android.util.Log;
59import android.util.SparseArray;
60import android.util.SparseIntArray;
61import android.util.TypedValue;
62import android.view.Display;
63import android.view.FocusFinder;
64import android.view.MotionEvent;
65import android.view.VelocityTracker;
66import android.view.View;
67import android.view.ViewConfiguration;
68import android.view.ViewGroup;
69import android.view.ViewParent;
70import android.view.accessibility.AccessibilityEvent;
71import android.view.accessibility.AccessibilityManager;
72import android.view.animation.Interpolator;
73
74import java.lang.annotation.Retention;
75import java.lang.annotation.RetentionPolicy;
76import java.lang.reflect.Constructor;
77import java.lang.reflect.InvocationTargetException;
78import java.util.ArrayList;
79import java.util.Arrays;
80import java.util.Collections;
81import java.util.List;
82import java.util.concurrent.TimeUnit;
83
84import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
85import static android.support.v7.widget.AdapterHelper.Callback;
86import static android.support.v7.widget.AdapterHelper.UpdateOp;
87
88/**
89 * A flexible view for providing a limited window into a large data set.
90 *
91 * <h3>Glossary of terms:</h3>
92 *
93 * <ul>
94 *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
95 *     that represent items in a data set.</li>
96 *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
97 *     <li><em>Index:</em> The index of an attached child view as used in a call to
98 *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
99 *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
100 *     to a <em>position</em> within the adapter.</li>
101 *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
102 *     position may be placed in a cache for later reuse to display the same type of data again
103 *     later. This can drastically improve performance by skipping initial layout inflation
104 *     or construction.</li>
105 *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
106 *     state during layout. Scrap views may be reused without becoming fully detached
107 *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
108 *     by the adapter if the view was considered <em>dirty</em>.</li>
109 *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
110 *     being displayed.</li>
111 * </ul>
112 *
113 * <h4>Positions in RecyclerView:</h4>
114 * <p>
115 * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
116 * {@link LayoutManager} to be able to detect data set changes in batches during a layout
117 * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
118 * It also helps with performance because all view bindings happen at the same time and unnecessary
119 * bindings are avoided.
120 * <p>
121 * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
122 * <ul>
123 *     <li>layout position: Position of an item in the latest layout calculation. This is the
124 *     position from the LayoutManager's perspective.</li>
125 *     <li>adapter position: Position of an item in the adapter. This is the position from
126 *     the Adapter's perspective.</li>
127 * </ul>
128 * <p>
129 * These two positions are the same except the time between dispatching <code>adapter.notify*
130 * </code> events and calculating the updated layout.
131 * <p>
132 * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
133 * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
134 * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
135 * last layout calculation. You can rely on these positions to be consistent with what user is
136 * currently seeing on the screen. For example, if you have a list of items on the screen and user
137 * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
138 * is seeing.
139 * <p>
140 * The other set of position related methods are in the form of
141 * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
142 * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
143 * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
144 * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
145 * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
146 * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
147 * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
148 * <code>null</code> results from these methods.
149 * <p>
150 * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
151 * writing an {@link Adapter}, you probably want to use adapter positions.
152 *
153 * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_layoutManager
154 */
155public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
156
157    static final String TAG = "RecyclerView";
158
159    static final boolean DEBUG = false;
160
161    private static final int[]  NESTED_SCROLLING_ATTRS
162            = {16843830 /* android.R.attr.nestedScrollingEnabled */};
163
164    private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
165
166    /**
167     * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
168     * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
169     * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
170     * recursively traverses itemView and invalidates display list for each ViewGroup that matches
171     * this criteria.
172     */
173    static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
174            || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
175    /**
176     * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
177     * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
178     * 0 when mode is unspecified.
179     */
180    static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
181
182    static final boolean POST_UPDATES_ON_ANIMATION = Build.VERSION.SDK_INT >= 16;
183
184    /**
185     * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
186     * RenderThread but before the next frame begins. We schedule prefetch work in this window.
187     */
188    private static final boolean ALLOW_PREFETCHING = Build.VERSION.SDK_INT >= 21;
189
190    static final boolean DISPATCH_TEMP_DETACH = false;
191    public static final int HORIZONTAL = 0;
192    public static final int VERTICAL = 1;
193
194    public static final int NO_POSITION = -1;
195    public static final long NO_ID = -1;
196    public static final int INVALID_TYPE = -1;
197
198    /**
199     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
200     * that the RecyclerView should use the standard touch slop for smooth,
201     * continuous scrolling.
202     */
203    public static final int TOUCH_SLOP_DEFAULT = 0;
204
205    /**
206     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
207     * that the RecyclerView should use the standard touch slop for scrolling
208     * widgets that snap to a page or other coarse-grained barrier.
209     */
210    public static final int TOUCH_SLOP_PAGING = 1;
211
212    static final int MAX_SCROLL_DURATION = 2000;
213
214    /**
215     * RecyclerView is calculating a scroll.
216     * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
217     * it. Try to avoid using EditText, focusable views or handle them with care.
218     */
219    static final String TRACE_SCROLL_TAG = "RV Scroll";
220
221    /**
222     * OnLayout has been called by the View system.
223     * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
224     * update themselves directly. This will cause a full re-layout but when it happens via the
225     * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
226     */
227    private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
228
229    /**
230     * NotifyDataSetChanged or equal has been called.
231     * If this is taking a long time, try sending granular notify adapter changes instead of just
232     * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
233     * might help.
234     */
235    private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
236
237    /**
238     * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
239     * If this is taking a long time, you may have dispatched too many Adapter updates causing too
240     * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
241     * methods.
242     */
243    private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
244
245    /**
246     * RecyclerView is rebinding a View.
247     * If this is taking a lot of time, consider optimizing your layout or make sure you are not
248     * doing extra operations in onBindViewHolder call.
249     */
250    static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
251
252    /**
253     * RecyclerView is attempting to pre-populate off screen views.
254     */
255    private static final String TRACE_PREFETCH_TAG = "RV Prefetch";
256
257    /**
258     * RecyclerView is creating a new View.
259     * If too many of these present in Systrace:
260     * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
261     * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
262     * > Adapter#onFailedToRecycleView(ViewHolder)})
263     *
264     * - There might be too many item view types.
265     * > Try merging them
266     *
267     * - There might be too many itemChange animations and not enough space in RecyclerPool.
268     * >Try increasing your pool size and item cache size.
269     */
270    static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
271    private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
272            new Class[]{Context.class, AttributeSet.class, int.class, int.class};
273
274    private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
275
276    final Recycler mRecycler = new Recycler();
277
278    private SavedState mPendingSavedState;
279
280    /**
281     * Handles adapter updates
282     */
283    AdapterHelper mAdapterHelper;
284
285    /**
286     * Handles abstraction between LayoutManager children and RecyclerView children
287     */
288    ChildHelper mChildHelper;
289
290    /**
291     * Keeps data about views to be used for animations
292     */
293    final ViewInfoStore mViewInfoStore = new ViewInfoStore();
294
295    /**
296     * Prior to L, there is no way to query this variable which is why we override the setter and
297     * track it here.
298     */
299    boolean mClipToPadding;
300
301    /**
302     * Note: this Runnable is only ever posted if:
303     * 1) We've been through first layout
304     * 2) We know we have a fixed size (mHasFixedSize)
305     * 3) We're attached
306     */
307    final Runnable mUpdateChildViewsRunnable = new Runnable() {
308        @Override
309        public void run() {
310            if (!mFirstLayoutComplete || isLayoutRequested()) {
311                // a layout request will happen, we should not do layout here.
312                return;
313            }
314            if (!mIsAttached) {
315                requestLayout();
316                // if we are not attached yet, mark us as requiring layout and skip
317                return;
318            }
319            if (mLayoutFrozen) {
320                mLayoutRequestEaten = true;
321                return; //we'll process updates when ice age ends.
322            }
323            consumePendingUpdateOperations();
324        }
325    };
326
327    final Rect mTempRect = new Rect();
328    private final Rect mTempRect2 = new Rect();
329    final RectF mTempRectF = new RectF();
330    Adapter mAdapter;
331    @VisibleForTesting LayoutManager mLayout;
332    RecyclerListener mRecyclerListener;
333    final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
334    private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
335            new ArrayList<>();
336    private OnItemTouchListener mActiveOnItemTouchListener;
337    boolean mIsAttached;
338    boolean mHasFixedSize;
339    @VisibleForTesting boolean mFirstLayoutComplete;
340
341    // Counting lock to control whether we should ignore requestLayout calls from children or not.
342    private int mEatRequestLayout = 0;
343
344    boolean mLayoutRequestEaten;
345    boolean mLayoutFrozen;
346    private boolean mIgnoreMotionEventTillDown;
347
348    // binary OR of change events that were eaten during a layout or scroll.
349    private int mEatenAccessibilityChangeFlags;
350    boolean mAdapterUpdateDuringMeasure;
351
352    private final AccessibilityManager mAccessibilityManager;
353    private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
354
355    /**
356     * Set to true when an adapter data set changed notification is received.
357     * In that case, we cannot run any animations since we don't know what happened.
358     */
359    boolean mDataSetHasChangedAfterLayout = false;
360
361    /**
362     * This variable is incremented during a dispatchLayout and/or scroll.
363     * Some methods should not be called during these periods (e.g. adapter data change).
364     * Doing so will create hard to find bugs so we better check it and throw an exception.
365     *
366     * @see #assertInLayoutOrScroll(String)
367     * @see #assertNotInLayoutOrScroll(String)
368     */
369    private int mLayoutOrScrollCounter = 0;
370
371    /**
372     * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
373     * (for API compatibility).
374     * <p>
375     * It is a bad practice for a developer to update the data in a scroll callback since it is
376     * potentially called during a layout.
377     */
378    private int mDispatchScrollCounter = 0;
379
380    private EdgeEffectCompat mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
381
382    ItemAnimator mItemAnimator = new DefaultItemAnimator();
383
384    private static final int INVALID_POINTER = -1;
385
386    /**
387     * The RecyclerView is not currently scrolling.
388     * @see #getScrollState()
389     */
390    public static final int SCROLL_STATE_IDLE = 0;
391
392    /**
393     * The RecyclerView is currently being dragged by outside input such as user touch input.
394     * @see #getScrollState()
395     */
396    public static final int SCROLL_STATE_DRAGGING = 1;
397
398    /**
399     * The RecyclerView is currently animating to a final position while not under
400     * outside control.
401     * @see #getScrollState()
402     */
403    public static final int SCROLL_STATE_SETTLING = 2;
404
405    // Touch/scrolling handling
406
407    private int mScrollState = SCROLL_STATE_IDLE;
408    private int mScrollPointerId = INVALID_POINTER;
409    private VelocityTracker mVelocityTracker;
410    private int mInitialTouchX;
411    private int mInitialTouchY;
412    private int mLastTouchX;
413    private int mLastTouchY;
414    private int mTouchSlop;
415    private OnFlingListener mOnFlingListener;
416    private final int mMinFlingVelocity;
417    private final int mMaxFlingVelocity;
418    // This value is used when handling generic motion events.
419    private float mScrollFactor = Float.MIN_VALUE;
420    private boolean mPreserveFocusAfterLayout = true;
421
422    final ViewFlinger mViewFlinger = new ViewFlinger();
423
424    private static final long MIN_PREFETCH_TIME_NANOS = TimeUnit.MILLISECONDS.toNanos(4);
425    static long sFrameIntervalNanos = 0;
426    ViewPrefetcher mViewPrefetcher = ALLOW_PREFETCHING ? new ViewPrefetcher() : null;
427
428    final State mState = new State();
429
430    private OnScrollListener mScrollListener;
431    private List<OnScrollListener> mScrollListeners;
432
433    // For use in item animations
434    boolean mItemsAddedOrRemoved = false;
435    boolean mItemsChanged = false;
436    private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
437            new ItemAnimatorRestoreListener();
438    boolean mPostedAnimatorRunner = false;
439    RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
440    private ChildDrawingOrderCallback mChildDrawingOrderCallback;
441
442    // simple array to keep min and max child position during a layout calculation
443    // preserved not to create a new one in each layout pass
444    private final int[] mMinMaxLayoutPositions = new int[2];
445
446    private NestedScrollingChildHelper mScrollingChildHelper;
447    private final int[] mScrollOffset = new int[2];
448    private final int[] mScrollConsumed = new int[2];
449    private final int[] mNestedOffsets = new int[2];
450
451    /**
452     * These are views that had their a11y importance changed during a layout. We defer these events
453     * until the end of the layout because a11y service may make sync calls back to the RV while
454     * the View's state is undefined.
455     */
456    private final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList();
457
458    private Runnable mItemAnimatorRunner = new Runnable() {
459        @Override
460        public void run() {
461            if (mItemAnimator != null) {
462                mItemAnimator.runPendingAnimations();
463            }
464            mPostedAnimatorRunner = false;
465        }
466    };
467
468    static final Interpolator sQuinticInterpolator = new Interpolator() {
469        @Override
470        public float getInterpolation(float t) {
471            t -= 1.0f;
472            return t * t * t * t * t + 1.0f;
473        }
474    };
475
476    /**
477     * The callback to convert view info diffs into animations.
478     */
479    private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
480            new ViewInfoStore.ProcessCallback() {
481        @Override
482        public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
483                @Nullable ItemHolderInfo postInfo) {
484            mRecycler.unscrapView(viewHolder);
485            animateDisappearance(viewHolder, info, postInfo);
486        }
487        @Override
488        public void processAppeared(ViewHolder viewHolder,
489                ItemHolderInfo preInfo, ItemHolderInfo info) {
490            animateAppearance(viewHolder, preInfo, info);
491        }
492
493        @Override
494        public void processPersistent(ViewHolder viewHolder,
495                @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
496            viewHolder.setIsRecyclable(false);
497            if (mDataSetHasChangedAfterLayout) {
498                // since it was rebound, use change instead as we'll be mapping them from
499                // stable ids. If stable ids were false, we would not be running any
500                // animations
501                if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo, postInfo)) {
502                    postAnimationRunner();
503                }
504            } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
505                postAnimationRunner();
506            }
507        }
508        @Override
509        public void unused(ViewHolder viewHolder) {
510            mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
511        }
512    };
513
514    public RecyclerView(Context context) {
515        this(context, null);
516    }
517
518    public RecyclerView(Context context, @Nullable AttributeSet attrs) {
519        this(context, attrs, 0);
520    }
521
522    public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
523        super(context, attrs, defStyle);
524        if (attrs != null) {
525            TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
526            mClipToPadding = a.getBoolean(0, true);
527            a.recycle();
528        } else {
529            mClipToPadding = true;
530        }
531        setScrollContainer(true);
532        setFocusableInTouchMode(true);
533
534        final ViewConfiguration vc = ViewConfiguration.get(context);
535        mTouchSlop = vc.getScaledTouchSlop();
536        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
537        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
538        setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
539
540        mItemAnimator.setListener(mItemAnimatorListener);
541        initAdapterManager();
542        initChildrenHelper();
543        // If not explicitly specified this view is important for accessibility.
544        if (ViewCompat.getImportantForAccessibility(this)
545                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
546            ViewCompat.setImportantForAccessibility(this,
547                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
548        }
549        mAccessibilityManager = (AccessibilityManager) getContext()
550                .getSystemService(Context.ACCESSIBILITY_SERVICE);
551        setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
552        // Create the layoutManager if specified.
553
554        boolean nestedScrollingEnabled = true;
555
556        if (attrs != null) {
557            int defStyleRes = 0;
558            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
559                    defStyle, defStyleRes);
560            String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
561            int descendantFocusability = a.getInt(
562                    R.styleable.RecyclerView_android_descendantFocusability, -1);
563            if (descendantFocusability == -1) {
564                setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
565            }
566            a.recycle();
567            createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
568
569            if (Build.VERSION.SDK_INT >= 21) {
570                a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
571                        defStyle, defStyleRes);
572                nestedScrollingEnabled = a.getBoolean(0, true);
573                a.recycle();
574            }
575        } else {
576            setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
577        }
578
579        // Re-set whether nested scrolling is enabled so that it is set on all API levels
580        setNestedScrollingEnabled(nestedScrollingEnabled);
581    }
582
583    /**
584     * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
585     * @return An instance of AccessibilityDelegateCompat used by RecyclerView
586     */
587    public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
588        return mAccessibilityDelegate;
589    }
590
591    /**
592     * Sets the accessibility delegate compatibility implementation used by RecyclerView.
593     * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
594     */
595    public void setAccessibilityDelegateCompat(
596            RecyclerViewAccessibilityDelegate accessibilityDelegate) {
597        mAccessibilityDelegate = accessibilityDelegate;
598        ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
599    }
600
601    /**
602     * Instantiate and set a LayoutManager, if specified in the attributes.
603     */
604    private void createLayoutManager(Context context, String className, AttributeSet attrs,
605            int defStyleAttr, int defStyleRes) {
606        if (className != null) {
607            className = className.trim();
608            if (className.length() != 0) {  // Can't use isEmpty since it was added in API 9.
609                className = getFullClassName(context, className);
610                try {
611                    ClassLoader classLoader;
612                    if (isInEditMode()) {
613                        // Stupid layoutlib cannot handle simple class loaders.
614                        classLoader = this.getClass().getClassLoader();
615                    } else {
616                        classLoader = context.getClassLoader();
617                    }
618                    Class<? extends LayoutManager> layoutManagerClass =
619                            classLoader.loadClass(className).asSubclass(LayoutManager.class);
620                    Constructor<? extends LayoutManager> constructor;
621                    Object[] constructorArgs = null;
622                    try {
623                        constructor = layoutManagerClass
624                                .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
625                        constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
626                    } catch (NoSuchMethodException e) {
627                        try {
628                            constructor = layoutManagerClass.getConstructor();
629                        } catch (NoSuchMethodException e1) {
630                            e1.initCause(e);
631                            throw new IllegalStateException(attrs.getPositionDescription() +
632                                    ": Error creating LayoutManager " + className, e1);
633                        }
634                    }
635                    constructor.setAccessible(true);
636                    setLayoutManager(constructor.newInstance(constructorArgs));
637                } catch (ClassNotFoundException e) {
638                    throw new IllegalStateException(attrs.getPositionDescription()
639                            + ": Unable to find LayoutManager " + className, e);
640                } catch (InvocationTargetException e) {
641                    throw new IllegalStateException(attrs.getPositionDescription()
642                            + ": Could not instantiate the LayoutManager: " + className, e);
643                } catch (InstantiationException e) {
644                    throw new IllegalStateException(attrs.getPositionDescription()
645                            + ": Could not instantiate the LayoutManager: " + className, e);
646                } catch (IllegalAccessException e) {
647                    throw new IllegalStateException(attrs.getPositionDescription()
648                            + ": Cannot access non-public constructor " + className, e);
649                } catch (ClassCastException e) {
650                    throw new IllegalStateException(attrs.getPositionDescription()
651                            + ": Class is not a LayoutManager " + className, e);
652                }
653            }
654        }
655    }
656
657    private String getFullClassName(Context context, String className) {
658        if (className.charAt(0) == '.') {
659            return context.getPackageName() + className;
660        }
661        if (className.contains(".")) {
662            return className;
663        }
664        return RecyclerView.class.getPackage().getName() + '.' + className;
665    }
666
667    private void initChildrenHelper() {
668        mChildHelper = new ChildHelper(new ChildHelper.Callback() {
669            @Override
670            public int getChildCount() {
671                return RecyclerView.this.getChildCount();
672            }
673
674            @Override
675            public void addView(View child, int index) {
676                RecyclerView.this.addView(child, index);
677                dispatchChildAttached(child);
678            }
679
680            @Override
681            public int indexOfChild(View view) {
682                return RecyclerView.this.indexOfChild(view);
683            }
684
685            @Override
686            public void removeViewAt(int index) {
687                final View child = RecyclerView.this.getChildAt(index);
688                if (child != null) {
689                    dispatchChildDetached(child);
690                }
691                RecyclerView.this.removeViewAt(index);
692            }
693
694            @Override
695            public View getChildAt(int offset) {
696                return RecyclerView.this.getChildAt(offset);
697            }
698
699            @Override
700            public void removeAllViews() {
701                final int count = getChildCount();
702                for (int i = 0; i < count; i ++) {
703                    dispatchChildDetached(getChildAt(i));
704                }
705                RecyclerView.this.removeAllViews();
706            }
707
708            @Override
709            public ViewHolder getChildViewHolder(View view) {
710                return getChildViewHolderInt(view);
711            }
712
713            @Override
714            public void attachViewToParent(View child, int index,
715                    ViewGroup.LayoutParams layoutParams) {
716                final ViewHolder vh = getChildViewHolderInt(child);
717                if (vh != null) {
718                    if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
719                        throw new IllegalArgumentException("Called attach on a child which is not"
720                                + " detached: " + vh);
721                    }
722                    if (DEBUG) {
723                        Log.d(TAG, "reAttach " + vh);
724                    }
725                    vh.clearTmpDetachFlag();
726                }
727                RecyclerView.this.attachViewToParent(child, index, layoutParams);
728            }
729
730            @Override
731            public void detachViewFromParent(int offset) {
732                final View view = getChildAt(offset);
733                if (view != null) {
734                    final ViewHolder vh = getChildViewHolderInt(view);
735                    if (vh != null) {
736                        if (vh.isTmpDetached() && !vh.shouldIgnore()) {
737                            throw new IllegalArgumentException("called detach on an already"
738                                    + " detached child " + vh);
739                        }
740                        if (DEBUG) {
741                            Log.d(TAG, "tmpDetach " + vh);
742                        }
743                        vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
744                    }
745                }
746                RecyclerView.this.detachViewFromParent(offset);
747            }
748
749            @Override
750            public void onEnteredHiddenState(View child) {
751                final ViewHolder vh = getChildViewHolderInt(child);
752                if (vh != null) {
753                    vh.onEnteredHiddenState(RecyclerView.this);
754                }
755            }
756
757            @Override
758            public void onLeftHiddenState(View child) {
759                final ViewHolder vh = getChildViewHolderInt(child);
760                if (vh != null) {
761                    vh.onLeftHiddenState(RecyclerView.this);
762                }
763            }
764        });
765    }
766
767    void initAdapterManager() {
768        mAdapterHelper = new AdapterHelper(new Callback() {
769            @Override
770            public ViewHolder findViewHolder(int position) {
771                final ViewHolder vh = findViewHolderForPosition(position, true);
772                if (vh == null) {
773                    return null;
774                }
775                // ensure it is not hidden because for adapter helper, the only thing matter is that
776                // LM thinks view is a child.
777                if (mChildHelper.isHidden(vh.itemView)) {
778                    if (DEBUG) {
779                        Log.d(TAG, "assuming view holder cannot be find because it is hidden");
780                    }
781                    return null;
782                }
783                return vh;
784            }
785
786            @Override
787            public void offsetPositionsForRemovingInvisible(int start, int count) {
788                offsetPositionRecordsForRemove(start, count, true);
789                mItemsAddedOrRemoved = true;
790                mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
791            }
792
793            @Override
794            public void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount) {
795                offsetPositionRecordsForRemove(positionStart, itemCount, false);
796                mItemsAddedOrRemoved = true;
797            }
798
799            @Override
800            public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
801                viewRangeUpdate(positionStart, itemCount, payload);
802                mItemsChanged = true;
803            }
804
805            @Override
806            public void onDispatchFirstPass(UpdateOp op) {
807                dispatchUpdate(op);
808            }
809
810            void dispatchUpdate(UpdateOp op) {
811                switch (op.cmd) {
812                    case UpdateOp.ADD:
813                        mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
814                        break;
815                    case UpdateOp.REMOVE:
816                        mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
817                        break;
818                    case UpdateOp.UPDATE:
819                        mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
820                                op.payload);
821                        break;
822                    case UpdateOp.MOVE:
823                        mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
824                        break;
825                }
826            }
827
828            @Override
829            public void onDispatchSecondPass(UpdateOp op) {
830                dispatchUpdate(op);
831            }
832
833            @Override
834            public void offsetPositionsForAdd(int positionStart, int itemCount) {
835                offsetPositionRecordsForInsert(positionStart, itemCount);
836                mItemsAddedOrRemoved = true;
837            }
838
839            @Override
840            public void offsetPositionsForMove(int from, int to) {
841                offsetPositionRecordsForMove(from, to);
842                // should we create mItemsMoved ?
843                mItemsAddedOrRemoved = true;
844            }
845        });
846    }
847
848    /**
849     * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
850     * size is not affected by the adapter contents. RecyclerView can still change its size based
851     * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
852     * size of its children or contents of its adapter (except the number of items in the adapter).
853     * <p>
854     * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
855     * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
856     *
857     * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
858     */
859    public void setHasFixedSize(boolean hasFixedSize) {
860        mHasFixedSize = hasFixedSize;
861    }
862
863    /**
864     * @return true if the app has specified that changes in adapter content cannot change
865     * the size of the RecyclerView itself.
866     */
867    public boolean hasFixedSize() {
868        return mHasFixedSize;
869    }
870
871    @Override
872    public void setClipToPadding(boolean clipToPadding) {
873        if (clipToPadding != mClipToPadding) {
874            invalidateGlows();
875        }
876        mClipToPadding = clipToPadding;
877        super.setClipToPadding(clipToPadding);
878        if (mFirstLayoutComplete) {
879            requestLayout();
880        }
881    }
882
883    /**
884     * Returns whether this RecyclerView will clip its children to its padding, and resize (but
885     * not clip) any EdgeEffect to the padded region, if padding is present.
886     * <p>
887     * By default, children are clipped to the padding of their parent
888     * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
889     *
890     * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
891     *         clip) any EdgeEffect to the padded region, false otherwise.
892     *
893     * @attr name android:clipToPadding
894     */
895    public boolean getClipToPadding() {
896        return mClipToPadding;
897    }
898
899    /**
900     * Configure the scrolling touch slop for a specific use case.
901     *
902     * Set up the RecyclerView's scrolling motion threshold based on common usages.
903     * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
904     *
905     * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
906     *                     the intended usage of this RecyclerView
907     */
908    public void setScrollingTouchSlop(int slopConstant) {
909        final ViewConfiguration vc = ViewConfiguration.get(getContext());
910        switch (slopConstant) {
911            default:
912                Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
913                      + slopConstant + "; using default value");
914                // fall-through
915            case TOUCH_SLOP_DEFAULT:
916                mTouchSlop = vc.getScaledTouchSlop();
917                break;
918
919            case TOUCH_SLOP_PAGING:
920                mTouchSlop = vc.getScaledPagingTouchSlop();
921                break;
922        }
923    }
924
925    /**
926     * Swaps the current adapter with the provided one. It is similar to
927     * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
928     * {@link ViewHolder} and does not clear the RecycledViewPool.
929     * <p>
930     * Note that it still calls onAdapterChanged callbacks.
931     *
932     * @param adapter The new adapter to set, or null to set no adapter.
933     * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
934     *                                      Views. If adapters have stable ids and/or you want to
935     *                                      animate the disappearing views, you may prefer to set
936     *                                      this to false.
937     * @see #setAdapter(Adapter)
938     */
939    public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
940        // bail out if layout is frozen
941        setLayoutFrozen(false);
942        setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
943        setDataSetChangedAfterLayout();
944        requestLayout();
945    }
946    /**
947     * Set a new adapter to provide child views on demand.
948     * <p>
949     * When adapter is changed, all existing views are recycled back to the pool. If the pool has
950     * only one adapter, it will be cleared.
951     *
952     * @param adapter The new adapter to set, or null to set no adapter.
953     * @see #swapAdapter(Adapter, boolean)
954     */
955    public void setAdapter(Adapter adapter) {
956        // bail out if layout is frozen
957        setLayoutFrozen(false);
958        setAdapterInternal(adapter, false, true);
959        requestLayout();
960    }
961
962    /**
963     * Replaces the current adapter with the new one and triggers listeners.
964     * @param adapter The new adapter
965     * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
966     *                               item types with the current adapter (helps us avoid cache
967     *                               invalidation).
968     * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
969     *                               compatibleWithPrevious is false, this parameter is ignored.
970     */
971    private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
972            boolean removeAndRecycleViews) {
973        if (mAdapter != null) {
974            mAdapter.unregisterAdapterDataObserver(mObserver);
975            mAdapter.onDetachedFromRecyclerView(this);
976        }
977        if (!compatibleWithPrevious || removeAndRecycleViews) {
978            // end all running animations
979            if (mItemAnimator != null) {
980                mItemAnimator.endAnimations();
981            }
982            // Since animations are ended, mLayout.children should be equal to
983            // recyclerView.children. This may not be true if item animator's end does not work as
984            // expected. (e.g. not release children instantly). It is safer to use mLayout's child
985            // count.
986            if (mLayout != null) {
987                mLayout.removeAndRecycleAllViews(mRecycler);
988                mLayout.removeAndRecycleScrapInt(mRecycler);
989            }
990            // we should clear it here before adapters are swapped to ensure correct callbacks.
991            mRecycler.clear();
992        }
993        mAdapterHelper.reset();
994        final Adapter oldAdapter = mAdapter;
995        mAdapter = adapter;
996        if (adapter != null) {
997            adapter.registerAdapterDataObserver(mObserver);
998            adapter.onAttachedToRecyclerView(this);
999        }
1000        if (mLayout != null) {
1001            mLayout.onAdapterChanged(oldAdapter, mAdapter);
1002        }
1003        mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
1004        mState.mStructureChanged = true;
1005        markKnownViewsInvalid();
1006    }
1007
1008    /**
1009     * Retrieves the previously set adapter or null if no adapter is set.
1010     *
1011     * @return The previously set adapter
1012     * @see #setAdapter(Adapter)
1013     */
1014    public Adapter getAdapter() {
1015        return mAdapter;
1016    }
1017
1018    /**
1019     * Register a listener that will be notified whenever a child view is recycled.
1020     *
1021     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1022     * that a child view is no longer needed. If an application associates expensive
1023     * or heavyweight data with item views, this may be a good place to release
1024     * or free those resources.</p>
1025     *
1026     * @param listener Listener to register, or null to clear
1027     */
1028    public void setRecyclerListener(RecyclerListener listener) {
1029        mRecyclerListener = listener;
1030    }
1031
1032    /**
1033     * <p>Return the offset of the RecyclerView's text baseline from the its top
1034     * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
1035     * this method returns -1.</p>
1036     *
1037     * @return the offset of the baseline within the RecyclerView's bounds or -1
1038     *         if baseline alignment is not supported
1039     */
1040    @Override
1041    public int getBaseline() {
1042        if (mLayout != null) {
1043            return mLayout.getBaseline();
1044        } else {
1045            return super.getBaseline();
1046        }
1047    }
1048
1049    /**
1050     * Register a listener that will be notified whenever a child view is attached to or detached
1051     * from RecyclerView.
1052     *
1053     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1054     * that a child view is no longer needed. If an application associates expensive
1055     * or heavyweight data with item views, this may be a good place to release
1056     * or free those resources.</p>
1057     *
1058     * @param listener Listener to register
1059     */
1060    public void addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1061        if (mOnChildAttachStateListeners == null) {
1062            mOnChildAttachStateListeners = new ArrayList<>();
1063        }
1064        mOnChildAttachStateListeners.add(listener);
1065    }
1066
1067    /**
1068     * Removes the provided listener from child attached state listeners list.
1069     *
1070     * @param listener Listener to unregister
1071     */
1072    public void removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1073        if (mOnChildAttachStateListeners == null) {
1074            return;
1075        }
1076        mOnChildAttachStateListeners.remove(listener);
1077    }
1078
1079    /**
1080     * Removes all listeners that were added via
1081     * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
1082     */
1083    public void clearOnChildAttachStateChangeListeners() {
1084        if (mOnChildAttachStateListeners != null) {
1085            mOnChildAttachStateListeners.clear();
1086        }
1087    }
1088
1089    /**
1090     * Set the {@link LayoutManager} that this RecyclerView will use.
1091     *
1092     * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
1093     * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
1094     * layout arrangements for child views. These arrangements are controlled by the
1095     * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
1096     *
1097     * <p>Several default strategies are provided for common uses such as lists and grids.</p>
1098     *
1099     * @param layout LayoutManager to use
1100     */
1101    public void setLayoutManager(LayoutManager layout) {
1102        if (layout == mLayout) {
1103            return;
1104        }
1105        stopScroll();
1106        // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
1107        // chance that LayoutManagers will re-use views.
1108        if (mLayout != null) {
1109            // end all running animations
1110            if (mItemAnimator != null) {
1111                mItemAnimator.endAnimations();
1112            }
1113            mLayout.removeAndRecycleAllViews(mRecycler);
1114            mLayout.removeAndRecycleScrapInt(mRecycler);
1115            mRecycler.clear();
1116
1117            if (mIsAttached) {
1118                mLayout.dispatchDetachedFromWindow(this, mRecycler);
1119            }
1120            mLayout.setRecyclerView(null);
1121            mLayout = null;
1122        } else {
1123            mRecycler.clear();
1124        }
1125        // this is just a defensive measure for faulty item animators.
1126        mChildHelper.removeAllViewsUnfiltered();
1127        mLayout = layout;
1128        if (layout != null) {
1129            if (layout.mRecyclerView != null) {
1130                throw new IllegalArgumentException("LayoutManager " + layout +
1131                        " is already attached to a RecyclerView: " + layout.mRecyclerView);
1132            }
1133            mLayout.setRecyclerView(this);
1134            if (mIsAttached) {
1135                mLayout.dispatchAttachedToWindow(this);
1136            }
1137        }
1138        mRecycler.updateViewCacheSize();
1139        requestLayout();
1140    }
1141
1142    /**
1143     * Set a {@link OnFlingListener} for this {@link RecyclerView}.
1144     * <p>
1145     * If the {@link OnFlingListener} is set then it will receive
1146     * calls to {@link #fling(int,int)} and will be able to intercept them.
1147     *
1148     * @param onFlingListener The {@link OnFlingListener} instance.
1149     */
1150    public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
1151        mOnFlingListener = onFlingListener;
1152    }
1153
1154    /**
1155     * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
1156     *
1157     * @return The {@link OnFlingListener} instance currently set (can be null).
1158     */
1159    @Nullable
1160    public OnFlingListener getOnFlingListener() {
1161        return mOnFlingListener;
1162    }
1163
1164    @Override
1165    protected Parcelable onSaveInstanceState() {
1166        SavedState state = new SavedState(super.onSaveInstanceState());
1167        if (mPendingSavedState != null) {
1168            state.copyFrom(mPendingSavedState);
1169        } else if (mLayout != null) {
1170            state.mLayoutState = mLayout.onSaveInstanceState();
1171        } else {
1172            state.mLayoutState = null;
1173        }
1174
1175        return state;
1176    }
1177
1178    @Override
1179    protected void onRestoreInstanceState(Parcelable state) {
1180        if (!(state instanceof SavedState)) {
1181            super.onRestoreInstanceState(state);
1182            return;
1183        }
1184
1185        mPendingSavedState = (SavedState) state;
1186        super.onRestoreInstanceState(mPendingSavedState.getSuperState());
1187        if (mLayout != null && mPendingSavedState.mLayoutState != null) {
1188            mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
1189        }
1190    }
1191
1192    /**
1193     * Override to prevent freezing of any views created by the adapter.
1194     */
1195    @Override
1196    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1197        dispatchFreezeSelfOnly(container);
1198    }
1199
1200    /**
1201     * Override to prevent thawing of any views created by the adapter.
1202     */
1203    @Override
1204    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1205        dispatchThawSelfOnly(container);
1206    }
1207
1208    /**
1209     * Adds a view to the animatingViews list.
1210     * mAnimatingViews holds the child views that are currently being kept around
1211     * purely for the purpose of being animated out of view. They are drawn as a regular
1212     * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
1213     * as they are managed separately from the regular child views.
1214     * @param viewHolder The ViewHolder to be removed
1215     */
1216    private void addAnimatingView(ViewHolder viewHolder) {
1217        final View view = viewHolder.itemView;
1218        final boolean alreadyParented = view.getParent() == this;
1219        mRecycler.unscrapView(getChildViewHolder(view));
1220        if (viewHolder.isTmpDetached()) {
1221            // re-attach
1222            mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
1223        } else if(!alreadyParented) {
1224            mChildHelper.addView(view, true);
1225        } else {
1226            mChildHelper.hide(view);
1227        }
1228    }
1229
1230    /**
1231     * Removes a view from the animatingViews list.
1232     * @param view The view to be removed
1233     * @see #addAnimatingView(RecyclerView.ViewHolder)
1234     * @return true if an animating view is removed
1235     */
1236    boolean removeAnimatingView(View view) {
1237        eatRequestLayout();
1238        final boolean removed = mChildHelper.removeViewIfHidden(view);
1239        if (removed) {
1240            final ViewHolder viewHolder = getChildViewHolderInt(view);
1241            mRecycler.unscrapView(viewHolder);
1242            mRecycler.recycleViewHolderInternal(viewHolder);
1243            if (DEBUG) {
1244                Log.d(TAG, "after removing animated view: " + view + ", " + this);
1245            }
1246        }
1247        // only clear request eaten flag if we removed the view.
1248        resumeRequestLayout(!removed);
1249        return removed;
1250    }
1251
1252    /**
1253     * Return the {@link LayoutManager} currently responsible for
1254     * layout policy for this RecyclerView.
1255     *
1256     * @return The currently bound LayoutManager
1257     */
1258    public LayoutManager getLayoutManager() {
1259        return mLayout;
1260    }
1261
1262    /**
1263     * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
1264     * if no pool is set for this view a new one will be created. See
1265     * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
1266     *
1267     * @return The pool used to store recycled item views for reuse.
1268     * @see #setRecycledViewPool(RecycledViewPool)
1269     */
1270    public RecycledViewPool getRecycledViewPool() {
1271        return mRecycler.getRecycledViewPool();
1272    }
1273
1274    /**
1275     * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
1276     * This can be useful if you have multiple RecyclerViews with adapters that use the same
1277     * view types, for example if you have several data sets with the same kinds of item views
1278     * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
1279     *
1280     * @param pool Pool to set. If this parameter is null a new pool will be created and used.
1281     */
1282    public void setRecycledViewPool(RecycledViewPool pool) {
1283        mRecycler.setRecycledViewPool(pool);
1284    }
1285
1286    /**
1287     * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
1288     *
1289     * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
1290     *
1291     * @see {@link ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)}
1292     */
1293    public void setViewCacheExtension(ViewCacheExtension extension) {
1294        mRecycler.setViewCacheExtension(extension);
1295    }
1296
1297    /**
1298     * Set the number of offscreen views to retain before adding them to the potentially shared
1299     * {@link #getRecycledViewPool() recycled view pool}.
1300     *
1301     * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
1302     * a LayoutManager to reuse those views unmodified without needing to return to the adapter
1303     * to rebind them.</p>
1304     *
1305     * @param size Number of views to cache offscreen before returning them to the general
1306     *             recycled view pool
1307     */
1308    public void setItemViewCacheSize(int size) {
1309        mRecycler.setViewCacheSize(size);
1310    }
1311
1312    /**
1313     * Return the current scrolling state of the RecyclerView.
1314     *
1315     * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
1316     * {@link #SCROLL_STATE_SETTLING}
1317     */
1318    public int getScrollState() {
1319        return mScrollState;
1320    }
1321
1322    void setScrollState(int state) {
1323        if (state == mScrollState) {
1324            return;
1325        }
1326        if (DEBUG) {
1327            Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
1328                    new Exception());
1329        }
1330        mScrollState = state;
1331        if (state != SCROLL_STATE_SETTLING) {
1332            stopScrollersInternal();
1333        }
1334        dispatchOnScrollStateChanged(state);
1335    }
1336
1337    /**
1338     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1339     * affect both measurement and drawing of individual item views.
1340     *
1341     * <p>Item decorations are ordered. Decorations placed earlier in the list will
1342     * be run/queried/drawn first for their effects on item views. Padding added to views
1343     * will be nested; a padding added by an earlier decoration will mean further
1344     * item decorations in the list will be asked to draw/pad within the previous decoration's
1345     * given area.</p>
1346     *
1347     * @param decor Decoration to add
1348     * @param index Position in the decoration chain to insert this decoration at. If this value
1349     *              is negative the decoration will be added at the end.
1350     */
1351    public void addItemDecoration(ItemDecoration decor, int index) {
1352        if (mLayout != null) {
1353            mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
1354                    + " layout");
1355        }
1356        if (mItemDecorations.isEmpty()) {
1357            setWillNotDraw(false);
1358        }
1359        if (index < 0) {
1360            mItemDecorations.add(decor);
1361        } else {
1362            mItemDecorations.add(index, decor);
1363        }
1364        markItemDecorInsetsDirty();
1365        requestLayout();
1366    }
1367
1368    /**
1369     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1370     * affect both measurement and drawing of individual item views.
1371     *
1372     * <p>Item decorations are ordered. Decorations placed earlier in the list will
1373     * be run/queried/drawn first for their effects on item views. Padding added to views
1374     * will be nested; a padding added by an earlier decoration will mean further
1375     * item decorations in the list will be asked to draw/pad within the previous decoration's
1376     * given area.</p>
1377     *
1378     * @param decor Decoration to add
1379     */
1380    public void addItemDecoration(ItemDecoration decor) {
1381        addItemDecoration(decor, -1);
1382    }
1383
1384    /**
1385     * Remove an {@link ItemDecoration} from this RecyclerView.
1386     *
1387     * <p>The given decoration will no longer impact the measurement and drawing of
1388     * item views.</p>
1389     *
1390     * @param decor Decoration to remove
1391     * @see #addItemDecoration(ItemDecoration)
1392     */
1393    public void removeItemDecoration(ItemDecoration decor) {
1394        if (mLayout != null) {
1395            mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
1396                    + " layout");
1397        }
1398        mItemDecorations.remove(decor);
1399        if (mItemDecorations.isEmpty()) {
1400            setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
1401        }
1402        markItemDecorInsetsDirty();
1403        requestLayout();
1404    }
1405
1406    /**
1407     * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
1408     * <p>
1409     * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
1410     * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
1411     * true if childDrawingOrderCallback is not null, false otherwise.
1412     * <p>
1413     * Note that child drawing order may be overridden by View's elevation.
1414     *
1415     * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
1416     *                                  system.
1417     */
1418    public void setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback) {
1419        if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
1420            return;
1421        }
1422        mChildDrawingOrderCallback = childDrawingOrderCallback;
1423        setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
1424    }
1425
1426    /**
1427     * Set a listener that will be notified of any changes in scroll state or position.
1428     *
1429     * @param listener Listener to set or null to clear
1430     *
1431     * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
1432     *             {@link #removeOnScrollListener(OnScrollListener)}
1433     */
1434    @Deprecated
1435    public void setOnScrollListener(OnScrollListener listener) {
1436        mScrollListener = listener;
1437    }
1438
1439    /**
1440     * Add a listener that will be notified of any changes in scroll state or position.
1441     *
1442     * <p>Components that add a listener should take care to remove it when finished.
1443     * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
1444     * to remove all attached listeners.</p>
1445     *
1446     * @param listener listener to set or null to clear
1447     */
1448    public void addOnScrollListener(OnScrollListener listener) {
1449        if (mScrollListeners == null) {
1450            mScrollListeners = new ArrayList<>();
1451        }
1452        mScrollListeners.add(listener);
1453    }
1454
1455    /**
1456     * Remove a listener that was notified of any changes in scroll state or position.
1457     *
1458     * @param listener listener to set or null to clear
1459     */
1460    public void removeOnScrollListener(OnScrollListener listener) {
1461        if (mScrollListeners != null) {
1462            mScrollListeners.remove(listener);
1463        }
1464    }
1465
1466    /**
1467     * Remove all secondary listener that were notified of any changes in scroll state or position.
1468     */
1469    public void clearOnScrollListeners() {
1470        if (mScrollListeners != null) {
1471            mScrollListeners.clear();
1472        }
1473    }
1474
1475    /**
1476     * Convenience method to scroll to a certain position.
1477     *
1478     * RecyclerView does not implement scrolling logic, rather forwards the call to
1479     * {@link android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
1480     * @param position Scroll to this adapter position
1481     * @see android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)
1482     */
1483    public void scrollToPosition(int position) {
1484        if (mLayoutFrozen) {
1485            return;
1486        }
1487        stopScroll();
1488        if (mLayout == null) {
1489            Log.e(TAG, "Cannot scroll to position a LayoutManager set. " +
1490                    "Call setLayoutManager with a non-null argument.");
1491            return;
1492        }
1493        mLayout.scrollToPosition(position);
1494        awakenScrollBars();
1495    }
1496
1497    void jumpToPositionForSmoothScroller(int position) {
1498        if (mLayout == null) {
1499            return;
1500        }
1501        mLayout.scrollToPosition(position);
1502        awakenScrollBars();
1503    }
1504
1505    /**
1506     * Starts a smooth scroll to an adapter position.
1507     * <p>
1508     * To support smooth scrolling, you must override
1509     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
1510     * {@link SmoothScroller}.
1511     * <p>
1512     * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
1513     * provide a custom smooth scroll logic, override
1514     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
1515     * LayoutManager.
1516     *
1517     * @param position The adapter position to scroll to
1518     * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
1519     */
1520    public void smoothScrollToPosition(int position) {
1521        if (mLayoutFrozen) {
1522            return;
1523        }
1524        if (mLayout == null) {
1525            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
1526                    "Call setLayoutManager with a non-null argument.");
1527            return;
1528        }
1529        mLayout.smoothScrollToPosition(this, mState, position);
1530    }
1531
1532    @Override
1533    public void scrollTo(int x, int y) {
1534        Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
1535                + "Use scrollToPosition instead");
1536    }
1537
1538    @Override
1539    public void scrollBy(int x, int y) {
1540        if (mLayout == null) {
1541            Log.e(TAG, "Cannot scroll without a LayoutManager set. " +
1542                    "Call setLayoutManager with a non-null argument.");
1543            return;
1544        }
1545        if (mLayoutFrozen) {
1546            return;
1547        }
1548        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1549        final boolean canScrollVertical = mLayout.canScrollVertically();
1550        if (canScrollHorizontal || canScrollVertical) {
1551            scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
1552        }
1553    }
1554
1555    /**
1556     * Helper method reflect data changes to the state.
1557     * <p>
1558     * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
1559     * but data actually changed.
1560     * <p>
1561     * This method consumes all deferred changes to avoid that case.
1562     */
1563    void consumePendingUpdateOperations() {
1564        if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
1565            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1566            dispatchLayout();
1567            TraceCompat.endSection();
1568            return;
1569        }
1570        if (!mAdapterHelper.hasPendingUpdates()) {
1571            return;
1572        }
1573
1574        // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
1575        // of the visible items is affected and if not, just ignore the change.
1576        if (mAdapterHelper.hasAnyUpdateTypes(UpdateOp.UPDATE) && !mAdapterHelper
1577                .hasAnyUpdateTypes(UpdateOp.ADD | UpdateOp.REMOVE | UpdateOp.MOVE)) {
1578            TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
1579            eatRequestLayout();
1580            mAdapterHelper.preProcess();
1581            if (!mLayoutRequestEaten) {
1582                if (hasUpdatedView()) {
1583                    dispatchLayout();
1584                } else {
1585                    // no need to layout, clean state
1586                    mAdapterHelper.consumePostponedUpdates();
1587                }
1588            }
1589            resumeRequestLayout(true);
1590            TraceCompat.endSection();
1591        } else if (mAdapterHelper.hasPendingUpdates()) {
1592            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1593            dispatchLayout();
1594            TraceCompat.endSection();
1595        }
1596    }
1597
1598    /**
1599     * @return True if an existing view holder needs to be updated
1600     */
1601    private boolean hasUpdatedView() {
1602        final int childCount = mChildHelper.getChildCount();
1603        for (int i = 0; i < childCount; i++) {
1604            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
1605            if (holder == null || holder.shouldIgnore()) {
1606                continue;
1607            }
1608            if (holder.isUpdated()) {
1609                return true;
1610            }
1611        }
1612        return false;
1613    }
1614
1615    /**
1616     * Does not perform bounds checking. Used by internal methods that have already validated input.
1617     * <p>
1618     * It also reports any unused scroll request to the related EdgeEffect.
1619     *
1620     * @param x The amount of horizontal scroll request
1621     * @param y The amount of vertical scroll request
1622     * @param ev The originating MotionEvent, or null if not from a touch event.
1623     *
1624     * @return Whether any scroll was consumed in either direction.
1625     */
1626    boolean scrollByInternal(int x, int y, MotionEvent ev) {
1627        int unconsumedX = 0, unconsumedY = 0;
1628        int consumedX = 0, consumedY = 0;
1629
1630        consumePendingUpdateOperations();
1631        if (mAdapter != null) {
1632            eatRequestLayout();
1633            onEnterLayoutOrScroll();
1634            TraceCompat.beginSection(TRACE_SCROLL_TAG);
1635            if (x != 0) {
1636                consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
1637                unconsumedX = x - consumedX;
1638            }
1639            if (y != 0) {
1640                consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
1641                unconsumedY = y - consumedY;
1642            }
1643            TraceCompat.endSection();
1644            repositionShadowingViews();
1645            onExitLayoutOrScroll();
1646            resumeRequestLayout(false);
1647        }
1648        if (!mItemDecorations.isEmpty()) {
1649            invalidate();
1650        }
1651
1652        if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
1653            // Update the last touch co-ords, taking any scroll offset into account
1654            mLastTouchX -= mScrollOffset[0];
1655            mLastTouchY -= mScrollOffset[1];
1656            if (ev != null) {
1657                ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
1658            }
1659            mNestedOffsets[0] += mScrollOffset[0];
1660            mNestedOffsets[1] += mScrollOffset[1];
1661        } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
1662            if (ev != null) {
1663                pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
1664            }
1665            considerReleasingGlowsOnScroll(x, y);
1666        }
1667        if (consumedX != 0 || consumedY != 0) {
1668            dispatchOnScrolled(consumedX, consumedY);
1669        }
1670        if (!awakenScrollBars()) {
1671            invalidate();
1672        }
1673        return consumedX != 0 || consumedY != 0;
1674    }
1675
1676    /**
1677     * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
1678     * range. This value is used to compute the length of the thumb within the scrollbar's track.
1679     * </p>
1680     *
1681     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1682     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
1683     *
1684     * <p>Default implementation returns 0.</p>
1685     *
1686     * <p>If you want to support scroll bars, override
1687     * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
1688     * LayoutManager. </p>
1689     *
1690     * @return The horizontal offset of the scrollbar's thumb
1691     * @see android.support.v7.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset
1692     * (RecyclerView.State)
1693     */
1694    @Override
1695    public int computeHorizontalScrollOffset() {
1696        if (mLayout == null) {
1697            return 0;
1698        }
1699        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
1700    }
1701
1702    /**
1703     * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
1704     * horizontal range. This value is used to compute the length of the thumb within the
1705     * scrollbar's track.</p>
1706     *
1707     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1708     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
1709     *
1710     * <p>Default implementation returns 0.</p>
1711     *
1712     * <p>If you want to support scroll bars, override
1713     * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
1714     * LayoutManager.</p>
1715     *
1716     * @return The horizontal extent of the scrollbar's thumb
1717     * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
1718     */
1719    @Override
1720    public int computeHorizontalScrollExtent() {
1721        if (mLayout == null) {
1722            return 0;
1723        }
1724        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
1725    }
1726
1727    /**
1728     * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
1729     *
1730     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1731     * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
1732     *
1733     * <p>Default implementation returns 0.</p>
1734     *
1735     * <p>If you want to support scroll bars, override
1736     * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
1737     * LayoutManager.</p>
1738     *
1739     * @return The total horizontal range represented by the vertical scrollbar
1740     * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
1741     */
1742    @Override
1743    public int computeHorizontalScrollRange() {
1744        if (mLayout == null) {
1745            return 0;
1746        }
1747        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
1748    }
1749
1750    /**
1751     * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
1752     * This value is used to compute the length of the thumb within the scrollbar's track. </p>
1753     *
1754     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1755     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
1756     *
1757     * <p>Default implementation returns 0.</p>
1758     *
1759     * <p>If you want to support scroll bars, override
1760     * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
1761     * LayoutManager.</p>
1762     *
1763     * @return The vertical offset of the scrollbar's thumb
1764     * @see android.support.v7.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset
1765     * (RecyclerView.State)
1766     */
1767    @Override
1768    public int computeVerticalScrollOffset() {
1769        if (mLayout == null) {
1770            return 0;
1771        }
1772        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
1773    }
1774
1775    /**
1776     * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
1777     * This value is used to compute the length of the thumb within the scrollbar's track.</p>
1778     *
1779     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1780     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
1781     *
1782     * <p>Default implementation returns 0.</p>
1783     *
1784     * <p>If you want to support scroll bars, override
1785     * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
1786     * LayoutManager.</p>
1787     *
1788     * @return The vertical extent of the scrollbar's thumb
1789     * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
1790     */
1791    @Override
1792    public int computeVerticalScrollExtent() {
1793        if (mLayout == null) {
1794            return 0;
1795        }
1796        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
1797    }
1798
1799    /**
1800     * <p>Compute the vertical range that the vertical scrollbar represents.</p>
1801     *
1802     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1803     * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
1804     *
1805     * <p>Default implementation returns 0.</p>
1806     *
1807     * <p>If you want to support scroll bars, override
1808     * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
1809     * LayoutManager.</p>
1810     *
1811     * @return The total vertical range represented by the vertical scrollbar
1812     * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
1813     */
1814    @Override
1815    public int computeVerticalScrollRange() {
1816        if (mLayout == null) {
1817            return 0;
1818        }
1819        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
1820    }
1821
1822
1823    void eatRequestLayout() {
1824        mEatRequestLayout++;
1825        if (mEatRequestLayout == 1 && !mLayoutFrozen) {
1826            mLayoutRequestEaten = false;
1827        }
1828    }
1829
1830    void resumeRequestLayout(boolean performLayoutChildren) {
1831        if (mEatRequestLayout < 1) {
1832            //noinspection PointlessBooleanExpression
1833            if (DEBUG) {
1834                throw new IllegalStateException("invalid eat request layout count");
1835            }
1836            mEatRequestLayout = 1;
1837        }
1838        if (!performLayoutChildren) {
1839            // Reset the layout request eaten counter.
1840            // This is necessary since eatRequest calls can be nested in which case the other
1841            // call will override the inner one.
1842            // for instance:
1843            // eat layout for process adapter updates
1844            //   eat layout for dispatchLayout
1845            //     a bunch of req layout calls arrive
1846
1847            mLayoutRequestEaten = false;
1848        }
1849        if (mEatRequestLayout == 1) {
1850            // when layout is frozen we should delay dispatchLayout()
1851            if (performLayoutChildren && mLayoutRequestEaten && !mLayoutFrozen &&
1852                    mLayout != null && mAdapter != null) {
1853                dispatchLayout();
1854            }
1855            if (!mLayoutFrozen) {
1856                mLayoutRequestEaten = false;
1857            }
1858        }
1859        mEatRequestLayout--;
1860    }
1861
1862    /**
1863     * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
1864     * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
1865     * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
1866     * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
1867     * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
1868     * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
1869     * called.
1870     *
1871     * <p>
1872     * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
1873     * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
1874     * RecyclerView, State, int)}.
1875     * <p>
1876     * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
1877     * stop frozen.
1878     * <p>
1879     * Note: Running ItemAnimator is not stopped automatically,  it's caller's
1880     * responsibility to call ItemAnimator.end().
1881     *
1882     * @param frozen   true to freeze layout and scroll, false to re-enable.
1883     */
1884    public void setLayoutFrozen(boolean frozen) {
1885        if (frozen != mLayoutFrozen) {
1886            assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
1887            if (!frozen) {
1888                mLayoutFrozen = false;
1889                if (mLayoutRequestEaten && mLayout != null && mAdapter != null) {
1890                    requestLayout();
1891                }
1892                mLayoutRequestEaten = false;
1893            } else {
1894                final long now = SystemClock.uptimeMillis();
1895                MotionEvent cancelEvent = MotionEvent.obtain(now, now,
1896                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
1897                onTouchEvent(cancelEvent);
1898                mLayoutFrozen = true;
1899                mIgnoreMotionEventTillDown = true;
1900                stopScroll();
1901            }
1902        }
1903    }
1904
1905    /**
1906     * Returns true if layout and scroll are frozen.
1907     *
1908     * @return true if layout and scroll are frozen
1909     * @see #setLayoutFrozen(boolean)
1910     */
1911    public boolean isLayoutFrozen() {
1912        return mLayoutFrozen;
1913    }
1914
1915    /**
1916     * Animate a scroll by the given amount of pixels along either axis.
1917     *
1918     * @param dx Pixels to scroll horizontally
1919     * @param dy Pixels to scroll vertically
1920     */
1921    public void smoothScrollBy(int dx, int dy) {
1922        if (mLayout == null) {
1923            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
1924                    "Call setLayoutManager with a non-null argument.");
1925            return;
1926        }
1927        if (mLayoutFrozen) {
1928            return;
1929        }
1930        if (!mLayout.canScrollHorizontally()) {
1931            dx = 0;
1932        }
1933        if (!mLayout.canScrollVertically()) {
1934            dy = 0;
1935        }
1936        if (dx != 0 || dy != 0) {
1937            mViewFlinger.smoothScrollBy(dx, dy);
1938        }
1939    }
1940
1941    /**
1942     * Begin a standard fling with an initial velocity along each axis in pixels per second.
1943     * If the velocity given is below the system-defined minimum this method will return false
1944     * and no fling will occur.
1945     *
1946     * @param velocityX Initial horizontal velocity in pixels per second
1947     * @param velocityY Initial vertical velocity in pixels per second
1948     * @return true if the fling was started, false if the velocity was too low to fling or
1949     * LayoutManager does not support scrolling in the axis fling is issued.
1950     *
1951     * @see LayoutManager#canScrollVertically()
1952     * @see LayoutManager#canScrollHorizontally()
1953     */
1954    public boolean fling(int velocityX, int velocityY) {
1955        if (mLayout == null) {
1956            Log.e(TAG, "Cannot fling without a LayoutManager set. " +
1957                    "Call setLayoutManager with a non-null argument.");
1958            return false;
1959        }
1960        if (mLayoutFrozen) {
1961            return false;
1962        }
1963
1964        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1965        final boolean canScrollVertical = mLayout.canScrollVertically();
1966
1967        if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
1968            velocityX = 0;
1969        }
1970        if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
1971            velocityY = 0;
1972        }
1973        if (velocityX == 0 && velocityY == 0) {
1974            // If we don't have any velocity, return false
1975            return false;
1976        }
1977
1978        if (!dispatchNestedPreFling(velocityX, velocityY)) {
1979            final boolean canScroll = canScrollHorizontal || canScrollVertical;
1980            dispatchNestedFling(velocityX, velocityY, canScroll);
1981
1982            if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
1983                return true;
1984            }
1985
1986            if (canScroll) {
1987                velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
1988                velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
1989                mViewFlinger.fling(velocityX, velocityY);
1990                return true;
1991            }
1992        }
1993        return false;
1994    }
1995
1996    /**
1997     * Stop any current scroll in progress, such as one started by
1998     * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
1999     */
2000    public void stopScroll() {
2001        setScrollState(SCROLL_STATE_IDLE);
2002        stopScrollersInternal();
2003    }
2004
2005    /**
2006     * Similar to {@link #stopScroll()} but does not set the state.
2007     */
2008    private void stopScrollersInternal() {
2009        mViewFlinger.stop();
2010        if (mLayout != null) {
2011            mLayout.stopSmoothScroller();
2012        }
2013    }
2014
2015    /**
2016     * Returns the minimum velocity to start a fling.
2017     *
2018     * @return The minimum velocity to start a fling
2019     */
2020    public int getMinFlingVelocity() {
2021        return mMinFlingVelocity;
2022    }
2023
2024
2025    /**
2026     * Returns the maximum fling velocity used by this RecyclerView.
2027     *
2028     * @return The maximum fling velocity used by this RecyclerView.
2029     */
2030    public int getMaxFlingVelocity() {
2031        return mMaxFlingVelocity;
2032    }
2033
2034    /**
2035     * Apply a pull to relevant overscroll glow effects
2036     */
2037    private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
2038        boolean invalidate = false;
2039        if (overscrollX < 0) {
2040            ensureLeftGlow();
2041            if (mLeftGlow.onPull(-overscrollX / getWidth(), 1f - y  / getHeight())) {
2042                invalidate = true;
2043            }
2044        } else if (overscrollX > 0) {
2045            ensureRightGlow();
2046            if (mRightGlow.onPull(overscrollX / getWidth(), y / getHeight())) {
2047                invalidate = true;
2048            }
2049        }
2050
2051        if (overscrollY < 0) {
2052            ensureTopGlow();
2053            if (mTopGlow.onPull(-overscrollY / getHeight(), x / getWidth())) {
2054                invalidate = true;
2055            }
2056        } else if (overscrollY > 0) {
2057            ensureBottomGlow();
2058            if (mBottomGlow.onPull(overscrollY / getHeight(), 1f - x / getWidth())) {
2059                invalidate = true;
2060            }
2061        }
2062
2063        if (invalidate || overscrollX != 0 || overscrollY != 0) {
2064            ViewCompat.postInvalidateOnAnimation(this);
2065        }
2066    }
2067
2068    private void releaseGlows() {
2069        boolean needsInvalidate = false;
2070        if (mLeftGlow != null) needsInvalidate = mLeftGlow.onRelease();
2071        if (mTopGlow != null) needsInvalidate |= mTopGlow.onRelease();
2072        if (mRightGlow != null) needsInvalidate |= mRightGlow.onRelease();
2073        if (mBottomGlow != null) needsInvalidate |= mBottomGlow.onRelease();
2074        if (needsInvalidate) {
2075            ViewCompat.postInvalidateOnAnimation(this);
2076        }
2077    }
2078
2079    void considerReleasingGlowsOnScroll(int dx, int dy) {
2080        boolean needsInvalidate = false;
2081        if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
2082            needsInvalidate = mLeftGlow.onRelease();
2083        }
2084        if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
2085            needsInvalidate |= mRightGlow.onRelease();
2086        }
2087        if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
2088            needsInvalidate |= mTopGlow.onRelease();
2089        }
2090        if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
2091            needsInvalidate |= mBottomGlow.onRelease();
2092        }
2093        if (needsInvalidate) {
2094            ViewCompat.postInvalidateOnAnimation(this);
2095        }
2096    }
2097
2098    void absorbGlows(int velocityX, int velocityY) {
2099        if (velocityX < 0) {
2100            ensureLeftGlow();
2101            mLeftGlow.onAbsorb(-velocityX);
2102        } else if (velocityX > 0) {
2103            ensureRightGlow();
2104            mRightGlow.onAbsorb(velocityX);
2105        }
2106
2107        if (velocityY < 0) {
2108            ensureTopGlow();
2109            mTopGlow.onAbsorb(-velocityY);
2110        } else if (velocityY > 0) {
2111            ensureBottomGlow();
2112            mBottomGlow.onAbsorb(velocityY);
2113        }
2114
2115        if (velocityX != 0 || velocityY != 0) {
2116            ViewCompat.postInvalidateOnAnimation(this);
2117        }
2118    }
2119
2120    void ensureLeftGlow() {
2121        if (mLeftGlow != null) {
2122            return;
2123        }
2124        mLeftGlow = new EdgeEffectCompat(getContext());
2125        if (mClipToPadding) {
2126            mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2127                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2128        } else {
2129            mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2130        }
2131    }
2132
2133    void ensureRightGlow() {
2134        if (mRightGlow != null) {
2135            return;
2136        }
2137        mRightGlow = new EdgeEffectCompat(getContext());
2138        if (mClipToPadding) {
2139            mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2140                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2141        } else {
2142            mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2143        }
2144    }
2145
2146    void ensureTopGlow() {
2147        if (mTopGlow != null) {
2148            return;
2149        }
2150        mTopGlow = new EdgeEffectCompat(getContext());
2151        if (mClipToPadding) {
2152            mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2153                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2154        } else {
2155            mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2156        }
2157
2158    }
2159
2160    void ensureBottomGlow() {
2161        if (mBottomGlow != null) {
2162            return;
2163        }
2164        mBottomGlow = new EdgeEffectCompat(getContext());
2165        if (mClipToPadding) {
2166            mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2167                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2168        } else {
2169            mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2170        }
2171    }
2172
2173    void invalidateGlows() {
2174        mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
2175    }
2176
2177    /**
2178     * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
2179     * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
2180     * that differs from other ViewGroups.
2181     * <p>
2182     * It first does a focus search within the RecyclerView. If this search finds a View that is in
2183     * the focus direction with respect to the currently focused View, RecyclerView returns that
2184     * child as the next focus target. When it cannot find such child, it calls
2185     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
2186     * in the focus search direction. If LayoutManager adds a View that matches the
2187     * focus search criteria, it will be returned as the focus search result. Otherwise,
2188     * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
2189     * <p>
2190     * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
2191     * is not in the focus direction is still valid focus target which may not be the desired
2192     * behavior if the Adapter has more children in the focus direction. To handle this case,
2193     * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
2194     * focus search in that direction. If there are no Views to gain focus, it will call
2195     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
2196     * focus search with the original (relative) direction. This allows RecyclerView to provide
2197     * better candidates to the focus search while still allowing the view system to take focus from
2198     * the RecyclerView and give it to a more suitable child if such child exists.
2199     *
2200     * @param focused The view that currently has focus
2201     * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
2202     * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
2203     * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
2204     *
2205     * @return A new View that can be the next focus after the focused View
2206     */
2207    @Override
2208    public View focusSearch(View focused, int direction) {
2209        View result = mLayout.onInterceptFocusSearch(focused, direction);
2210        if (result != null) {
2211            return result;
2212        }
2213        final boolean canRunFocusFailure = mAdapter != null && mLayout != null
2214                && !isComputingLayout() && !mLayoutFrozen;
2215
2216        final FocusFinder ff = FocusFinder.getInstance();
2217        if (canRunFocusFailure
2218                && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
2219            // convert direction to absolute direction and see if we have a view there and if not
2220            // tell LayoutManager to add if it can.
2221            boolean needsFocusFailureLayout = false;
2222            if (mLayout.canScrollVertically()) {
2223                final int absDir =
2224                        direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
2225                final View found = ff.findNextFocus(this, focused, absDir);
2226                needsFocusFailureLayout = found == null;
2227            }
2228            if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
2229                boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
2230                final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
2231                        ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2232                final View found = ff.findNextFocus(this, focused, absDir);
2233                needsFocusFailureLayout = found == null;
2234            }
2235            if (needsFocusFailureLayout) {
2236                consumePendingUpdateOperations();
2237                final View focusedItemView = findContainingItemView(focused);
2238                if (focusedItemView == null) {
2239                    // panic, focused view is not a child anymore, cannot call super.
2240                    return null;
2241                }
2242                eatRequestLayout();
2243                mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2244                resumeRequestLayout(false);
2245            }
2246            result = ff.findNextFocus(this, focused, direction);
2247        } else {
2248            result = ff.findNextFocus(this, focused, direction);
2249            if (result == null && canRunFocusFailure) {
2250                consumePendingUpdateOperations();
2251                final View focusedItemView = findContainingItemView(focused);
2252                if (focusedItemView == null) {
2253                    // panic, focused view is not a child anymore, cannot call super.
2254                    return null;
2255                }
2256                eatRequestLayout();
2257                result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2258                resumeRequestLayout(false);
2259            }
2260        }
2261        return isPreferredNextFocus(focused, result, direction)
2262                ? result : super.focusSearch(focused, direction);
2263    }
2264
2265    /**
2266     * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
2267     * assign it as the next focus View instead of letting view hierarchy decide.
2268     * A good candidate means a View that is aligned in the focus direction wrt the focused View
2269     * and is not the RecyclerView itself.
2270     * When this method returns false, RecyclerView will let the parent make the decision so the
2271     * same View may still get the focus as a result of that search.
2272     */
2273    private boolean isPreferredNextFocus(View focused, View next, int direction) {
2274        if (next == null || next == this) {
2275            return false;
2276        }
2277        if (focused == null) {
2278            return true;
2279        }
2280
2281        if(direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
2282            final boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
2283            final int absHorizontal = (direction == View.FOCUS_FORWARD) ^ rtl
2284                    ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2285            if (isPreferredNextFocusAbsolute(focused, next, absHorizontal)) {
2286                return true;
2287            }
2288            if (direction == View.FOCUS_FORWARD) {
2289                return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_DOWN);
2290            } else {
2291                return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_UP);
2292            }
2293        } else {
2294            return isPreferredNextFocusAbsolute(focused, next, direction);
2295        }
2296
2297    }
2298
2299    /**
2300     * Logic taken from FocusSearch#isCandidate
2301     */
2302    private boolean isPreferredNextFocusAbsolute(View focused, View next, int direction) {
2303        mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2304        mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
2305        offsetDescendantRectToMyCoords(focused, mTempRect);
2306        offsetDescendantRectToMyCoords(next, mTempRect2);
2307        switch (direction) {
2308            case View.FOCUS_LEFT:
2309                return (mTempRect.right > mTempRect2.right
2310                        || mTempRect.left >= mTempRect2.right)
2311                        && mTempRect.left > mTempRect2.left;
2312            case View.FOCUS_RIGHT:
2313                return (mTempRect.left < mTempRect2.left
2314                        || mTempRect.right <= mTempRect2.left)
2315                        && mTempRect.right < mTempRect2.right;
2316            case View.FOCUS_UP:
2317                return (mTempRect.bottom > mTempRect2.bottom
2318                        || mTempRect.top >= mTempRect2.bottom)
2319                        && mTempRect.top > mTempRect2.top;
2320            case View.FOCUS_DOWN:
2321                return (mTempRect.top < mTempRect2.top
2322                        || mTempRect.bottom <= mTempRect2.top)
2323                        && mTempRect.bottom < mTempRect2.bottom;
2324        }
2325        throw new IllegalArgumentException("direction must be absolute. received:" + direction);
2326    }
2327
2328    @Override
2329    public void requestChildFocus(View child, View focused) {
2330        if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
2331            mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2332
2333            // get item decor offsets w/o refreshing. If they are invalid, there will be another
2334            // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
2335            // View in viewport.
2336            final ViewGroup.LayoutParams focusedLayoutParams = focused.getLayoutParams();
2337            if (focusedLayoutParams instanceof LayoutParams) {
2338                // if focused child has item decors, use them. Otherwise, ignore.
2339                final LayoutParams lp = (LayoutParams) focusedLayoutParams;
2340                if (!lp.mInsetsDirty) {
2341                    final Rect insets = lp.mDecorInsets;
2342                    mTempRect.left -= insets.left;
2343                    mTempRect.right += insets.right;
2344                    mTempRect.top -= insets.top;
2345                    mTempRect.bottom += insets.bottom;
2346                }
2347            }
2348
2349            offsetDescendantRectToMyCoords(focused, mTempRect);
2350            offsetRectIntoDescendantCoords(child, mTempRect);
2351            requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete);
2352        }
2353        super.requestChildFocus(child, focused);
2354    }
2355
2356    @Override
2357    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
2358        return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
2359    }
2360
2361    @Override
2362    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
2363        if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
2364            super.addFocusables(views, direction, focusableMode);
2365        }
2366    }
2367
2368    @Override
2369    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
2370        if (isComputingLayout()) {
2371            // if we are in the middle of a layout calculation, don't let any child take focus.
2372            // RV will handle it after layout calculation is finished.
2373            return false;
2374        }
2375        return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
2376    }
2377
2378    @Override
2379    protected void onAttachedToWindow() {
2380        super.onAttachedToWindow();
2381        mLayoutOrScrollCounter = 0;
2382        mIsAttached = true;
2383        mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
2384        if (mLayout != null) {
2385            mLayout.dispatchAttachedToWindow(this);
2386        }
2387        mPostedAnimatorRunner = false;
2388        if (ALLOW_PREFETCHING && sFrameIntervalNanos == 0) {
2389            // We only query the display/refresh rate once, since it's an expensive binder call
2390            float refreshRate = 60.0f;
2391            Display display = ViewCompat.getDisplay(this);
2392            if (!isInEditMode() && display != null) {
2393                float displayRefreshRate = display.getRefreshRate();
2394                if (displayRefreshRate >= 30.0f) {
2395                    // break 60 fps assumption if data appears good
2396                    refreshRate = displayRefreshRate;
2397                }
2398            }
2399            sFrameIntervalNanos = (long) (1000000000 / refreshRate);
2400        }
2401    }
2402
2403    @Override
2404    protected void onDetachedFromWindow() {
2405        super.onDetachedFromWindow();
2406        if (mItemAnimator != null) {
2407            mItemAnimator.endAnimations();
2408        }
2409        stopScroll();
2410        mIsAttached = false;
2411        if (mLayout != null) {
2412            mLayout.dispatchDetachedFromWindow(this, mRecycler);
2413        }
2414        mPendingAccessibilityImportanceChange.clear();
2415        removeCallbacks(mItemAnimatorRunner);
2416        mViewInfoStore.onDetach();
2417    }
2418
2419    /**
2420     * Returns true if RecyclerView is attached to window.
2421     */
2422    // @override
2423    public boolean isAttachedToWindow() {
2424        return mIsAttached;
2425    }
2426
2427    /**
2428     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2429     * {@link IllegalStateException} if it <b>is not</b>.
2430     *
2431     * @param message The message for the exception. Can be null.
2432     * @see #assertNotInLayoutOrScroll(String)
2433     */
2434    void assertInLayoutOrScroll(String message) {
2435        if (!isComputingLayout()) {
2436            if (message == null) {
2437                throw new IllegalStateException("Cannot call this method unless RecyclerView is "
2438                        + "computing a layout or scrolling");
2439            }
2440            throw new IllegalStateException(message);
2441
2442        }
2443    }
2444
2445    /**
2446     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2447     * {@link IllegalStateException} if it <b>is</b>.
2448     *
2449     * @param message The message for the exception. Can be null.
2450     * @see #assertInLayoutOrScroll(String)
2451     */
2452    void assertNotInLayoutOrScroll(String message) {
2453        if (isComputingLayout()) {
2454            if (message == null) {
2455                throw new IllegalStateException("Cannot call this method while RecyclerView is "
2456                        + "computing a layout or scrolling");
2457            }
2458            throw new IllegalStateException(message);
2459        }
2460        if (mDispatchScrollCounter > 0) {
2461            Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might be run"
2462                    + " during a measure & layout pass where you cannot change the RecyclerView"
2463                    + " data. Any method call that might change the structure of the RecyclerView"
2464                    + " or the adapter contents should be postponed to the next frame.",
2465                    new IllegalStateException(""));
2466        }
2467    }
2468
2469    /**
2470     * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
2471     * to child views or this view's standard scrolling behavior.
2472     *
2473     * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
2474     * returns true from
2475     * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
2476     * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
2477     * for each incoming MotionEvent until the end of the gesture.</p>
2478     *
2479     * @param listener Listener to add
2480     * @see SimpleOnItemTouchListener
2481     */
2482    public void addOnItemTouchListener(OnItemTouchListener listener) {
2483        mOnItemTouchListeners.add(listener);
2484    }
2485
2486    /**
2487     * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
2488     *
2489     * @param listener Listener to remove
2490     */
2491    public void removeOnItemTouchListener(OnItemTouchListener listener) {
2492        mOnItemTouchListeners.remove(listener);
2493        if (mActiveOnItemTouchListener == listener) {
2494            mActiveOnItemTouchListener = null;
2495        }
2496    }
2497
2498    private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
2499        final int action = e.getAction();
2500        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
2501            mActiveOnItemTouchListener = null;
2502        }
2503
2504        final int listenerCount = mOnItemTouchListeners.size();
2505        for (int i = 0; i < listenerCount; i++) {
2506            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2507            if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
2508                mActiveOnItemTouchListener = listener;
2509                return true;
2510            }
2511        }
2512        return false;
2513    }
2514
2515    private boolean dispatchOnItemTouch(MotionEvent e) {
2516        final int action = e.getAction();
2517        if (mActiveOnItemTouchListener != null) {
2518            if (action == MotionEvent.ACTION_DOWN) {
2519                // Stale state from a previous gesture, we're starting a new one. Clear it.
2520                mActiveOnItemTouchListener = null;
2521            } else {
2522                mActiveOnItemTouchListener.onTouchEvent(this, e);
2523                if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
2524                    // Clean up for the next gesture.
2525                    mActiveOnItemTouchListener = null;
2526                }
2527                return true;
2528            }
2529        }
2530
2531        // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
2532        // as called from onInterceptTouchEvent; skip it.
2533        if (action != MotionEvent.ACTION_DOWN) {
2534            final int listenerCount = mOnItemTouchListeners.size();
2535            for (int i = 0; i < listenerCount; i++) {
2536                final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2537                if (listener.onInterceptTouchEvent(this, e)) {
2538                    mActiveOnItemTouchListener = listener;
2539                    return true;
2540                }
2541            }
2542        }
2543        return false;
2544    }
2545
2546    @Override
2547    public boolean onInterceptTouchEvent(MotionEvent e) {
2548        if (mLayoutFrozen) {
2549            // When layout is frozen,  RV does not intercept the motion event.
2550            // A child view e.g. a button may still get the click.
2551            return false;
2552        }
2553        if (dispatchOnItemTouchIntercept(e)) {
2554            cancelTouch();
2555            return true;
2556        }
2557
2558        if (mLayout == null) {
2559            return false;
2560        }
2561
2562        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2563        final boolean canScrollVertically = mLayout.canScrollVertically();
2564
2565        if (mVelocityTracker == null) {
2566            mVelocityTracker = VelocityTracker.obtain();
2567        }
2568        mVelocityTracker.addMovement(e);
2569
2570        final int action = MotionEventCompat.getActionMasked(e);
2571        final int actionIndex = MotionEventCompat.getActionIndex(e);
2572
2573        switch (action) {
2574            case MotionEvent.ACTION_DOWN:
2575                if (mIgnoreMotionEventTillDown) {
2576                    mIgnoreMotionEventTillDown = false;
2577                }
2578                mScrollPointerId = e.getPointerId(0);
2579                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2580                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2581
2582                if (mScrollState == SCROLL_STATE_SETTLING) {
2583                    getParent().requestDisallowInterceptTouchEvent(true);
2584                    setScrollState(SCROLL_STATE_DRAGGING);
2585                }
2586
2587                // Clear the nested offsets
2588                mNestedOffsets[0] = mNestedOffsets[1] = 0;
2589
2590                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2591                if (canScrollHorizontally) {
2592                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2593                }
2594                if (canScrollVertically) {
2595                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2596                }
2597                startNestedScroll(nestedScrollAxis);
2598                break;
2599
2600            case MotionEventCompat.ACTION_POINTER_DOWN:
2601                mScrollPointerId = e.getPointerId(actionIndex);
2602                mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2603                mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2604                break;
2605
2606            case MotionEvent.ACTION_MOVE: {
2607                final int index = e.findPointerIndex(mScrollPointerId);
2608                if (index < 0) {
2609                    Log.e(TAG, "Error processing scroll; pointer index for id " +
2610                            mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2611                    return false;
2612                }
2613
2614                final int x = (int) (e.getX(index) + 0.5f);
2615                final int y = (int) (e.getY(index) + 0.5f);
2616                if (mScrollState != SCROLL_STATE_DRAGGING) {
2617                    final int dx = x - mInitialTouchX;
2618                    final int dy = y - mInitialTouchY;
2619                    boolean startScroll = false;
2620                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2621                        mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
2622                        startScroll = true;
2623                    }
2624                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2625                        mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
2626                        startScroll = true;
2627                    }
2628                    if (startScroll) {
2629                        setScrollState(SCROLL_STATE_DRAGGING);
2630                    }
2631                }
2632            } break;
2633
2634            case MotionEventCompat.ACTION_POINTER_UP: {
2635                onPointerUp(e);
2636            } break;
2637
2638            case MotionEvent.ACTION_UP: {
2639                mVelocityTracker.clear();
2640                stopNestedScroll();
2641            } break;
2642
2643            case MotionEvent.ACTION_CANCEL: {
2644                cancelTouch();
2645            }
2646        }
2647        return mScrollState == SCROLL_STATE_DRAGGING;
2648    }
2649
2650    @Override
2651    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2652        final int listenerCount = mOnItemTouchListeners.size();
2653        for (int i = 0; i < listenerCount; i++) {
2654            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2655            listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
2656        }
2657        super.requestDisallowInterceptTouchEvent(disallowIntercept);
2658    }
2659
2660    @Override
2661    public boolean onTouchEvent(MotionEvent e) {
2662        if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
2663            return false;
2664        }
2665        if (dispatchOnItemTouch(e)) {
2666            cancelTouch();
2667            return true;
2668        }
2669
2670        if (mLayout == null) {
2671            return false;
2672        }
2673
2674        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2675        final boolean canScrollVertically = mLayout.canScrollVertically();
2676
2677        if (mVelocityTracker == null) {
2678            mVelocityTracker = VelocityTracker.obtain();
2679        }
2680        boolean eventAddedToVelocityTracker = false;
2681
2682        final MotionEvent vtev = MotionEvent.obtain(e);
2683        final int action = MotionEventCompat.getActionMasked(e);
2684        final int actionIndex = MotionEventCompat.getActionIndex(e);
2685
2686        if (action == MotionEvent.ACTION_DOWN) {
2687            mNestedOffsets[0] = mNestedOffsets[1] = 0;
2688        }
2689        vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
2690
2691        switch (action) {
2692            case MotionEvent.ACTION_DOWN: {
2693                mScrollPointerId = e.getPointerId(0);
2694                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2695                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2696
2697                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2698                if (canScrollHorizontally) {
2699                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2700                }
2701                if (canScrollVertically) {
2702                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2703                }
2704                startNestedScroll(nestedScrollAxis);
2705            } break;
2706
2707            case MotionEventCompat.ACTION_POINTER_DOWN: {
2708                mScrollPointerId = e.getPointerId(actionIndex);
2709                mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2710                mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2711            } break;
2712
2713            case MotionEvent.ACTION_MOVE: {
2714                final int index = e.findPointerIndex(mScrollPointerId);
2715                if (index < 0) {
2716                    Log.e(TAG, "Error processing scroll; pointer index for id " +
2717                            mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2718                    return false;
2719                }
2720
2721                final int x = (int) (e.getX(index) + 0.5f);
2722                final int y = (int) (e.getY(index) + 0.5f);
2723                int dx = mLastTouchX - x;
2724                int dy = mLastTouchY - y;
2725
2726                if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
2727                    dx -= mScrollConsumed[0];
2728                    dy -= mScrollConsumed[1];
2729                    vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
2730                    // Updated the nested offsets
2731                    mNestedOffsets[0] += mScrollOffset[0];
2732                    mNestedOffsets[1] += mScrollOffset[1];
2733                }
2734
2735                if (mScrollState != SCROLL_STATE_DRAGGING) {
2736                    boolean startScroll = false;
2737                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2738                        if (dx > 0) {
2739                            dx -= mTouchSlop;
2740                        } else {
2741                            dx += mTouchSlop;
2742                        }
2743                        startScroll = true;
2744                    }
2745                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2746                        if (dy > 0) {
2747                            dy -= mTouchSlop;
2748                        } else {
2749                            dy += mTouchSlop;
2750                        }
2751                        startScroll = true;
2752                    }
2753                    if (startScroll) {
2754                        setScrollState(SCROLL_STATE_DRAGGING);
2755                    }
2756                }
2757
2758                if (mScrollState == SCROLL_STATE_DRAGGING) {
2759                    mLastTouchX = x - mScrollOffset[0];
2760                    mLastTouchY = y - mScrollOffset[1];
2761
2762                    if (scrollByInternal(
2763                            canScrollHorizontally ? dx : 0,
2764                            canScrollVertically ? dy : 0,
2765                            vtev)) {
2766                        getParent().requestDisallowInterceptTouchEvent(true);
2767                    }
2768                    if (ALLOW_PREFETCHING) {
2769                        mViewPrefetcher.postFromTraversal(dx, dy);
2770                    }
2771                }
2772            } break;
2773
2774            case MotionEventCompat.ACTION_POINTER_UP: {
2775                onPointerUp(e);
2776            } break;
2777
2778            case MotionEvent.ACTION_UP: {
2779                mVelocityTracker.addMovement(vtev);
2780                eventAddedToVelocityTracker = true;
2781                mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
2782                final float xvel = canScrollHorizontally ?
2783                        -VelocityTrackerCompat.getXVelocity(mVelocityTracker, mScrollPointerId) : 0;
2784                final float yvel = canScrollVertically ?
2785                        -VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId) : 0;
2786                if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
2787                    setScrollState(SCROLL_STATE_IDLE);
2788                }
2789                resetTouch();
2790            } break;
2791
2792            case MotionEvent.ACTION_CANCEL: {
2793                cancelTouch();
2794            } break;
2795        }
2796
2797        if (!eventAddedToVelocityTracker) {
2798            mVelocityTracker.addMovement(vtev);
2799        }
2800        vtev.recycle();
2801
2802        return true;
2803    }
2804
2805    private void resetTouch() {
2806        if (mVelocityTracker != null) {
2807            mVelocityTracker.clear();
2808        }
2809        stopNestedScroll();
2810        releaseGlows();
2811    }
2812
2813    private void cancelTouch() {
2814        resetTouch();
2815        setScrollState(SCROLL_STATE_IDLE);
2816    }
2817
2818    private void onPointerUp(MotionEvent e) {
2819        final int actionIndex = MotionEventCompat.getActionIndex(e);
2820        if (e.getPointerId(actionIndex) == mScrollPointerId) {
2821            // Pick a new pointer to pick up the slack.
2822            final int newIndex = actionIndex == 0 ? 1 : 0;
2823            mScrollPointerId = e.getPointerId(newIndex);
2824            mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
2825            mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
2826        }
2827    }
2828
2829    // @Override
2830    public boolean onGenericMotionEvent(MotionEvent event) {
2831        if (mLayout == null) {
2832            return false;
2833        }
2834        if (mLayoutFrozen) {
2835            return false;
2836        }
2837        if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
2838            if (event.getAction() == MotionEventCompat.ACTION_SCROLL) {
2839                final float vScroll, hScroll;
2840                if (mLayout.canScrollVertically()) {
2841                    // Inverse the sign of the vertical scroll to align the scroll orientation
2842                    // with AbsListView.
2843                    vScroll = -MotionEventCompat
2844                            .getAxisValue(event, MotionEventCompat.AXIS_VSCROLL);
2845                } else {
2846                    vScroll = 0f;
2847                }
2848                if (mLayout.canScrollHorizontally()) {
2849                    hScroll = MotionEventCompat
2850                            .getAxisValue(event, MotionEventCompat.AXIS_HSCROLL);
2851                } else {
2852                    hScroll = 0f;
2853                }
2854
2855                if (vScroll != 0 || hScroll != 0) {
2856                    final float scrollFactor = getScrollFactor();
2857                    scrollByInternal((int) (hScroll * scrollFactor),
2858                            (int) (vScroll * scrollFactor), event);
2859                }
2860            }
2861        }
2862        return false;
2863    }
2864
2865    /**
2866     * Ported from View.getVerticalScrollFactor.
2867     */
2868    private float getScrollFactor() {
2869        if (mScrollFactor == Float.MIN_VALUE) {
2870            TypedValue outValue = new TypedValue();
2871            if (getContext().getTheme().resolveAttribute(
2872                    android.R.attr.listPreferredItemHeight, outValue, true)) {
2873                mScrollFactor = outValue.getDimension(
2874                        getContext().getResources().getDisplayMetrics());
2875            } else {
2876                return 0; //listPreferredItemHeight is not defined, no generic scrolling
2877            }
2878        }
2879        return mScrollFactor;
2880    }
2881
2882    @Override
2883    protected void onMeasure(int widthSpec, int heightSpec) {
2884        if (mLayout == null) {
2885            defaultOnMeasure(widthSpec, heightSpec);
2886            return;
2887        }
2888        if (mLayout.mAutoMeasure) {
2889            final int widthMode = MeasureSpec.getMode(widthSpec);
2890            final int heightMode = MeasureSpec.getMode(heightSpec);
2891            final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
2892                    && heightMode == MeasureSpec.EXACTLY;
2893            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
2894            if (skipMeasure || mAdapter == null) {
2895                return;
2896            }
2897            if (mState.mLayoutStep == State.STEP_START) {
2898                dispatchLayoutStep1();
2899            }
2900            // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
2901            // consistency
2902            mLayout.setMeasureSpecs(widthSpec, heightSpec);
2903            mState.mIsMeasuring = true;
2904            dispatchLayoutStep2();
2905
2906            // now we can get the width and height from the children.
2907            mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
2908
2909            // if RecyclerView has non-exact width and height and if there is at least one child
2910            // which also has non-exact width & height, we have to re-measure.
2911            if (mLayout.shouldMeasureTwice()) {
2912                mLayout.setMeasureSpecs(
2913                        MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
2914                        MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
2915                mState.mIsMeasuring = true;
2916                dispatchLayoutStep2();
2917                // now we can get the width and height from the children.
2918                mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
2919            }
2920        } else {
2921            if (mHasFixedSize) {
2922                mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
2923                return;
2924            }
2925            // custom onMeasure
2926            if (mAdapterUpdateDuringMeasure) {
2927                eatRequestLayout();
2928                processAdapterUpdatesAndSetAnimationFlags();
2929
2930                if (mState.mRunPredictiveAnimations) {
2931                    mState.mInPreLayout = true;
2932                } else {
2933                    // consume remaining updates to provide a consistent state with the layout pass.
2934                    mAdapterHelper.consumeUpdatesInOnePass();
2935                    mState.mInPreLayout = false;
2936                }
2937                mAdapterUpdateDuringMeasure = false;
2938                resumeRequestLayout(false);
2939            }
2940
2941            if (mAdapter != null) {
2942                mState.mItemCount = mAdapter.getItemCount();
2943            } else {
2944                mState.mItemCount = 0;
2945            }
2946            eatRequestLayout();
2947            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
2948            resumeRequestLayout(false);
2949            mState.mInPreLayout = false; // clear
2950        }
2951    }
2952
2953    /**
2954     * Used when onMeasure is called before layout manager is set
2955     */
2956    void defaultOnMeasure(int widthSpec, int heightSpec) {
2957        // calling LayoutManager here is not pretty but that API is already public and it is better
2958        // than creating another method since this is internal.
2959        final int width = LayoutManager.chooseSize(widthSpec,
2960                getPaddingLeft() + getPaddingRight(),
2961                ViewCompat.getMinimumWidth(this));
2962        final int height = LayoutManager.chooseSize(heightSpec,
2963                getPaddingTop() + getPaddingBottom(),
2964                ViewCompat.getMinimumHeight(this));
2965
2966        setMeasuredDimension(width, height);
2967    }
2968
2969    @Override
2970    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
2971        super.onSizeChanged(w, h, oldw, oldh);
2972        if (w != oldw || h != oldh) {
2973            invalidateGlows();
2974            // layout's w/h are updated during measure/layout steps.
2975        }
2976    }
2977
2978    /**
2979     * Sets the {@link ItemAnimator} that will handle animations involving changes
2980     * to the items in this RecyclerView. By default, RecyclerView instantiates and
2981     * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
2982     * enabled for the RecyclerView depends on the ItemAnimator and whether
2983     * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
2984     * supports item animations}.
2985     *
2986     * @param animator The ItemAnimator being set. If null, no animations will occur
2987     * when changes occur to the items in this RecyclerView.
2988     */
2989    public void setItemAnimator(ItemAnimator animator) {
2990        if (mItemAnimator != null) {
2991            mItemAnimator.endAnimations();
2992            mItemAnimator.setListener(null);
2993        }
2994        mItemAnimator = animator;
2995        if (mItemAnimator != null) {
2996            mItemAnimator.setListener(mItemAnimatorListener);
2997        }
2998    }
2999
3000    void onEnterLayoutOrScroll() {
3001        mLayoutOrScrollCounter ++;
3002    }
3003
3004    void onExitLayoutOrScroll() {
3005        mLayoutOrScrollCounter --;
3006        if (mLayoutOrScrollCounter < 1) {
3007            if (DEBUG && mLayoutOrScrollCounter < 0) {
3008                throw new IllegalStateException("layout or scroll counter cannot go below zero."
3009                        + "Some calls are not matching");
3010            }
3011            mLayoutOrScrollCounter = 0;
3012            dispatchContentChangedIfNecessary();
3013            dispatchPendingImportantForAccessibilityChanges();
3014        }
3015    }
3016
3017    boolean isAccessibilityEnabled() {
3018        return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
3019    }
3020
3021    private void dispatchContentChangedIfNecessary() {
3022        final int flags = mEatenAccessibilityChangeFlags;
3023        mEatenAccessibilityChangeFlags = 0;
3024        if (flags != 0 && isAccessibilityEnabled()) {
3025            final AccessibilityEvent event = AccessibilityEvent.obtain();
3026            event.setEventType(AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
3027            AccessibilityEventCompat.setContentChangeTypes(event, flags);
3028            sendAccessibilityEventUnchecked(event);
3029        }
3030    }
3031
3032    /**
3033     * Returns whether RecyclerView is currently computing a layout.
3034     * <p>
3035     * If this method returns true, it means that RecyclerView is in a lockdown state and any
3036     * attempt to update adapter contents will result in an exception because adapter contents
3037     * cannot be changed while RecyclerView is trying to compute the layout.
3038     * <p>
3039     * It is very unlikely that your code will be running during this state as it is
3040     * called by the framework when a layout traversal happens or RecyclerView starts to scroll
3041     * in response to system events (touch, accessibility etc).
3042     * <p>
3043     * This case may happen if you have some custom logic to change adapter contents in
3044     * response to a View callback (e.g. focus change callback) which might be triggered during a
3045     * layout calculation. In these cases, you should just postpone the change using a Handler or a
3046     * similar mechanism.
3047     *
3048     * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
3049     *         otherwise
3050     */
3051    public boolean isComputingLayout() {
3052        return mLayoutOrScrollCounter > 0;
3053    }
3054
3055    /**
3056     * Returns true if an accessibility event should not be dispatched now. This happens when an
3057     * accessibility request arrives while RecyclerView does not have a stable state which is very
3058     * hard to handle for a LayoutManager. Instead, this method records necessary information about
3059     * the event and dispatches a window change event after the critical section is finished.
3060     *
3061     * @return True if the accessibility event should be postponed.
3062     */
3063    boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
3064        if (isComputingLayout()) {
3065            int type = 0;
3066            if (event != null) {
3067                type = AccessibilityEventCompat.getContentChangeTypes(event);
3068            }
3069            if (type == 0) {
3070                type = AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED;
3071            }
3072            mEatenAccessibilityChangeFlags |= type;
3073            return true;
3074        }
3075        return false;
3076    }
3077
3078    @Override
3079    public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
3080        if (shouldDeferAccessibilityEvent(event)) {
3081            return;
3082        }
3083        super.sendAccessibilityEventUnchecked(event);
3084    }
3085
3086    /**
3087     * Gets the current ItemAnimator for this RecyclerView. A null return value
3088     * indicates that there is no animator and that item changes will happen without
3089     * any animations. By default, RecyclerView instantiates and
3090     * uses an instance of {@link DefaultItemAnimator}.
3091     *
3092     * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
3093     * when changes occur to the items in this RecyclerView.
3094     */
3095    public ItemAnimator getItemAnimator() {
3096        return mItemAnimator;
3097    }
3098
3099    /**
3100     * Post a runnable to the next frame to run pending item animations. Only the first such
3101     * request will be posted, governed by the mPostedAnimatorRunner flag.
3102     */
3103    void postAnimationRunner() {
3104        if (!mPostedAnimatorRunner && mIsAttached) {
3105            ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
3106            mPostedAnimatorRunner = true;
3107        }
3108    }
3109
3110    private boolean predictiveItemAnimationsEnabled() {
3111        return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
3112    }
3113
3114    /**
3115     * Consumes adapter updates and calculates which type of animations we want to run.
3116     * Called in onMeasure and dispatchLayout.
3117     * <p>
3118     * This method may process only the pre-layout state of updates or all of them.
3119     */
3120    private void processAdapterUpdatesAndSetAnimationFlags() {
3121        if (mDataSetHasChangedAfterLayout) {
3122            // Processing these items have no value since data set changed unexpectedly.
3123            // Instead, we just reset it.
3124            mAdapterHelper.reset();
3125            markKnownViewsInvalid();
3126            mLayout.onItemsChanged(this);
3127        }
3128        // simple animations are a subset of advanced animations (which will cause a
3129        // pre-layout step)
3130        // If layout supports predictive animations, pre-process to decide if we want to run them
3131        if (predictiveItemAnimationsEnabled()) {
3132            mAdapterHelper.preProcess();
3133        } else {
3134            mAdapterHelper.consumeUpdatesInOnePass();
3135        }
3136        boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
3137        mState.mRunSimpleAnimations = mFirstLayoutComplete && mItemAnimator != null &&
3138                (mDataSetHasChangedAfterLayout || animationTypeSupported ||
3139                        mLayout.mRequestedSimpleAnimations) &&
3140                (!mDataSetHasChangedAfterLayout || mAdapter.hasStableIds());
3141        mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations &&
3142                animationTypeSupported && !mDataSetHasChangedAfterLayout &&
3143                predictiveItemAnimationsEnabled();
3144    }
3145
3146    /**
3147     * Wrapper around layoutChildren() that handles animating changes caused by layout.
3148     * Animations work on the assumption that there are five different kinds of items
3149     * in play:
3150     * PERSISTENT: items are visible before and after layout
3151     * REMOVED: items were visible before layout and were removed by the app
3152     * ADDED: items did not exist before layout and were added by the app
3153     * DISAPPEARING: items exist in the data set before/after, but changed from
3154     * visible to non-visible in the process of layout (they were moved off
3155     * screen as a side-effect of other changes)
3156     * APPEARING: items exist in the data set before/after, but changed from
3157     * non-visible to visible in the process of layout (they were moved on
3158     * screen as a side-effect of other changes)
3159     * The overall approach figures out what items exist before/after layout and
3160     * infers one of the five above states for each of the items. Then the animations
3161     * are set up accordingly:
3162     * PERSISTENT views are animated via
3163     * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3164     * DISAPPEARING views are animated via
3165     * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3166     * APPEARING views are animated via
3167     * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3168     * and changed views are animated via
3169     * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
3170     */
3171    void dispatchLayout() {
3172        if (mAdapter == null) {
3173            Log.e(TAG, "No adapter attached; skipping layout");
3174            // leave the state in START
3175            return;
3176        }
3177        if (mLayout == null) {
3178            Log.e(TAG, "No layout manager attached; skipping layout");
3179            // leave the state in START
3180            return;
3181        }
3182        mState.mIsMeasuring = false;
3183        if (mState.mLayoutStep == State.STEP_START) {
3184            dispatchLayoutStep1();
3185            mLayout.setExactMeasureSpecsFrom(this);
3186            dispatchLayoutStep2();
3187        } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth() ||
3188                mLayout.getHeight() != getHeight()) {
3189            // First 2 steps are done in onMeasure but looks like we have to run again due to
3190            // changed size.
3191            mLayout.setExactMeasureSpecsFrom(this);
3192            dispatchLayoutStep2();
3193        } else {
3194            // always make sure we sync them (to ensure mode is exact)
3195            mLayout.setExactMeasureSpecsFrom(this);
3196        }
3197        dispatchLayoutStep3();
3198    }
3199
3200    private void saveFocusInfo() {
3201        View child = null;
3202        if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
3203            child = getFocusedChild();
3204        }
3205
3206        final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
3207        if (focusedVh == null) {
3208            resetFocusInfo();
3209        } else {
3210            mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
3211            mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION :
3212                    focusedVh.getAdapterPosition();
3213            mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
3214        }
3215    }
3216
3217    private void resetFocusInfo() {
3218        mState.mFocusedItemId = NO_ID;
3219        mState.mFocusedItemPosition = NO_POSITION;
3220        mState.mFocusedSubChildId = View.NO_ID;
3221    }
3222
3223    private void recoverFocusFromState() {
3224        if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()) {
3225            return;
3226        }
3227        // only recover focus if RV itself has the focus or the focused view is hidden
3228        if (!isFocused()) {
3229            final View focusedChild = getFocusedChild();
3230            if (focusedChild == null || !mChildHelper.isHidden(focusedChild)) {
3231                return;
3232            }
3233        }
3234        ViewHolder focusTarget = null;
3235        if (mState.mFocusedItemPosition != NO_POSITION) {
3236            focusTarget = findViewHolderForAdapterPosition(mState.mFocusedItemPosition);
3237        }
3238        if (focusTarget == null && mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
3239            focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
3240        }
3241        if (focusTarget == null || focusTarget.itemView.hasFocus() ||
3242                !focusTarget.itemView.hasFocusable()) {
3243            return;
3244        }
3245        // looks like the focused item has been replaced with another view that represents the
3246        // same item in the adapter. Request focus on that.
3247        View viewToFocus = focusTarget.itemView;
3248        if (mState.mFocusedSubChildId != NO_ID) {
3249            View child = focusTarget.itemView.findViewById(mState.mFocusedSubChildId);
3250            if (child != null && child.isFocusable()) {
3251                viewToFocus = child;
3252            }
3253        }
3254        viewToFocus.requestFocus();
3255    }
3256
3257    private int getDeepestFocusedViewWithId(View view) {
3258        int lastKnownId = view.getId();
3259        while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
3260            view = ((ViewGroup) view).getFocusedChild();
3261            final int id = view.getId();
3262            if (id != View.NO_ID) {
3263                lastKnownId = view.getId();
3264            }
3265        }
3266        return lastKnownId;
3267    }
3268
3269    /**
3270     * The first step of a layout where we;
3271     * - process adapter updates
3272     * - decide which animation should run
3273     * - save information about current views
3274     * - If necessary, run predictive layout and save its information
3275     */
3276    private void dispatchLayoutStep1() {
3277        mState.assertLayoutStep(State.STEP_START);
3278        mState.mIsMeasuring = false;
3279        eatRequestLayout();
3280        mViewInfoStore.clear();
3281        onEnterLayoutOrScroll();
3282        saveFocusInfo();
3283        processAdapterUpdatesAndSetAnimationFlags();
3284        mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
3285        mItemsAddedOrRemoved = mItemsChanged = false;
3286        mState.mInPreLayout = mState.mRunPredictiveAnimations;
3287        mState.mItemCount = mAdapter.getItemCount();
3288        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3289
3290        if (mState.mRunSimpleAnimations) {
3291            // Step 0: Find out where all non-removed items are, pre-layout
3292            int count = mChildHelper.getChildCount();
3293            for (int i = 0; i < count; ++i) {
3294                final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3295                if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
3296                    continue;
3297                }
3298                final ItemHolderInfo animationInfo = mItemAnimator
3299                        .recordPreLayoutInformation(mState, holder,
3300                                ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
3301                                holder.getUnmodifiedPayloads());
3302                mViewInfoStore.addToPreLayout(holder, animationInfo);
3303                if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
3304                        && !holder.shouldIgnore() && !holder.isInvalid()) {
3305                    long key = getChangedHolderKey(holder);
3306                    // This is NOT the only place where a ViewHolder is added to old change holders
3307                    // list. There is another case where:
3308                    //    * A VH is currently hidden but not deleted
3309                    //    * The hidden item is changed in the adapter
3310                    //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
3311                    // When this case is detected, RV will un-hide that view and add to the old
3312                    // change holders list.
3313                    mViewInfoStore.addToOldChangeHolders(key, holder);
3314                }
3315            }
3316        }
3317        if (mState.mRunPredictiveAnimations) {
3318            // Step 1: run prelayout: This will use the old positions of items. The layout manager
3319            // is expected to layout everything, even removed items (though not to add removed
3320            // items back to the container). This gives the pre-layout position of APPEARING views
3321            // which come into existence as part of the real layout.
3322
3323            // Save old positions so that LayoutManager can run its mapping logic.
3324            saveOldPositions();
3325            final boolean didStructureChange = mState.mStructureChanged;
3326            mState.mStructureChanged = false;
3327            // temporarily disable flag because we are asking for previous layout
3328            mLayout.onLayoutChildren(mRecycler, mState);
3329            mState.mStructureChanged = didStructureChange;
3330
3331            for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
3332                final View child = mChildHelper.getChildAt(i);
3333                final ViewHolder viewHolder = getChildViewHolderInt(child);
3334                if (viewHolder.shouldIgnore()) {
3335                    continue;
3336                }
3337                if (!mViewInfoStore.isInPreLayout(viewHolder)) {
3338                    int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
3339                    boolean wasHidden = viewHolder
3340                            .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3341                    if (!wasHidden) {
3342                        flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
3343                    }
3344                    final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
3345                            mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
3346                    if (wasHidden) {
3347                        recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
3348                    } else {
3349                        mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
3350                    }
3351                }
3352            }
3353            // we don't process disappearing list because they may re-appear in post layout pass.
3354            clearOldPositions();
3355        } else {
3356            clearOldPositions();
3357        }
3358        onExitLayoutOrScroll();
3359        resumeRequestLayout(false);
3360        mState.mLayoutStep = State.STEP_LAYOUT;
3361    }
3362
3363    /**
3364     * The second layout step where we do the actual layout of the views for the final state.
3365     * This step might be run multiple times if necessary (e.g. measure).
3366     */
3367    private void dispatchLayoutStep2() {
3368        eatRequestLayout();
3369        onEnterLayoutOrScroll();
3370        mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
3371        mAdapterHelper.consumeUpdatesInOnePass();
3372        mState.mItemCount = mAdapter.getItemCount();
3373        mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
3374
3375        // Step 2: Run layout
3376        mState.mInPreLayout = false;
3377        mLayout.onLayoutChildren(mRecycler, mState);
3378
3379        mState.mStructureChanged = false;
3380        mPendingSavedState = null;
3381
3382        // onLayoutChildren may have caused client code to disable item animations; re-check
3383        mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
3384        mState.mLayoutStep = State.STEP_ANIMATIONS;
3385        onExitLayoutOrScroll();
3386        resumeRequestLayout(false);
3387    }
3388
3389    /**
3390     * The final step of the layout where we save the information about views for animations,
3391     * trigger animations and do any necessary cleanup.
3392     */
3393    private void dispatchLayoutStep3() {
3394        mState.assertLayoutStep(State.STEP_ANIMATIONS);
3395        eatRequestLayout();
3396        onEnterLayoutOrScroll();
3397        mState.mLayoutStep = State.STEP_START;
3398        if (mState.mRunSimpleAnimations) {
3399            // Step 3: Find out where things are now, and process change animations.
3400            // traverse list in reverse because we may call animateChange in the loop which may
3401            // remove the target view holder.
3402            for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
3403                ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3404                if (holder.shouldIgnore()) {
3405                    continue;
3406                }
3407                long key = getChangedHolderKey(holder);
3408                final ItemHolderInfo animationInfo = mItemAnimator
3409                        .recordPostLayoutInformation(mState, holder);
3410                ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
3411                if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
3412                    // run a change animation
3413
3414                    // If an Item is CHANGED but the updated version is disappearing, it creates
3415                    // a conflicting case.
3416                    // Since a view that is marked as disappearing is likely to be going out of
3417                    // bounds, we run a change animation. Both views will be cleaned automatically
3418                    // once their animations finish.
3419                    // On the other hand, if it is the same view holder instance, we run a
3420                    // disappearing animation instead because we are not going to rebind the updated
3421                    // VH unless it is enforced by the layout manager.
3422                    final boolean oldDisappearing = mViewInfoStore.isDisappearing(
3423                            oldChangeViewHolder);
3424                    final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
3425                    if (oldDisappearing && oldChangeViewHolder == holder) {
3426                        // run disappear animation instead of change
3427                        mViewInfoStore.addToPostLayout(holder, animationInfo);
3428                    } else {
3429                        final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
3430                                oldChangeViewHolder);
3431                        // we add and remove so that any post info is merged.
3432                        mViewInfoStore.addToPostLayout(holder, animationInfo);
3433                        ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
3434                        if (preInfo == null) {
3435                            handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
3436                        } else {
3437                            animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
3438                                    oldDisappearing, newDisappearing);
3439                        }
3440                    }
3441                } else {
3442                    mViewInfoStore.addToPostLayout(holder, animationInfo);
3443                }
3444            }
3445
3446            // Step 4: Process view info lists and trigger animations
3447            mViewInfoStore.process(mViewInfoProcessCallback);
3448        }
3449
3450        mLayout.removeAndRecycleScrapInt(mRecycler);
3451        mState.mPreviousLayoutItemCount = mState.mItemCount;
3452        mDataSetHasChangedAfterLayout = false;
3453        mState.mRunSimpleAnimations = false;
3454
3455        mState.mRunPredictiveAnimations = false;
3456        mLayout.mRequestedSimpleAnimations = false;
3457        if (mRecycler.mChangedScrap != null) {
3458            mRecycler.mChangedScrap.clear();
3459        }
3460        mLayout.onLayoutCompleted(mState);
3461        onExitLayoutOrScroll();
3462        resumeRequestLayout(false);
3463        mViewInfoStore.clear();
3464        if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
3465            dispatchOnScrolled(0, 0);
3466        }
3467        recoverFocusFromState();
3468        resetFocusInfo();
3469    }
3470
3471    /**
3472     * This handles the case where there is an unexpected VH missing in the pre-layout map.
3473     * <p>
3474     * We might be able to detect the error in the application which will help the developer to
3475     * resolve the issue.
3476     * <p>
3477     * If it is not an expected error, we at least print an error to notify the developer and ignore
3478     * the animation.
3479     *
3480     * https://code.google.com/p/android/issues/detail?id=193958
3481     *
3482     * @param key The change key
3483     * @param holder Current ViewHolder
3484     * @param oldChangeViewHolder Changed ViewHolder
3485     */
3486    private void handleMissingPreInfoForChangeError(long key,
3487            ViewHolder holder, ViewHolder oldChangeViewHolder) {
3488        // check if two VH have the same key, if so, print that as an error
3489        final int childCount = mChildHelper.getChildCount();
3490        for (int i = 0; i < childCount; i++) {
3491            View view = mChildHelper.getChildAt(i);
3492            ViewHolder other = getChildViewHolderInt(view);
3493            if (other == holder) {
3494                continue;
3495            }
3496            final long otherKey = getChangedHolderKey(other);
3497            if (otherKey == key) {
3498                if (mAdapter != null && mAdapter.hasStableIds()) {
3499                    throw new IllegalStateException("Two different ViewHolders have the same stable"
3500                            + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
3501                            + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
3502                } else {
3503                    throw new IllegalStateException("Two different ViewHolders have the same change"
3504                            + " ID. This might happen due to inconsistent Adapter update events or"
3505                            + " if the LayoutManager lays out the same View multiple times."
3506                            + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
3507                }
3508            }
3509        }
3510        // Very unlikely to happen but if it does, notify the developer.
3511        Log.e(TAG, "Problem while matching changed view holders with the new"
3512                + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
3513                + " cannot be found but it is necessary for " + holder);
3514    }
3515
3516    /**
3517     * Records the animation information for a view holder that was bounced from hidden list. It
3518     * also clears the bounce back flag.
3519     */
3520    void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
3521            ItemHolderInfo animationInfo) {
3522        // looks like this view bounced back from hidden list!
3523        viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3524        if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
3525                && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
3526            long key = getChangedHolderKey(viewHolder);
3527            mViewInfoStore.addToOldChangeHolders(key, viewHolder);
3528        }
3529        mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
3530    }
3531
3532    private void findMinMaxChildLayoutPositions(int[] into) {
3533        final int count = mChildHelper.getChildCount();
3534        if (count == 0) {
3535            into[0] = NO_POSITION;
3536            into[1] = NO_POSITION;
3537            return;
3538        }
3539        int minPositionPreLayout = Integer.MAX_VALUE;
3540        int maxPositionPreLayout = Integer.MIN_VALUE;
3541        for (int i = 0; i < count; ++i) {
3542            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3543            if (holder.shouldIgnore()) {
3544                continue;
3545            }
3546            final int pos = holder.getLayoutPosition();
3547            if (pos < minPositionPreLayout) {
3548                minPositionPreLayout = pos;
3549            }
3550            if (pos > maxPositionPreLayout) {
3551                maxPositionPreLayout = pos;
3552            }
3553        }
3554        into[0] = minPositionPreLayout;
3555        into[1] = maxPositionPreLayout;
3556    }
3557
3558    private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
3559        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3560        return mMinMaxLayoutPositions[0] != minPositionPreLayout ||
3561                mMinMaxLayoutPositions[1] != maxPositionPreLayout;
3562    }
3563
3564    @Override
3565    protected void removeDetachedView(View child, boolean animate) {
3566        ViewHolder vh = getChildViewHolderInt(child);
3567        if (vh != null) {
3568            if (vh.isTmpDetached()) {
3569                vh.clearTmpDetachFlag();
3570            } else if (!vh.shouldIgnore()) {
3571                throw new IllegalArgumentException("Called removeDetachedView with a view which"
3572                        + " is not flagged as tmp detached." + vh);
3573            }
3574        }
3575        dispatchChildDetached(child);
3576        super.removeDetachedView(child, animate);
3577    }
3578
3579    /**
3580     * Returns a unique key to be used while handling change animations.
3581     * It might be child's position or stable id depending on the adapter type.
3582     */
3583    long getChangedHolderKey(ViewHolder holder) {
3584        return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
3585    }
3586
3587    void animateAppearance(@NonNull ViewHolder itemHolder,
3588            @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
3589        itemHolder.setIsRecyclable(false);
3590        if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
3591            postAnimationRunner();
3592        }
3593    }
3594
3595    void animateDisappearance(@NonNull ViewHolder holder,
3596            @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
3597        addAnimatingView(holder);
3598        holder.setIsRecyclable(false);
3599        if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
3600            postAnimationRunner();
3601        }
3602    }
3603
3604    private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
3605            @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
3606            boolean oldHolderDisappearing, boolean newHolderDisappearing) {
3607        oldHolder.setIsRecyclable(false);
3608        if (oldHolderDisappearing) {
3609            addAnimatingView(oldHolder);
3610        }
3611        if (oldHolder != newHolder) {
3612            if (newHolderDisappearing) {
3613                addAnimatingView(newHolder);
3614            }
3615            oldHolder.mShadowedHolder = newHolder;
3616            // old holder should disappear after animation ends
3617            addAnimatingView(oldHolder);
3618            mRecycler.unscrapView(oldHolder);
3619            newHolder.setIsRecyclable(false);
3620            newHolder.mShadowingHolder = oldHolder;
3621        }
3622        if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
3623            postAnimationRunner();
3624        }
3625    }
3626
3627    @Override
3628    protected void onLayout(boolean changed, int l, int t, int r, int b) {
3629        TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
3630        dispatchLayout();
3631        TraceCompat.endSection();
3632        mFirstLayoutComplete = true;
3633    }
3634
3635    @Override
3636    public void requestLayout() {
3637        if (mEatRequestLayout == 0 && !mLayoutFrozen) {
3638            super.requestLayout();
3639        } else {
3640            mLayoutRequestEaten = true;
3641        }
3642    }
3643
3644    void markItemDecorInsetsDirty() {
3645        final int childCount = mChildHelper.getUnfilteredChildCount();
3646        for (int i = 0; i < childCount; i++) {
3647            final View child = mChildHelper.getUnfilteredChildAt(i);
3648            ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
3649        }
3650        mRecycler.markItemDecorInsetsDirty();
3651    }
3652
3653    @Override
3654    public void draw(Canvas c) {
3655        super.draw(c);
3656
3657        final int count = mItemDecorations.size();
3658        for (int i = 0; i < count; i++) {
3659            mItemDecorations.get(i).onDrawOver(c, this, mState);
3660        }
3661        // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
3662        // need find children closest to edges. Not sure if it is worth the effort.
3663        boolean needsInvalidate = false;
3664        if (mLeftGlow != null && !mLeftGlow.isFinished()) {
3665            final int restore = c.save();
3666            final int padding = mClipToPadding ? getPaddingBottom() : 0;
3667            c.rotate(270);
3668            c.translate(-getHeight() + padding, 0);
3669            needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
3670            c.restoreToCount(restore);
3671        }
3672        if (mTopGlow != null && !mTopGlow.isFinished()) {
3673            final int restore = c.save();
3674            if (mClipToPadding) {
3675                c.translate(getPaddingLeft(), getPaddingTop());
3676            }
3677            needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
3678            c.restoreToCount(restore);
3679        }
3680        if (mRightGlow != null && !mRightGlow.isFinished()) {
3681            final int restore = c.save();
3682            final int width = getWidth();
3683            final int padding = mClipToPadding ? getPaddingTop() : 0;
3684            c.rotate(90);
3685            c.translate(-padding, -width);
3686            needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
3687            c.restoreToCount(restore);
3688        }
3689        if (mBottomGlow != null && !mBottomGlow.isFinished()) {
3690            final int restore = c.save();
3691            c.rotate(180);
3692            if (mClipToPadding) {
3693                c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
3694            } else {
3695                c.translate(-getWidth(), -getHeight());
3696            }
3697            needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
3698            c.restoreToCount(restore);
3699        }
3700
3701        // If some views are animating, ItemDecorators are likely to move/change with them.
3702        // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
3703        // display lists are not invalidated.
3704        if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0 &&
3705                mItemAnimator.isRunning()) {
3706            needsInvalidate = true;
3707        }
3708
3709        if (needsInvalidate) {
3710            ViewCompat.postInvalidateOnAnimation(this);
3711        }
3712    }
3713
3714    @Override
3715    public void onDraw(Canvas c) {
3716        super.onDraw(c);
3717
3718        final int count = mItemDecorations.size();
3719        for (int i = 0; i < count; i++) {
3720            mItemDecorations.get(i).onDraw(c, this, mState);
3721        }
3722    }
3723
3724    @Override
3725    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
3726        return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
3727    }
3728
3729    @Override
3730    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
3731        if (mLayout == null) {
3732            throw new IllegalStateException("RecyclerView has no LayoutManager");
3733        }
3734        return mLayout.generateDefaultLayoutParams();
3735    }
3736
3737    @Override
3738    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
3739        if (mLayout == null) {
3740            throw new IllegalStateException("RecyclerView has no LayoutManager");
3741        }
3742        return mLayout.generateLayoutParams(getContext(), attrs);
3743    }
3744
3745    @Override
3746    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
3747        if (mLayout == null) {
3748            throw new IllegalStateException("RecyclerView has no LayoutManager");
3749        }
3750        return mLayout.generateLayoutParams(p);
3751    }
3752
3753    /**
3754     * Returns true if RecyclerView is currently running some animations.
3755     * <p>
3756     * If you want to be notified when animations are finished, use
3757     * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
3758     *
3759     * @return True if there are some item animations currently running or waiting to be started.
3760     */
3761    public boolean isAnimating() {
3762        return mItemAnimator != null && mItemAnimator.isRunning();
3763    }
3764
3765    void saveOldPositions() {
3766        final int childCount = mChildHelper.getUnfilteredChildCount();
3767        for (int i = 0; i < childCount; i++) {
3768            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3769            if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
3770                throw new IllegalStateException("view holder cannot have position -1 unless it"
3771                        + " is removed");
3772            }
3773            if (!holder.shouldIgnore()) {
3774                holder.saveOldPosition();
3775            }
3776        }
3777    }
3778
3779    void clearOldPositions() {
3780        final int childCount = mChildHelper.getUnfilteredChildCount();
3781        for (int i = 0; i < childCount; i++) {
3782            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3783            if (!holder.shouldIgnore()) {
3784                holder.clearOldPosition();
3785            }
3786        }
3787        mRecycler.clearOldPositions();
3788    }
3789
3790    void offsetPositionRecordsForMove(int from, int to) {
3791        final int childCount = mChildHelper.getUnfilteredChildCount();
3792        final int start, end, inBetweenOffset;
3793        if (from < to) {
3794            start = from;
3795            end = to;
3796            inBetweenOffset = -1;
3797        } else {
3798            start = to;
3799            end = from;
3800            inBetweenOffset = 1;
3801        }
3802
3803        for (int i = 0; i < childCount; i++) {
3804            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3805            if (holder == null || holder.mPosition < start || holder.mPosition > end) {
3806                continue;
3807            }
3808            if (DEBUG) {
3809                Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder " +
3810                        holder);
3811            }
3812            if (holder.mPosition == from) {
3813                holder.offsetPosition(to - from, false);
3814            } else {
3815                holder.offsetPosition(inBetweenOffset, false);
3816            }
3817
3818            mState.mStructureChanged = true;
3819        }
3820        mRecycler.offsetPositionRecordsForMove(from, to);
3821        requestLayout();
3822    }
3823
3824    void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
3825        final int childCount = mChildHelper.getUnfilteredChildCount();
3826        for (int i = 0; i < childCount; i++) {
3827            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3828            if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
3829                if (DEBUG) {
3830                    Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder " +
3831                            holder + " now at position " + (holder.mPosition + itemCount));
3832                }
3833                holder.offsetPosition(itemCount, false);
3834                mState.mStructureChanged = true;
3835            }
3836        }
3837        mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
3838        requestLayout();
3839    }
3840
3841    void offsetPositionRecordsForRemove(int positionStart, int itemCount,
3842            boolean applyToPreLayout) {
3843        final int positionEnd = positionStart + itemCount;
3844        final int childCount = mChildHelper.getUnfilteredChildCount();
3845        for (int i = 0; i < childCount; i++) {
3846            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3847            if (holder != null && !holder.shouldIgnore()) {
3848                if (holder.mPosition >= positionEnd) {
3849                    if (DEBUG) {
3850                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +
3851                                " holder " + holder + " now at position " +
3852                                (holder.mPosition - itemCount));
3853                    }
3854                    holder.offsetPosition(-itemCount, applyToPreLayout);
3855                    mState.mStructureChanged = true;
3856                } else if (holder.mPosition >= positionStart) {
3857                    if (DEBUG) {
3858                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +
3859                                " holder " + holder + " now REMOVED");
3860                    }
3861                    holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
3862                            applyToPreLayout);
3863                    mState.mStructureChanged = true;
3864                }
3865            }
3866        }
3867        mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
3868        requestLayout();
3869    }
3870
3871    /**
3872     * Rebind existing views for the given range, or create as needed.
3873     *
3874     * @param positionStart Adapter position to start at
3875     * @param itemCount Number of views that must explicitly be rebound
3876     */
3877    void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
3878        final int childCount = mChildHelper.getUnfilteredChildCount();
3879        final int positionEnd = positionStart + itemCount;
3880
3881        for (int i = 0; i < childCount; i++) {
3882            final View child = mChildHelper.getUnfilteredChildAt(i);
3883            final ViewHolder holder = getChildViewHolderInt(child);
3884            if (holder == null || holder.shouldIgnore()) {
3885                continue;
3886            }
3887            if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
3888                // We re-bind these view holders after pre-processing is complete so that
3889                // ViewHolders have their final positions assigned.
3890                holder.addFlags(ViewHolder.FLAG_UPDATE);
3891                holder.addChangePayload(payload);
3892                // lp cannot be null since we get ViewHolder from it.
3893                ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
3894            }
3895        }
3896        mRecycler.viewRangeUpdate(positionStart, itemCount);
3897    }
3898
3899    boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
3900        return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
3901                viewHolder.getUnmodifiedPayloads());
3902    }
3903
3904    void setDataSetChangedAfterLayout() {
3905        if (mDataSetHasChangedAfterLayout) {
3906            return;
3907        }
3908        mDataSetHasChangedAfterLayout = true;
3909        final int childCount = mChildHelper.getUnfilteredChildCount();
3910        for (int i = 0; i < childCount; i++) {
3911            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3912            if (holder != null && !holder.shouldIgnore()) {
3913                holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
3914            }
3915        }
3916        mRecycler.setAdapterPositionsAsUnknown();
3917    }
3918
3919    /**
3920     * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
3921     * data change event.
3922     */
3923    void markKnownViewsInvalid() {
3924        final int childCount = mChildHelper.getUnfilteredChildCount();
3925        for (int i = 0; i < childCount; i++) {
3926            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3927            if (holder != null && !holder.shouldIgnore()) {
3928                holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
3929            }
3930        }
3931        markItemDecorInsetsDirty();
3932        mRecycler.markKnownViewsInvalid();
3933    }
3934
3935    /**
3936     * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
3937     * will trigger a {@link #requestLayout()} call.
3938     */
3939    public void invalidateItemDecorations() {
3940        if (mItemDecorations.size() == 0) {
3941            return;
3942        }
3943        if (mLayout != null) {
3944            mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
3945                    + " or layout");
3946        }
3947        markItemDecorInsetsDirty();
3948        requestLayout();
3949    }
3950
3951    /**
3952     * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
3953     * focus even if the View representing the Item is replaced during a layout calculation.
3954     * <p>
3955     * By default, this value is {@code true}.
3956     *
3957     * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
3958     * focus.
3959     *
3960     * @see #setPreserveFocusAfterLayout(boolean)
3961     */
3962    public boolean getPreserveFocusAfterLayout() {
3963        return mPreserveFocusAfterLayout;
3964    }
3965
3966    /**
3967     * Set whether the RecyclerView should try to keep the same Item focused after a layout
3968     * calculation or not.
3969     * <p>
3970     * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
3971     * views may lose focus during a layout calculation as their state changes or they are replaced
3972     * with another view due to type change or animation. In these cases, RecyclerView can request
3973     * focus on the new view automatically.
3974     *
3975     * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
3976     *                                 layout calculations. Defaults to true.
3977     *
3978     * @see #getPreserveFocusAfterLayout()
3979     */
3980    public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
3981        mPreserveFocusAfterLayout = preserveFocusAfterLayout;
3982    }
3983
3984    /**
3985     * Retrieve the {@link ViewHolder} for the given child view.
3986     *
3987     * @param child Child of this RecyclerView to query for its ViewHolder
3988     * @return The child view's ViewHolder
3989     */
3990    public ViewHolder getChildViewHolder(View child) {
3991        final ViewParent parent = child.getParent();
3992        if (parent != null && parent != this) {
3993            throw new IllegalArgumentException("View " + child + " is not a direct child of " +
3994                    this);
3995        }
3996        return getChildViewHolderInt(child);
3997    }
3998
3999    /**
4000     * Traverses the ancestors of the given view and returns the item view that contains it and
4001     * also a direct child of the RecyclerView. This returned view can be used to get the
4002     * ViewHolder by calling {@link #getChildViewHolder(View)}.
4003     *
4004     * @param view The view that is a descendant of the RecyclerView.
4005     *
4006     * @return The direct child of the RecyclerView which contains the given view or null if the
4007     * provided view is not a descendant of this RecyclerView.
4008     *
4009     * @see #getChildViewHolder(View)
4010     * @see #findContainingViewHolder(View)
4011     */
4012    @Nullable
4013    public View findContainingItemView(View view) {
4014        ViewParent parent = view.getParent();
4015        while (parent != null && parent != this && parent instanceof View) {
4016            view = (View) parent;
4017            parent = view.getParent();
4018        }
4019        return parent == this ? view : null;
4020    }
4021
4022    /**
4023     * Returns the ViewHolder that contains the given view.
4024     *
4025     * @param view The view that is a descendant of the RecyclerView.
4026     *
4027     * @return The ViewHolder that contains the given view or null if the provided view is not a
4028     * descendant of this RecyclerView.
4029     */
4030    @Nullable
4031    public ViewHolder findContainingViewHolder(View view) {
4032        View itemView = findContainingItemView(view);
4033        return itemView == null ? null : getChildViewHolder(itemView);
4034    }
4035
4036
4037    static ViewHolder getChildViewHolderInt(View child) {
4038        if (child == null) {
4039            return null;
4040        }
4041        return ((LayoutParams) child.getLayoutParams()).mViewHolder;
4042    }
4043
4044    /**
4045     * @deprecated use {@link #getChildAdapterPosition(View)} or
4046     * {@link #getChildLayoutPosition(View)}.
4047     */
4048    @Deprecated
4049    public int getChildPosition(View child) {
4050        return getChildAdapterPosition(child);
4051    }
4052
4053    /**
4054     * Return the adapter position that the given child view corresponds to.
4055     *
4056     * @param child Child View to query
4057     * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
4058     */
4059    public int getChildAdapterPosition(View child) {
4060        final ViewHolder holder = getChildViewHolderInt(child);
4061        return holder != null ? holder.getAdapterPosition() : NO_POSITION;
4062    }
4063
4064    /**
4065     * Return the adapter position of the given child view as of the latest completed layout pass.
4066     * <p>
4067     * This position may not be equal to Item's adapter position if there are pending changes
4068     * in the adapter which have not been reflected to the layout yet.
4069     *
4070     * @param child Child View to query
4071     * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
4072     * the View is representing a removed item.
4073     */
4074    public int getChildLayoutPosition(View child) {
4075        final ViewHolder holder = getChildViewHolderInt(child);
4076        return holder != null ? holder.getLayoutPosition() : NO_POSITION;
4077    }
4078
4079    /**
4080     * Return the stable item id that the given child view corresponds to.
4081     *
4082     * @param child Child View to query
4083     * @return Item id corresponding to the given view or {@link #NO_ID}
4084     */
4085    public long getChildItemId(View child) {
4086        if (mAdapter == null || !mAdapter.hasStableIds()) {
4087            return NO_ID;
4088        }
4089        final ViewHolder holder = getChildViewHolderInt(child);
4090        return holder != null ? holder.getItemId() : NO_ID;
4091    }
4092
4093    /**
4094     * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
4095     * {@link #findViewHolderForAdapterPosition(int)}
4096     */
4097    @Deprecated
4098    public ViewHolder findViewHolderForPosition(int position) {
4099        return findViewHolderForPosition(position, false);
4100    }
4101
4102    /**
4103     * Return the ViewHolder for the item in the given position of the data set as of the latest
4104     * layout pass.
4105     * <p>
4106     * This method checks only the children of RecyclerView. If the item at the given
4107     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4108     * <p>
4109     * Note that when Adapter contents change, ViewHolder positions are not updated until the
4110     * next layout calculation. If there are pending adapter updates, the return value of this
4111     * method may not match your adapter contents. You can use
4112     * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
4113     * <p>
4114     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4115     * with the same layout position representing the same Item. In this case, the updated
4116     * ViewHolder will be returned.
4117     *
4118     * @param position The position of the item in the data set of the adapter
4119     * @return The ViewHolder at <code>position</code> or null if there is no such item
4120     */
4121    public ViewHolder findViewHolderForLayoutPosition(int position) {
4122        return findViewHolderForPosition(position, false);
4123    }
4124
4125    /**
4126     * Return the ViewHolder for the item in the given position of the data set. Unlike
4127     * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
4128     * adapter changes that may not be reflected to the layout yet. On the other hand, if
4129     * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
4130     * calculated yet, this method will return <code>null</code> since the new positions of views
4131     * are unknown until the layout is calculated.
4132     * <p>
4133     * This method checks only the children of RecyclerView. If the item at the given
4134     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4135     * <p>
4136     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4137     * representing the same Item. In this case, the updated ViewHolder will be returned.
4138     *
4139     * @param position The position of the item in the data set of the adapter
4140     * @return The ViewHolder at <code>position</code> or null if there is no such item
4141     */
4142    public ViewHolder findViewHolderForAdapterPosition(int position) {
4143        if (mDataSetHasChangedAfterLayout) {
4144            return null;
4145        }
4146        final int childCount = mChildHelper.getUnfilteredChildCount();
4147        // hidden VHs are not preferred but if that is the only one we find, we rather return it
4148        ViewHolder hidden = null;
4149        for (int i = 0; i < childCount; i++) {
4150            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4151            if (holder != null && !holder.isRemoved() && getAdapterPositionFor(holder) == position) {
4152                if (mChildHelper.isHidden(holder.itemView)) {
4153                    hidden = holder;
4154                } else {
4155                    return holder;
4156                }
4157            }
4158        }
4159        return hidden;
4160    }
4161
4162    ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
4163        final int childCount = mChildHelper.getUnfilteredChildCount();
4164        ViewHolder hidden = null;
4165        for (int i = 0; i < childCount; i++) {
4166            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4167            if (holder != null && !holder.isRemoved()) {
4168                if (checkNewPosition) {
4169                    if (holder.mPosition != position) {
4170                        continue;
4171                    }
4172                } else if (holder.getLayoutPosition() != position) {
4173                    continue;
4174                }
4175                if (mChildHelper.isHidden(holder.itemView)) {
4176                    hidden = holder;
4177                } else {
4178                    return holder;
4179                }
4180            }
4181        }
4182        // This method should not query cached views. It creates a problem during adapter updates
4183        // when we are dealing with already laid out views. Also, for the public method, it is more
4184        // reasonable to return null if position is not laid out.
4185        return hidden;
4186    }
4187
4188    /**
4189     * Return the ViewHolder for the item with the given id. The RecyclerView must
4190     * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
4191     * return a non-null value.
4192     * <p>
4193     * This method checks only the children of RecyclerView. If the item with the given
4194     * <code>id</code> is not laid out, it <em>will not</em> create a new one.
4195     *
4196     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
4197     * same id. In this case, the updated ViewHolder will be returned.
4198     *
4199     * @param id The id for the requested item
4200     * @return The ViewHolder with the given <code>id</code> or null if there is no such item
4201     */
4202    public ViewHolder findViewHolderForItemId(long id) {
4203        if (mAdapter == null || !mAdapter.hasStableIds()) {
4204            return null;
4205        }
4206        final int childCount = mChildHelper.getUnfilteredChildCount();
4207        ViewHolder hidden = null;
4208        for (int i = 0; i < childCount; i++) {
4209            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4210            if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
4211                if (mChildHelper.isHidden(holder.itemView)) {
4212                    hidden = holder;
4213                } else {
4214                    return holder;
4215                }
4216            }
4217        }
4218        return hidden;
4219    }
4220
4221    /**
4222     * Find the topmost view under the given point.
4223     *
4224     * @param x Horizontal position in pixels to search
4225     * @param y Vertical position in pixels to search
4226     * @return The child view under (x, y) or null if no matching child is found
4227     */
4228    public View findChildViewUnder(float x, float y) {
4229        final int count = mChildHelper.getChildCount();
4230        for (int i = count - 1; i >= 0; i--) {
4231            final View child = mChildHelper.getChildAt(i);
4232            final float translationX = ViewCompat.getTranslationX(child);
4233            final float translationY = ViewCompat.getTranslationY(child);
4234            if (x >= child.getLeft() + translationX &&
4235                    x <= child.getRight() + translationX &&
4236                    y >= child.getTop() + translationY &&
4237                    y <= child.getBottom() + translationY) {
4238                return child;
4239            }
4240        }
4241        return null;
4242    }
4243
4244    @Override
4245    public boolean drawChild(Canvas canvas, View child, long drawingTime) {
4246        return super.drawChild(canvas, child, drawingTime);
4247    }
4248
4249    /**
4250     * Offset the bounds of all child views by <code>dy</code> pixels.
4251     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4252     *
4253     * @param dy Vertical pixel offset to apply to the bounds of all child views
4254     */
4255    public void offsetChildrenVertical(int dy) {
4256        final int childCount = mChildHelper.getChildCount();
4257        for (int i = 0; i < childCount; i++) {
4258            mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
4259        }
4260    }
4261
4262    /**
4263     * Called when an item view is attached to this RecyclerView.
4264     *
4265     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4266     * of child views as they become attached. This will be called before a
4267     * {@link LayoutManager} measures or lays out the view and is a good time to perform these
4268     * changes.</p>
4269     *
4270     * @param child Child view that is now attached to this RecyclerView and its associated window
4271     */
4272    public void onChildAttachedToWindow(View child) {
4273    }
4274
4275    /**
4276     * Called when an item view is detached from this RecyclerView.
4277     *
4278     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4279     * of child views as they become detached. This will be called as a
4280     * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
4281     *
4282     * @param child Child view that is now detached from this RecyclerView and its associated window
4283     */
4284    public void onChildDetachedFromWindow(View child) {
4285    }
4286
4287    /**
4288     * Offset the bounds of all child views by <code>dx</code> pixels.
4289     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4290     *
4291     * @param dx Horizontal pixel offset to apply to the bounds of all child views
4292     */
4293    public void offsetChildrenHorizontal(int dx) {
4294        final int childCount = mChildHelper.getChildCount();
4295        for (int i = 0; i < childCount; i++) {
4296            mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
4297        }
4298    }
4299
4300    /**
4301     * Returns the bounds of the view including its decoration and margins.
4302     *
4303     * @param view The view element to check
4304     * @param outBounds A rect that will receive the bounds of the element including its
4305     *                  decoration and margins.
4306     */
4307    public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
4308        getDecoratedBoundsWithMarginsInt(view, outBounds);
4309    }
4310
4311    static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
4312        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
4313        final Rect insets = lp.mDecorInsets;
4314        outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
4315                view.getTop() - insets.top - lp.topMargin,
4316                view.getRight() + insets.right + lp.rightMargin,
4317                view.getBottom() + insets.bottom + lp.bottomMargin);
4318    }
4319
4320    Rect getItemDecorInsetsForChild(View child) {
4321        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
4322        if (!lp.mInsetsDirty) {
4323            return lp.mDecorInsets;
4324        }
4325
4326        if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
4327            // changed/invalid items should not be updated until they are rebound.
4328            return lp.mDecorInsets;
4329        }
4330        final Rect insets = lp.mDecorInsets;
4331        insets.set(0, 0, 0, 0);
4332        final int decorCount = mItemDecorations.size();
4333        for (int i = 0; i < decorCount; i++) {
4334            mTempRect.set(0, 0, 0, 0);
4335            mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
4336            insets.left += mTempRect.left;
4337            insets.top += mTempRect.top;
4338            insets.right += mTempRect.right;
4339            insets.bottom += mTempRect.bottom;
4340        }
4341        lp.mInsetsDirty = false;
4342        return insets;
4343    }
4344
4345    /**
4346     * Called when the scroll position of this RecyclerView changes. Subclasses should use
4347     * this method to respond to scrolling within the adapter's data set instead of an explicit
4348     * listener.
4349     *
4350     * <p>This method will always be invoked before listeners. If a subclass needs to perform
4351     * any additional upkeep or bookkeeping after scrolling but before listeners run,
4352     * this is a good place to do so.</p>
4353     *
4354     * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
4355     * the distance scrolled in either direction within the adapter's data set instead of absolute
4356     * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
4357     * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
4358     * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
4359     * do not correspond to the data set scroll position. However, some subclasses may choose
4360     * to use these fields as special offsets.</p>
4361     *
4362     * @param dx horizontal distance scrolled in pixels
4363     * @param dy vertical distance scrolled in pixels
4364     */
4365    public void onScrolled(int dx, int dy) {
4366        // Do nothing
4367    }
4368
4369    void dispatchOnScrolled(int hresult, int vresult) {
4370        mDispatchScrollCounter ++;
4371        // Pass the current scrollX/scrollY values; no actual change in these properties occurred
4372        // but some general-purpose code may choose to respond to changes this way.
4373        final int scrollX = getScrollX();
4374        final int scrollY = getScrollY();
4375        onScrollChanged(scrollX, scrollY, scrollX, scrollY);
4376
4377        // Pass the real deltas to onScrolled, the RecyclerView-specific method.
4378        onScrolled(hresult, vresult);
4379
4380        // Invoke listeners last. Subclassed view methods always handle the event first.
4381        // All internal state is consistent by the time listeners are invoked.
4382        if (mScrollListener != null) {
4383            mScrollListener.onScrolled(this, hresult, vresult);
4384        }
4385        if (mScrollListeners != null) {
4386            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4387                mScrollListeners.get(i).onScrolled(this, hresult, vresult);
4388            }
4389        }
4390        mDispatchScrollCounter --;
4391    }
4392
4393    /**
4394     * Called when the scroll state of this RecyclerView changes. Subclasses should use this
4395     * method to respond to state changes instead of an explicit listener.
4396     *
4397     * <p>This method will always be invoked before listeners, but after the LayoutManager
4398     * responds to the scroll state change.</p>
4399     *
4400     * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
4401     *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
4402     */
4403    public void onScrollStateChanged(int state) {
4404        // Do nothing
4405    }
4406
4407    void dispatchOnScrollStateChanged(int state) {
4408        // Let the LayoutManager go first; this allows it to bring any properties into
4409        // a consistent state before the RecyclerView subclass responds.
4410        if (mLayout != null) {
4411            mLayout.onScrollStateChanged(state);
4412        }
4413
4414        // Let the RecyclerView subclass handle this event next; any LayoutManager property
4415        // changes will be reflected by this time.
4416        onScrollStateChanged(state);
4417
4418        // Listeners go last. All other internal state is consistent by this point.
4419        if (mScrollListener != null) {
4420            mScrollListener.onScrollStateChanged(this, state);
4421        }
4422        if (mScrollListeners != null) {
4423            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4424                mScrollListeners.get(i).onScrollStateChanged(this, state);
4425            }
4426        }
4427    }
4428
4429    /**
4430     * Returns whether there are pending adapter updates which are not yet applied to the layout.
4431     * <p>
4432     * If this method returns <code>true</code>, it means that what user is currently seeing may not
4433     * reflect them adapter contents (depending on what has changed).
4434     * You may use this information to defer or cancel some operations.
4435     * <p>
4436     * This method returns true if RecyclerView has not yet calculated the first layout after it is
4437     * attached to the Window or the Adapter has been replaced.
4438     *
4439     * @return True if there are some adapter updates which are not yet reflected to layout or false
4440     * if layout is up to date.
4441     */
4442    public boolean hasPendingAdapterUpdates() {
4443        return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
4444                || mAdapterHelper.hasPendingUpdates();
4445    }
4446
4447    /**
4448     * Runs prefetch work immediately after a traversal, in the downtime while the UI thread is
4449     * waiting for VSYNC.
4450     */
4451    class ViewPrefetcher implements Runnable {
4452
4453        long mPostTimeNanos;
4454        private int mDx;
4455        private int mDy;
4456
4457        int[] mItemPrefetchArray;
4458
4459        /**
4460         * Schedule a prefetch immediately after the current traversal.
4461         */
4462        public void postFromTraversal(int dx, int dy) {
4463            if (ALLOW_PREFETCHING
4464                    && mAdapter != null
4465                    && mLayout != null
4466                    && mLayout.getItemPrefetchCount() > 0) {
4467                mDx = dx;
4468                mDy = dy;
4469                mPostTimeNanos = System.nanoTime();
4470                RecyclerView.this.post(this);
4471            }
4472        }
4473
4474        public boolean lastPrefetchIncludedPosition(int position) {
4475            if (mItemPrefetchArray != null) {
4476                for (int i = 0; i < mItemPrefetchArray.length; i++) {
4477                    if (mItemPrefetchArray[i] == position) return true;
4478                }
4479            }
4480            return false;
4481        }
4482
4483        /**
4484         * Called when prefetch indices are no longer valid for cache prioritization.
4485         */
4486        public void clearPrefetchPositions() {
4487            if (mItemPrefetchArray != null) {
4488                Arrays.fill(mItemPrefetchArray, -1);
4489            }
4490        }
4491
4492        @Override
4493        public void run() {
4494            try {
4495                TraceCompat.beginSection(TRACE_PREFETCH_TAG);
4496                final int prefetchCount = mLayout.getItemPrefetchCount();
4497                if (mAdapter == null
4498                        || mLayout == null
4499                        || !mLayout.isItemPrefetchEnabled()
4500                        || prefetchCount < 1
4501                        || hasPendingAdapterUpdates()) {
4502                    // abort - no work
4503                    return;
4504                }
4505
4506                // Query last vsync so we can predict next one. Note that drawing time not yet
4507                // valid in animation/input callbacks, so query it here to be safe.
4508                long lastFrameVsyncNanos = TimeUnit.MILLISECONDS.toNanos(getDrawingTime());
4509                if (lastFrameVsyncNanos == 0 || sFrameIntervalNanos == 0) {
4510                    // abort - couldn't get info for estimating next vsync
4511                    return;
4512                }
4513
4514                long nowNanos = System.nanoTime();
4515                long nextFrameNanos = lastFrameVsyncNanos + sFrameIntervalNanos;
4516                if (nowNanos - mPostTimeNanos > sFrameIntervalNanos
4517                        || nextFrameNanos - nowNanos < MIN_PREFETCH_TIME_NANOS) {
4518                    // abort - Executing either too far after post, or too near next scheduled vsync
4519                    return;
4520                }
4521
4522                if (mItemPrefetchArray == null || mItemPrefetchArray.length < prefetchCount) {
4523                    mItemPrefetchArray = new int[prefetchCount];
4524                }
4525                Arrays.fill(mItemPrefetchArray, -1);
4526                int viewCount = mLayout.gatherPrefetchIndices(mDx, mDy, mState, mItemPrefetchArray);
4527                mRecycler.prefetch(mItemPrefetchArray, viewCount);
4528            } finally {
4529                TraceCompat.endSection();
4530            }
4531        }
4532    }
4533
4534    private class ViewFlinger implements Runnable {
4535        private int mLastFlingX;
4536        private int mLastFlingY;
4537        private ScrollerCompat mScroller;
4538        private Interpolator mInterpolator = sQuinticInterpolator;
4539
4540
4541        // When set to true, postOnAnimation callbacks are delayed until the run method completes
4542        private boolean mEatRunOnAnimationRequest = false;
4543
4544        // Tracks if postAnimationCallback should be re-attached when it is done
4545        private boolean mReSchedulePostAnimationCallback = false;
4546
4547        public ViewFlinger() {
4548            mScroller = ScrollerCompat.create(getContext(), sQuinticInterpolator);
4549        }
4550
4551        @Override
4552        public void run() {
4553            if (mLayout == null) {
4554                stop();
4555                return; // no layout, cannot scroll.
4556            }
4557            disableRunOnAnimationRequests();
4558            consumePendingUpdateOperations();
4559            // keep a local reference so that if it is changed during onAnimation method, it won't
4560            // cause unexpected behaviors
4561            final ScrollerCompat scroller = mScroller;
4562            final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
4563            if (scroller.computeScrollOffset()) {
4564                final int x = scroller.getCurrX();
4565                final int y = scroller.getCurrY();
4566                final int dx = x - mLastFlingX;
4567                final int dy = y - mLastFlingY;
4568                int hresult = 0;
4569                int vresult = 0;
4570                mLastFlingX = x;
4571                mLastFlingY = y;
4572                int overscrollX = 0, overscrollY = 0;
4573                if (mAdapter != null) {
4574                    eatRequestLayout();
4575                    onEnterLayoutOrScroll();
4576                    TraceCompat.beginSection(TRACE_SCROLL_TAG);
4577                    if (dx != 0) {
4578                        hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
4579                        overscrollX = dx - hresult;
4580                    }
4581                    if (dy != 0) {
4582                        vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
4583                        overscrollY = dy - vresult;
4584                    }
4585                    TraceCompat.endSection();
4586                    repositionShadowingViews();
4587
4588                    onExitLayoutOrScroll();
4589                    resumeRequestLayout(false);
4590
4591                    if (smoothScroller != null && !smoothScroller.isPendingInitialRun() &&
4592                            smoothScroller.isRunning()) {
4593                        final int adapterSize = mState.getItemCount();
4594                        if (adapterSize == 0) {
4595                            smoothScroller.stop();
4596                        } else if (smoothScroller.getTargetPosition() >= adapterSize) {
4597                            smoothScroller.setTargetPosition(adapterSize - 1);
4598                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4599                        } else {
4600                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4601                        }
4602                    }
4603                }
4604                if (!mItemDecorations.isEmpty()) {
4605                    invalidate();
4606                }
4607                if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
4608                    considerReleasingGlowsOnScroll(dx, dy);
4609                }
4610                if (overscrollX != 0 || overscrollY != 0) {
4611                    final int vel = (int) scroller.getCurrVelocity();
4612
4613                    int velX = 0;
4614                    if (overscrollX != x) {
4615                        velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
4616                    }
4617
4618                    int velY = 0;
4619                    if (overscrollY != y) {
4620                        velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
4621                    }
4622
4623                    if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
4624                        absorbGlows(velX, velY);
4625                    }
4626                    if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0) &&
4627                            (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
4628                        scroller.abortAnimation();
4629                    }
4630                }
4631                if (hresult != 0 || vresult != 0) {
4632                    dispatchOnScrolled(hresult, vresult);
4633                }
4634
4635                if (!awakenScrollBars()) {
4636                    invalidate();
4637                }
4638
4639                final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
4640                        && vresult == dy;
4641                final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
4642                        && hresult == dx;
4643                final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
4644                        || fullyConsumedVertical;
4645
4646                if (scroller.isFinished() || !fullyConsumedAny) {
4647                    setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this.
4648                    if (ALLOW_PREFETCHING) {
4649                        mViewPrefetcher.clearPrefetchPositions();
4650                    }
4651                } else {
4652                    postOnAnimation();
4653                    if (ALLOW_PREFETCHING) {
4654                        mViewPrefetcher.postFromTraversal(dx, dy);
4655                    }
4656                }
4657            }
4658            // call this after the onAnimation is complete not to have inconsistent callbacks etc.
4659            if (smoothScroller != null) {
4660                if (smoothScroller.isPendingInitialRun()) {
4661                    smoothScroller.onAnimation(0, 0);
4662                }
4663                if (!mReSchedulePostAnimationCallback) {
4664                    smoothScroller.stop(); //stop if it does not trigger any scroll
4665                }
4666            }
4667            enableRunOnAnimationRequests();
4668        }
4669
4670        private void disableRunOnAnimationRequests() {
4671            mReSchedulePostAnimationCallback = false;
4672            mEatRunOnAnimationRequest = true;
4673        }
4674
4675        private void enableRunOnAnimationRequests() {
4676            mEatRunOnAnimationRequest = false;
4677            if (mReSchedulePostAnimationCallback) {
4678                postOnAnimation();
4679            }
4680        }
4681
4682        void postOnAnimation() {
4683            if (mEatRunOnAnimationRequest) {
4684                mReSchedulePostAnimationCallback = true;
4685            } else {
4686                removeCallbacks(this);
4687                ViewCompat.postOnAnimation(RecyclerView.this, this);
4688            }
4689        }
4690
4691        public void fling(int velocityX, int velocityY) {
4692            setScrollState(SCROLL_STATE_SETTLING);
4693            mLastFlingX = mLastFlingY = 0;
4694            mScroller.fling(0, 0, velocityX, velocityY,
4695                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
4696            postOnAnimation();
4697        }
4698
4699        public void smoothScrollBy(int dx, int dy) {
4700            smoothScrollBy(dx, dy, 0, 0);
4701        }
4702
4703        public void smoothScrollBy(int dx, int dy, int vx, int vy) {
4704            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
4705        }
4706
4707        private float distanceInfluenceForSnapDuration(float f) {
4708            f -= 0.5f; // center the values about 0.
4709            f *= 0.3f * Math.PI / 2.0f;
4710            return (float) Math.sin(f);
4711        }
4712
4713        private int computeScrollDuration(int dx, int dy, int vx, int vy) {
4714            final int absDx = Math.abs(dx);
4715            final int absDy = Math.abs(dy);
4716            final boolean horizontal = absDx > absDy;
4717            final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
4718            final int delta = (int) Math.sqrt(dx * dx + dy * dy);
4719            final int containerSize = horizontal ? getWidth() : getHeight();
4720            final int halfContainerSize = containerSize / 2;
4721            final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
4722            final float distance = halfContainerSize + halfContainerSize *
4723                    distanceInfluenceForSnapDuration(distanceRatio);
4724
4725            final int duration;
4726            if (velocity > 0) {
4727                duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
4728            } else {
4729                float absDelta = (float) (horizontal ? absDx : absDy);
4730                duration = (int) (((absDelta / containerSize) + 1) * 300);
4731            }
4732            return Math.min(duration, MAX_SCROLL_DURATION);
4733        }
4734
4735        public void smoothScrollBy(int dx, int dy, int duration) {
4736            smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
4737        }
4738
4739        public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
4740            if (mInterpolator != interpolator) {
4741                mInterpolator = interpolator;
4742                mScroller = ScrollerCompat.create(getContext(), interpolator);
4743            }
4744            setScrollState(SCROLL_STATE_SETTLING);
4745            mLastFlingX = mLastFlingY = 0;
4746            mScroller.startScroll(0, 0, dx, dy, duration);
4747            postOnAnimation();
4748        }
4749
4750        public void stop() {
4751            removeCallbacks(this);
4752            mScroller.abortAnimation();
4753        }
4754
4755    }
4756
4757    void repositionShadowingViews() {
4758        // Fix up shadow views used by change animations
4759        int count = mChildHelper.getChildCount();
4760        for (int i = 0; i < count; i++) {
4761            View view = mChildHelper.getChildAt(i);
4762            ViewHolder holder = getChildViewHolder(view);
4763            if (holder != null && holder.mShadowingHolder != null) {
4764                View shadowingView = holder.mShadowingHolder.itemView;
4765                int left = view.getLeft();
4766                int top = view.getTop();
4767                if (left != shadowingView.getLeft() ||
4768                        top != shadowingView.getTop()) {
4769                    shadowingView.layout(left, top,
4770                            left + shadowingView.getWidth(),
4771                            top + shadowingView.getHeight());
4772                }
4773            }
4774        }
4775    }
4776
4777    private class RecyclerViewDataObserver extends AdapterDataObserver {
4778        RecyclerViewDataObserver() {
4779        }
4780
4781        @Override
4782        public void onChanged() {
4783            assertNotInLayoutOrScroll(null);
4784            if (mAdapter.hasStableIds()) {
4785                // TODO Determine what actually changed.
4786                // This is more important to implement now since this callback will disable all
4787                // animations because we cannot rely on positions.
4788                mState.mStructureChanged = true;
4789                setDataSetChangedAfterLayout();
4790            } else {
4791                mState.mStructureChanged = true;
4792                setDataSetChangedAfterLayout();
4793            }
4794            if (!mAdapterHelper.hasPendingUpdates()) {
4795                requestLayout();
4796            }
4797        }
4798
4799        @Override
4800        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
4801            assertNotInLayoutOrScroll(null);
4802            if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
4803                triggerUpdateProcessor();
4804            }
4805        }
4806
4807        @Override
4808        public void onItemRangeInserted(int positionStart, int itemCount) {
4809            assertNotInLayoutOrScroll(null);
4810            if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
4811                triggerUpdateProcessor();
4812            }
4813        }
4814
4815        @Override
4816        public void onItemRangeRemoved(int positionStart, int itemCount) {
4817            assertNotInLayoutOrScroll(null);
4818            if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
4819                triggerUpdateProcessor();
4820            }
4821        }
4822
4823        @Override
4824        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
4825            assertNotInLayoutOrScroll(null);
4826            if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
4827                triggerUpdateProcessor();
4828            }
4829        }
4830
4831        void triggerUpdateProcessor() {
4832            if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
4833                ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
4834            } else {
4835                mAdapterUpdateDuringMeasure = true;
4836                requestLayout();
4837            }
4838        }
4839    }
4840
4841    /**
4842     * RecycledViewPool lets you share Views between multiple RecyclerViews.
4843     * <p>
4844     * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
4845     * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
4846     * <p>
4847     * RecyclerView automatically creates a pool for itself if you don't provide one.
4848     *
4849     */
4850    public static class RecycledViewPool {
4851        private SparseArray<ArrayList<ViewHolder>> mScrap =
4852                new SparseArray<ArrayList<ViewHolder>>();
4853        private SparseIntArray mMaxScrap = new SparseIntArray();
4854        private int mAttachCount = 0;
4855
4856        private static final int DEFAULT_MAX_SCRAP = 5;
4857
4858        public void clear() {
4859            mScrap.clear();
4860        }
4861
4862        public void setMaxRecycledViews(int viewType, int max) {
4863            mMaxScrap.put(viewType, max);
4864            final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
4865            if (scrapHeap != null) {
4866                while (scrapHeap.size() > max) {
4867                    scrapHeap.remove(scrapHeap.size() - 1);
4868                }
4869            }
4870        }
4871
4872        public ViewHolder getRecycledView(int viewType) {
4873            final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
4874            if (scrapHeap != null && !scrapHeap.isEmpty()) {
4875                final int index = scrapHeap.size() - 1;
4876                final ViewHolder scrap = scrapHeap.get(index);
4877                scrapHeap.remove(index);
4878                return scrap;
4879            }
4880            return null;
4881        }
4882
4883        int size() {
4884            int count = 0;
4885            for (int i = 0; i < mScrap.size(); i ++) {
4886                ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i);
4887                if (viewHolders != null) {
4888                    count += viewHolders.size();
4889                }
4890            }
4891            return count;
4892        }
4893
4894        public void putRecycledView(ViewHolder scrap) {
4895            final int viewType = scrap.getItemViewType();
4896            final ArrayList scrapHeap = getScrapHeapForType(viewType);
4897            if (mMaxScrap.get(viewType) <= scrapHeap.size()) {
4898                return;
4899            }
4900            if (DEBUG && scrapHeap.contains(scrap)) {
4901                throw new IllegalArgumentException("this scrap item already exists");
4902            }
4903            scrap.resetInternal();
4904            scrapHeap.add(scrap);
4905        }
4906
4907        void attach(Adapter adapter) {
4908            mAttachCount++;
4909        }
4910
4911        void detach() {
4912            mAttachCount--;
4913        }
4914
4915
4916        /**
4917         * Detaches the old adapter and attaches the new one.
4918         * <p>
4919         * RecycledViewPool will clear its cache if it has only one adapter attached and the new
4920         * adapter uses a different ViewHolder than the oldAdapter.
4921         *
4922         * @param oldAdapter The previous adapter instance. Will be detached.
4923         * @param newAdapter The new adapter instance. Will be attached.
4924         * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
4925         *                               ViewHolder and view types.
4926         */
4927        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
4928                boolean compatibleWithPrevious) {
4929            if (oldAdapter != null) {
4930                detach();
4931            }
4932            if (!compatibleWithPrevious && mAttachCount == 0) {
4933                clear();
4934            }
4935            if (newAdapter != null) {
4936                attach(newAdapter);
4937            }
4938        }
4939
4940        private ArrayList<ViewHolder> getScrapHeapForType(int viewType) {
4941            ArrayList<ViewHolder> scrap = mScrap.get(viewType);
4942            if (scrap == null) {
4943                scrap = new ArrayList<>();
4944                mScrap.put(viewType, scrap);
4945                if (mMaxScrap.indexOfKey(viewType) < 0) {
4946                    mMaxScrap.put(viewType, DEFAULT_MAX_SCRAP);
4947                }
4948            }
4949            return scrap;
4950        }
4951    }
4952
4953    /**
4954     * A Recycler is responsible for managing scrapped or detached item views for reuse.
4955     *
4956     * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
4957     * that has been marked for removal or reuse.</p>
4958     *
4959     * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
4960     * an adapter's data set representing the data at a given position or item ID.
4961     * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
4962     * If not, the view can be quickly reused by the LayoutManager with no further work.
4963     * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
4964     * may be repositioned by a LayoutManager without remeasurement.</p>
4965     */
4966    public final class Recycler {
4967        final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
4968        ArrayList<ViewHolder> mChangedScrap = null;
4969
4970        final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
4971
4972        private final List<ViewHolder>
4973                mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
4974
4975        private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
4976        int mViewCacheMax = DEFAULT_CACHE_SIZE;
4977
4978        private RecycledViewPool mRecyclerPool;
4979
4980        private ViewCacheExtension mViewCacheExtension;
4981
4982        static final int DEFAULT_CACHE_SIZE = 2;
4983
4984        /**
4985         * Clear scrap views out of this recycler. Detached views contained within a
4986         * recycled view pool will remain.
4987         */
4988        public void clear() {
4989            mAttachedScrap.clear();
4990            recycleAndClearCachedViews();
4991        }
4992
4993        /**
4994         * Set the maximum number of detached, valid views we should retain for later use.
4995         *
4996         * @param viewCount Number of views to keep before sending views to the shared pool
4997         */
4998        public void setViewCacheSize(int viewCount) {
4999            mRequestedCacheMax = viewCount;
5000            updateViewCacheSize();
5001        }
5002
5003        void updateViewCacheSize() {
5004            int extraCache = 0;
5005            if (mLayout != null && ALLOW_PREFETCHING) {
5006                extraCache = mLayout.isItemPrefetchEnabled() ? mLayout.getItemPrefetchCount() : 0;
5007            }
5008            mViewCacheMax = mRequestedCacheMax + extraCache;
5009            // first, try the views that can be recycled
5010            for (int i = mCachedViews.size() - 1;
5011                    i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
5012                recycleCachedViewAt(i);
5013            }
5014        }
5015
5016        /**
5017         * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
5018         *
5019         * @return List of ViewHolders in the scrap list.
5020         */
5021        public List<ViewHolder> getScrapList() {
5022            return mUnmodifiableAttachedScrap;
5023        }
5024
5025        /**
5026         * Helper method for getViewForPosition.
5027         * <p>
5028         * Checks whether a given view holder can be used for the provided position.
5029         *
5030         * @param holder ViewHolder
5031         * @return true if ViewHolder matches the provided position, false otherwise
5032         */
5033        boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
5034            // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
5035            // if it is not removed, verify the type and id.
5036            if (holder.isRemoved()) {
5037                if (DEBUG && !mState.isPreLayout()) {
5038                    throw new IllegalStateException("should not receive a removed view unless it"
5039                            + " is pre layout");
5040                }
5041                return mState.isPreLayout();
5042            }
5043            if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
5044                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
5045                        + "adapter position" + holder);
5046            }
5047            if (!mState.isPreLayout()) {
5048                // don't check type if it is pre-layout.
5049                final int type = mAdapter.getItemViewType(holder.mPosition);
5050                if (type != holder.getItemViewType()) {
5051                    return false;
5052                }
5053            }
5054            if (mAdapter.hasStableIds()) {
5055                return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
5056            }
5057            return true;
5058        }
5059
5060        /**
5061         * Binds the given View to the position. The View can be a View previously retrieved via
5062         * {@link #getViewForPosition(int)} or created by
5063         * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
5064         * <p>
5065         * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
5066         * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
5067         * wants to handle its own recycling logic.
5068         * <p>
5069         * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
5070         * you don't need to call this method unless you want to bind this View to another position.
5071         *
5072         * @param view The view to update.
5073         * @param position The position of the item to bind to this View.
5074         */
5075        public void bindViewToPosition(View view, int position) {
5076            ViewHolder holder = getChildViewHolderInt(view);
5077            if (holder == null) {
5078                throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
5079                        + " pass arbitrary views to this method, they should be created by the "
5080                        + "Adapter");
5081            }
5082            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5083            if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5084                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5085                        + "position " + position + "(offset:" + offsetPosition + ")."
5086                        + "state:" + mState.getItemCount());
5087            }
5088            holder.mOwnerRecyclerView = RecyclerView.this;
5089            mAdapter.bindViewHolder(holder, offsetPosition);
5090            attachAccessibilityDelegate(view);
5091            if (mState.isPreLayout()) {
5092                holder.mPreLayoutPosition = position;
5093            }
5094
5095            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5096            final LayoutParams rvLayoutParams;
5097            if (lp == null) {
5098                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5099                holder.itemView.setLayoutParams(rvLayoutParams);
5100            } else if (!checkLayoutParams(lp)) {
5101                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5102                holder.itemView.setLayoutParams(rvLayoutParams);
5103            } else {
5104                rvLayoutParams = (LayoutParams) lp;
5105            }
5106
5107            rvLayoutParams.mInsetsDirty = true;
5108            rvLayoutParams.mViewHolder = holder;
5109            rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
5110        }
5111
5112        /**
5113         * RecyclerView provides artificial position range (item count) in pre-layout state and
5114         * automatically maps these positions to {@link Adapter} positions when
5115         * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
5116         * <p>
5117         * Usually, LayoutManager does not need to worry about this. However, in some cases, your
5118         * LayoutManager may need to call some custom component with item positions in which
5119         * case you need the actual adapter position instead of the pre layout position. You
5120         * can use this method to convert a pre-layout position to adapter (post layout) position.
5121         * <p>
5122         * Note that if the provided position belongs to a deleted ViewHolder, this method will
5123         * return -1.
5124         * <p>
5125         * Calling this method in post-layout state returns the same value back.
5126         *
5127         * @param position The pre-layout position to convert. Must be greater or equal to 0 and
5128         *                 less than {@link State#getItemCount()}.
5129         */
5130        public int convertPreLayoutPositionToPostLayout(int position) {
5131            if (position < 0 || position >= mState.getItemCount()) {
5132                throw new IndexOutOfBoundsException("invalid position " + position + ". State "
5133                        + "item count is " + mState.getItemCount());
5134            }
5135            if (!mState.isPreLayout()) {
5136                return position;
5137            }
5138            return mAdapterHelper.findPositionOffset(position);
5139        }
5140
5141        /**
5142         * Obtain a view initialized for the given position.
5143         *
5144         * This method should be used by {@link LayoutManager} implementations to obtain
5145         * views to represent data from an {@link Adapter}.
5146         * <p>
5147         * The Recycler may reuse a scrap or detached view from a shared pool if one is
5148         * available for the correct view type. If the adapter has not indicated that the
5149         * data at the given position has changed, the Recycler will attempt to hand back
5150         * a scrap view that was previously initialized for that data without rebinding.
5151         *
5152         * @param position Position to obtain a view for
5153         * @return A view representing the data at <code>position</code> from <code>adapter</code>
5154         */
5155        public View getViewForPosition(int position) {
5156            return getViewForPosition(position, false);
5157        }
5158
5159        View getViewForPosition(int position, boolean dryRun) {
5160            if (position < 0 || position >= mState.getItemCount()) {
5161                throw new IndexOutOfBoundsException("Invalid item position " + position
5162                        + "(" + position + "). Item count:" + mState.getItemCount());
5163            }
5164            boolean fromScrap = false;
5165            ViewHolder holder = null;
5166            // 0) If there is a changed scrap, try to find from there
5167            if (mState.isPreLayout()) {
5168                holder = getChangedScrapViewForPosition(position);
5169                fromScrap = holder != null;
5170            }
5171            // 1) Find from scrap by position
5172            if (holder == null) {
5173                holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);
5174                if (holder != null) {
5175                    if (!validateViewHolderForOffsetPosition(holder)) {
5176                        // recycle this scrap
5177                        if (!dryRun) {
5178                            // we would like to recycle this but need to make sure it is not used by
5179                            // animation logic etc.
5180                            holder.addFlags(ViewHolder.FLAG_INVALID);
5181                            if (holder.isScrap()) {
5182                                removeDetachedView(holder.itemView, false);
5183                                holder.unScrap();
5184                            } else if (holder.wasReturnedFromScrap()) {
5185                                holder.clearReturnedFromScrapFlag();
5186                            }
5187                            recycleViewHolderInternal(holder);
5188                        }
5189                        holder = null;
5190                    } else {
5191                        fromScrap = true;
5192                    }
5193                }
5194            }
5195            if (holder == null) {
5196                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5197                if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5198                    throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5199                            + "position " + position + "(offset:" + offsetPosition + ")."
5200                            + "state:" + mState.getItemCount());
5201                }
5202
5203                final int type = mAdapter.getItemViewType(offsetPosition);
5204                // 2) Find from scrap via stable ids, if exists
5205                if (mAdapter.hasStableIds()) {
5206                    holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
5207                    if (holder != null) {
5208                        // update position
5209                        holder.mPosition = offsetPosition;
5210                        fromScrap = true;
5211                    }
5212                }
5213                if (holder == null && mViewCacheExtension != null) {
5214                    // We are NOT sending the offsetPosition because LayoutManager does not
5215                    // know it.
5216                    final View view = mViewCacheExtension
5217                            .getViewForPositionAndType(this, position, type);
5218                    if (view != null) {
5219                        holder = getChildViewHolder(view);
5220                        if (holder == null) {
5221                            throw new IllegalArgumentException("getViewForPositionAndType returned"
5222                                    + " a view which does not have a ViewHolder");
5223                        } else if (holder.shouldIgnore()) {
5224                            throw new IllegalArgumentException("getViewForPositionAndType returned"
5225                                    + " a view that is ignored. You must call stopIgnoring before"
5226                                    + " returning this view.");
5227                        }
5228                    }
5229                }
5230                if (holder == null) { // fallback to recycler
5231                    // try recycler.
5232                    // Head to the shared pool.
5233                    if (DEBUG) {
5234                        Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "
5235                                + "pool");
5236                    }
5237                    holder = getRecycledViewPool().getRecycledView(type);
5238                    if (holder != null) {
5239                        holder.resetInternal();
5240                        if (FORCE_INVALIDATE_DISPLAY_LIST) {
5241                            invalidateDisplayListInt(holder);
5242                        }
5243                    }
5244                }
5245                if (holder == null) {
5246                    holder = mAdapter.createViewHolder(RecyclerView.this, type);
5247                    if (DEBUG) {
5248                        Log.d(TAG, "getViewForPosition created new ViewHolder");
5249                    }
5250                }
5251            }
5252
5253            // This is very ugly but the only place we can grab this information
5254            // before the View is rebound and returned to the LayoutManager for post layout ops.
5255            // We don't need this in pre-layout since the VH is not updated by the LM.
5256            if (fromScrap && !mState.isPreLayout() && holder
5257                    .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
5258                holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5259                if (mState.mRunSimpleAnimations) {
5260                    int changeFlags = ItemAnimator
5261                            .buildAdapterChangeFlagsForAnimations(holder);
5262                    changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
5263                    final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
5264                            holder, changeFlags, holder.getUnmodifiedPayloads());
5265                    recordAnimationInfoIfBouncedHiddenView(holder, info);
5266                }
5267            }
5268
5269            boolean bound = false;
5270            if (mState.isPreLayout() && holder.isBound()) {
5271                // do not update unless we absolutely have to.
5272                holder.mPreLayoutPosition = position;
5273            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
5274                if (DEBUG && holder.isRemoved()) {
5275                    throw new IllegalStateException("Removed holder should be bound and it should"
5276                            + " come here only in pre-layout. Holder: " + holder);
5277                }
5278                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5279                holder.mOwnerRecyclerView = RecyclerView.this;
5280                mAdapter.bindViewHolder(holder, offsetPosition);
5281                attachAccessibilityDelegate(holder.itemView);
5282                bound = true;
5283                if (mState.isPreLayout()) {
5284                    holder.mPreLayoutPosition = position;
5285                }
5286            }
5287
5288            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5289            final LayoutParams rvLayoutParams;
5290            if (lp == null) {
5291                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5292                holder.itemView.setLayoutParams(rvLayoutParams);
5293            } else if (!checkLayoutParams(lp)) {
5294                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5295                holder.itemView.setLayoutParams(rvLayoutParams);
5296            } else {
5297                rvLayoutParams = (LayoutParams) lp;
5298            }
5299            rvLayoutParams.mViewHolder = holder;
5300            rvLayoutParams.mPendingInvalidate = fromScrap && bound;
5301            return holder.itemView;
5302        }
5303
5304        private void attachAccessibilityDelegate(View itemView) {
5305            if (isAccessibilityEnabled()) {
5306                if (ViewCompat.getImportantForAccessibility(itemView) ==
5307                        ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
5308                    ViewCompat.setImportantForAccessibility(itemView,
5309                            ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
5310                }
5311                if (!ViewCompat.hasAccessibilityDelegate(itemView)) {
5312                    ViewCompat.setAccessibilityDelegate(itemView,
5313                            mAccessibilityDelegate.getItemDelegate());
5314                }
5315            }
5316        }
5317
5318        private void invalidateDisplayListInt(ViewHolder holder) {
5319            if (holder.itemView instanceof ViewGroup) {
5320                invalidateDisplayListInt((ViewGroup) holder.itemView, false);
5321            }
5322        }
5323
5324        private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
5325            for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
5326                final View view = viewGroup.getChildAt(i);
5327                if (view instanceof ViewGroup) {
5328                    invalidateDisplayListInt((ViewGroup) view, true);
5329                }
5330            }
5331            if (!invalidateThis) {
5332                return;
5333            }
5334            // we need to force it to become invisible
5335            if (viewGroup.getVisibility() == View.INVISIBLE) {
5336                viewGroup.setVisibility(View.VISIBLE);
5337                viewGroup.setVisibility(View.INVISIBLE);
5338            } else {
5339                final int visibility = viewGroup.getVisibility();
5340                viewGroup.setVisibility(View.INVISIBLE);
5341                viewGroup.setVisibility(visibility);
5342            }
5343        }
5344
5345        /**
5346         * Recycle a detached view. The specified view will be added to a pool of views
5347         * for later rebinding and reuse.
5348         *
5349         * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
5350         * View is scrapped, it will be removed from scrap list.</p>
5351         *
5352         * @param view Removed view for recycling
5353         * @see LayoutManager#removeAndRecycleView(View, Recycler)
5354         */
5355        public void recycleView(View view) {
5356            // This public recycle method tries to make view recycle-able since layout manager
5357            // intended to recycle this view (e.g. even if it is in scrap or change cache)
5358            ViewHolder holder = getChildViewHolderInt(view);
5359            if (holder.isTmpDetached()) {
5360                removeDetachedView(view, false);
5361            }
5362            if (holder.isScrap()) {
5363                holder.unScrap();
5364            } else if (holder.wasReturnedFromScrap()){
5365                holder.clearReturnedFromScrapFlag();
5366            }
5367            recycleViewHolderInternal(holder);
5368        }
5369
5370        /**
5371         * Internally, use this method instead of {@link #recycleView(android.view.View)} to
5372         * catch potential bugs.
5373         * @param view
5374         */
5375        void recycleViewInternal(View view) {
5376            recycleViewHolderInternal(getChildViewHolderInt(view));
5377        }
5378
5379        void recycleAndClearCachedViews() {
5380            final int count = mCachedViews.size();
5381            for (int i = count - 1; i >= 0; i--) {
5382                recycleCachedViewAt(i);
5383            }
5384            mCachedViews.clear();
5385            if (ALLOW_PREFETCHING) {
5386                mViewPrefetcher.clearPrefetchPositions();
5387            }
5388        }
5389
5390        /**
5391         * Recycles a cached view and removes the view from the list. Views are added to cache
5392         * if and only if they are recyclable, so this method does not check it again.
5393         * <p>
5394         * A small exception to this rule is when the view does not have an animator reference
5395         * but transient state is true (due to animations created outside ItemAnimator). In that
5396         * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
5397         * still recyclable since Adapter wants to do so.
5398         *
5399         * @param cachedViewIndex The index of the view in cached views list
5400         */
5401        void recycleCachedViewAt(int cachedViewIndex) {
5402            if (DEBUG) {
5403                Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
5404            }
5405            ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
5406            if (DEBUG) {
5407                Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
5408            }
5409            addViewHolderToRecycledViewPool(viewHolder);
5410            mCachedViews.remove(cachedViewIndex);
5411        }
5412
5413        /**
5414         * internal implementation checks if view is scrapped or attached and throws an exception
5415         * if so.
5416         * Public version un-scraps before calling recycle.
5417         */
5418        void recycleViewHolderInternal(ViewHolder holder) {
5419            if (holder.isScrap() || holder.itemView.getParent() != null) {
5420                throw new IllegalArgumentException(
5421                        "Scrapped or attached views may not be recycled. isScrap:"
5422                                + holder.isScrap() + " isAttached:"
5423                                + (holder.itemView.getParent() != null));
5424            }
5425
5426            if (holder.isTmpDetached()) {
5427                throw new IllegalArgumentException("Tmp detached view should be removed "
5428                        + "from RecyclerView before it can be recycled: " + holder);
5429            }
5430
5431            if (holder.shouldIgnore()) {
5432                throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
5433                        + " should first call stopIgnoringView(view) before calling recycle.");
5434            }
5435            //noinspection unchecked
5436            final boolean transientStatePreventsRecycling = holder
5437                    .doesTransientStatePreventRecycling();
5438            final boolean forceRecycle = mAdapter != null
5439                    && transientStatePreventsRecycling
5440                    && mAdapter.onFailedToRecycleView(holder);
5441            boolean cached = false;
5442            boolean recycled = false;
5443            if (DEBUG && mCachedViews.contains(holder)) {
5444                throw new IllegalArgumentException("cached view received recycle internal? " +
5445                        holder);
5446            }
5447            if (forceRecycle || holder.isRecyclable()) {
5448                if (mViewCacheMax > 0
5449                        && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
5450                                | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_UPDATE)) {
5451                    // Retire oldest cached view
5452                    int cachedViewSize = mCachedViews.size();
5453                    if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
5454                        recycleCachedViewAt(0);
5455                        cachedViewSize--;
5456                    }
5457
5458                    int targetCacheIndex = cachedViewSize;
5459                    if (ALLOW_PREFETCHING
5460                            && cachedViewSize > 0
5461                            && !mViewPrefetcher.lastPrefetchIncludedPosition(holder.mPosition)) {
5462                        // when adding the view, skip past most recently prefetched views
5463                        int cacheIndex = cachedViewSize - 1;
5464                        while (cacheIndex >= 0) {
5465                            int cachedPos = mCachedViews.get(cacheIndex).mPosition;
5466                            if (!mViewPrefetcher.lastPrefetchIncludedPosition(cachedPos)) {
5467                                break;
5468                            }
5469                            cacheIndex--;
5470                        }
5471                        targetCacheIndex = cacheIndex + 1;
5472                    }
5473                    mCachedViews.add(targetCacheIndex, holder);
5474                    cached = true;
5475                }
5476                if (!cached) {
5477                    addViewHolderToRecycledViewPool(holder);
5478                    recycled = true;
5479                }
5480            } else if (DEBUG) {
5481                Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
5482                        + "re-visit here. We are still removing it from animation lists");
5483            }
5484            // even if the holder is not removed, we still call this method so that it is removed
5485            // from view holder lists.
5486            mViewInfoStore.removeViewHolder(holder);
5487            if (!cached && !recycled && transientStatePreventsRecycling) {
5488                holder.mOwnerRecyclerView = null;
5489            }
5490        }
5491
5492        void addViewHolderToRecycledViewPool(ViewHolder holder) {
5493            ViewCompat.setAccessibilityDelegate(holder.itemView, null);
5494            dispatchViewRecycled(holder);
5495            holder.mOwnerRecyclerView = null;
5496            getRecycledViewPool().putRecycledView(holder);
5497        }
5498
5499        /**
5500         * Used as a fast path for unscrapping and recycling a view during a bulk operation.
5501         * The caller must call {@link #clearScrap()} when it's done to update the recycler's
5502         * internal bookkeeping.
5503         */
5504        void quickRecycleScrapView(View view) {
5505            final ViewHolder holder = getChildViewHolderInt(view);
5506            holder.mScrapContainer = null;
5507            holder.mInChangeScrap = false;
5508            holder.clearReturnedFromScrapFlag();
5509            recycleViewHolderInternal(holder);
5510        }
5511
5512        /**
5513         * Mark an attached view as scrap.
5514         *
5515         * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
5516         * for rebinding and reuse. Requests for a view for a given position may return a
5517         * reused or rebound scrap view instance.</p>
5518         *
5519         * @param view View to scrap
5520         */
5521        void scrapView(View view) {
5522            final ViewHolder holder = getChildViewHolderInt(view);
5523            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
5524                    || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
5525                if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
5526                    throw new IllegalArgumentException("Called scrap view with an invalid view."
5527                            + " Invalid views cannot be reused from scrap, they should rebound from"
5528                            + " recycler pool.");
5529                }
5530                holder.setScrapContainer(this, false);
5531                mAttachedScrap.add(holder);
5532            } else {
5533                if (mChangedScrap == null) {
5534                    mChangedScrap = new ArrayList<ViewHolder>();
5535                }
5536                holder.setScrapContainer(this, true);
5537                mChangedScrap.add(holder);
5538            }
5539        }
5540
5541        /**
5542         * Remove a previously scrapped view from the pool of eligible scrap.
5543         *
5544         * <p>This view will no longer be eligible for reuse until re-scrapped or
5545         * until it is explicitly removed and recycled.</p>
5546         */
5547        void unscrapView(ViewHolder holder) {
5548            if (holder.mInChangeScrap) {
5549                mChangedScrap.remove(holder);
5550            } else {
5551                mAttachedScrap.remove(holder);
5552            }
5553            holder.mScrapContainer = null;
5554            holder.mInChangeScrap = false;
5555            holder.clearReturnedFromScrapFlag();
5556        }
5557
5558        int getScrapCount() {
5559            return mAttachedScrap.size();
5560        }
5561
5562        View getScrapViewAt(int index) {
5563            return mAttachedScrap.get(index).itemView;
5564        }
5565
5566        void clearScrap() {
5567            mAttachedScrap.clear();
5568            if (mChangedScrap != null) {
5569                mChangedScrap.clear();
5570            }
5571        }
5572
5573        ViewHolder getChangedScrapViewForPosition(int position) {
5574            // If pre-layout, check the changed scrap for an exact match.
5575            final int changedScrapSize;
5576            if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
5577                return null;
5578            }
5579            // find by position
5580            for (int i = 0; i < changedScrapSize; i++) {
5581                final ViewHolder holder = mChangedScrap.get(i);
5582                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
5583                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5584                    return holder;
5585                }
5586            }
5587            // find by id
5588            if (mAdapter.hasStableIds()) {
5589                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5590                if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
5591                    final long id = mAdapter.getItemId(offsetPosition);
5592                    for (int i = 0; i < changedScrapSize; i++) {
5593                        final ViewHolder holder = mChangedScrap.get(i);
5594                        if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
5595                            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5596                            return holder;
5597                        }
5598                    }
5599                }
5600            }
5601            return null;
5602        }
5603
5604        /**
5605         * Returns a scrap view for the position. If type is not INVALID_TYPE, it also checks if
5606         * ViewHolder's type matches the provided type.
5607         *
5608         * @param position Item position
5609         * @param type View type
5610         * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
5611         * @return a ViewHolder that can be re-used for this position.
5612         */
5613        ViewHolder getScrapViewForPosition(int position, int type, boolean dryRun) {
5614            final int scrapCount = mAttachedScrap.size();
5615
5616            // Try first for an exact, non-invalid match from scrap.
5617            for (int i = 0; i < scrapCount; i++) {
5618                final ViewHolder holder = mAttachedScrap.get(i);
5619                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
5620                        && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
5621                    if (type != INVALID_TYPE && holder.getItemViewType() != type) {
5622                        Log.e(TAG, "Scrap view for position " + position + " isn't dirty but has" +
5623                                " wrong view type! (found " + holder.getItemViewType() +
5624                                " but expected " + type + ")");
5625                        break;
5626                    }
5627                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5628                    return holder;
5629                }
5630            }
5631
5632            if (!dryRun) {
5633                View view = mChildHelper.findHiddenNonRemovedView(position, type);
5634                if (view != null) {
5635                    // This View is good to be used. We just need to unhide, detach and move to the
5636                    // scrap list.
5637                    final ViewHolder vh = getChildViewHolderInt(view);
5638                    mChildHelper.unhide(view);
5639                    int layoutIndex = mChildHelper.indexOfChild(view);
5640                    if (layoutIndex == RecyclerView.NO_POSITION) {
5641                        throw new IllegalStateException("layout index should not be -1 after "
5642                                + "unhiding a view:" + vh);
5643                    }
5644                    mChildHelper.detachViewFromParent(layoutIndex);
5645                    scrapView(view);
5646                    vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
5647                            | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5648                    return vh;
5649                }
5650            }
5651
5652            // Search in our first-level recycled view cache.
5653            final int cacheSize = mCachedViews.size();
5654            for (int i = 0; i < cacheSize; i++) {
5655                final ViewHolder holder = mCachedViews.get(i);
5656                // invalid view holders may be in cache if adapter has stable ids as they can be
5657                // retrieved via getScrapViewForId
5658                if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
5659                    if (!dryRun) {
5660                        mCachedViews.remove(i);
5661                    }
5662                    if (DEBUG) {
5663                        Log.d(TAG, "getScrapViewForPosition(" + position + ", " + type +
5664                                ") found match in cache: " + holder);
5665                    }
5666                    return holder;
5667                }
5668            }
5669            return null;
5670        }
5671
5672        ViewHolder getScrapViewForId(long id, int type, boolean dryRun) {
5673            // Look in our attached views first
5674            final int count = mAttachedScrap.size();
5675            for (int i = count - 1; i >= 0; i--) {
5676                final ViewHolder holder = mAttachedScrap.get(i);
5677                if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
5678                    if (type == holder.getItemViewType()) {
5679                        holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5680                        if (holder.isRemoved()) {
5681                            // this might be valid in two cases:
5682                            // > item is removed but we are in pre-layout pass
5683                            // >> do nothing. return as is. make sure we don't rebind
5684                            // > item is removed then added to another position and we are in
5685                            // post layout.
5686                            // >> remove removed and invalid flags, add update flag to rebind
5687                            // because item was invisible to us and we don't know what happened in
5688                            // between.
5689                            if (!mState.isPreLayout()) {
5690                                holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE |
5691                                        ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
5692                            }
5693                        }
5694                        return holder;
5695                    } else if (!dryRun) {
5696                        // if we are running animations, it is actually better to keep it in scrap
5697                        // but this would force layout manager to lay it out which would be bad.
5698                        // Recycle this scrap. Type mismatch.
5699                        mAttachedScrap.remove(i);
5700                        removeDetachedView(holder.itemView, false);
5701                        quickRecycleScrapView(holder.itemView);
5702                    }
5703                }
5704            }
5705
5706            // Search the first-level cache
5707            final int cacheSize = mCachedViews.size();
5708            for (int i = cacheSize - 1; i >= 0; i--) {
5709                final ViewHolder holder = mCachedViews.get(i);
5710                if (holder.getItemId() == id) {
5711                    if (type == holder.getItemViewType()) {
5712                        if (!dryRun) {
5713                            mCachedViews.remove(i);
5714                        }
5715                        return holder;
5716                    } else if (!dryRun) {
5717                        recycleCachedViewAt(i);
5718                    }
5719                }
5720            }
5721            return null;
5722        }
5723
5724        void dispatchViewRecycled(ViewHolder holder) {
5725            if (mRecyclerListener != null) {
5726                mRecyclerListener.onViewRecycled(holder);
5727            }
5728            if (mAdapter != null) {
5729                mAdapter.onViewRecycled(holder);
5730            }
5731            if (mState != null) {
5732                mViewInfoStore.removeViewHolder(holder);
5733            }
5734            if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
5735        }
5736
5737        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
5738                boolean compatibleWithPrevious) {
5739            clear();
5740            getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
5741        }
5742
5743        void offsetPositionRecordsForMove(int from, int to) {
5744            final int start, end, inBetweenOffset;
5745            if (from < to) {
5746                start = from;
5747                end = to;
5748                inBetweenOffset = -1;
5749            } else {
5750                start = to;
5751                end = from;
5752                inBetweenOffset = 1;
5753            }
5754            final int cachedCount = mCachedViews.size();
5755            for (int i = 0; i < cachedCount; i++) {
5756                final ViewHolder holder = mCachedViews.get(i);
5757                if (holder == null || holder.mPosition < start || holder.mPosition > end) {
5758                    continue;
5759                }
5760                if (holder.mPosition == from) {
5761                    holder.offsetPosition(to - from, false);
5762                } else {
5763                    holder.offsetPosition(inBetweenOffset, false);
5764                }
5765                if (DEBUG) {
5766                    Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder " +
5767                            holder);
5768                }
5769            }
5770        }
5771
5772        void offsetPositionRecordsForInsert(int insertedAt, int count) {
5773            final int cachedCount = mCachedViews.size();
5774            for (int i = 0; i < cachedCount; i++) {
5775                final ViewHolder holder = mCachedViews.get(i);
5776                if (holder != null && holder.mPosition >= insertedAt) {
5777                    if (DEBUG) {
5778                        Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder " +
5779                                holder + " now at position " + (holder.mPosition + count));
5780                    }
5781                    holder.offsetPosition(count, true);
5782                }
5783            }
5784        }
5785
5786        /**
5787         * @param removedFrom Remove start index
5788         * @param count Remove count
5789         * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
5790         *                         false, they'll be applied before the second layout pass
5791         */
5792        void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
5793            final int removedEnd = removedFrom + count;
5794            final int cachedCount = mCachedViews.size();
5795            for (int i = cachedCount - 1; i >= 0; i--) {
5796                final ViewHolder holder = mCachedViews.get(i);
5797                if (holder != null) {
5798                    if (holder.mPosition >= removedEnd) {
5799                        if (DEBUG) {
5800                            Log.d(TAG, "offsetPositionRecordsForRemove cached " + i +
5801                                    " holder " + holder + " now at position " +
5802                                    (holder.mPosition - count));
5803                        }
5804                        holder.offsetPosition(-count, applyToPreLayout);
5805                    } else if (holder.mPosition >= removedFrom) {
5806                        // Item for this view was removed. Dump it from the cache.
5807                        holder.addFlags(ViewHolder.FLAG_REMOVED);
5808                        recycleCachedViewAt(i);
5809                    }
5810                }
5811            }
5812        }
5813
5814        void setViewCacheExtension(ViewCacheExtension extension) {
5815            mViewCacheExtension = extension;
5816        }
5817
5818        void setRecycledViewPool(RecycledViewPool pool) {
5819            if (mRecyclerPool != null) {
5820                mRecyclerPool.detach();
5821            }
5822            mRecyclerPool = pool;
5823            if (pool != null) {
5824                mRecyclerPool.attach(getAdapter());
5825            }
5826        }
5827
5828        RecycledViewPool getRecycledViewPool() {
5829            if (mRecyclerPool == null) {
5830                mRecyclerPool = new RecycledViewPool();
5831            }
5832            return mRecyclerPool;
5833        }
5834
5835        void viewRangeUpdate(int positionStart, int itemCount) {
5836            final int positionEnd = positionStart + itemCount;
5837            final int cachedCount = mCachedViews.size();
5838            for (int i = cachedCount - 1; i >= 0; i--) {
5839                final ViewHolder holder = mCachedViews.get(i);
5840                if (holder == null) {
5841                    continue;
5842                }
5843
5844                final int pos = holder.getLayoutPosition();
5845                if (pos >= positionStart && pos < positionEnd) {
5846                    holder.addFlags(ViewHolder.FLAG_UPDATE);
5847                    recycleCachedViewAt(i);
5848                    // cached views should not be flagged as changed because this will cause them
5849                    // to animate when they are returned from cache.
5850                }
5851            }
5852        }
5853
5854        void setAdapterPositionsAsUnknown() {
5855            final int cachedCount = mCachedViews.size();
5856            for (int i = 0; i < cachedCount; i++) {
5857                final ViewHolder holder = mCachedViews.get(i);
5858                if (holder != null) {
5859                    holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
5860                }
5861            }
5862        }
5863
5864        void markKnownViewsInvalid() {
5865            if (mAdapter != null && mAdapter.hasStableIds()) {
5866                final int cachedCount = mCachedViews.size();
5867                for (int i = 0; i < cachedCount; i++) {
5868                    final ViewHolder holder = mCachedViews.get(i);
5869                    if (holder != null) {
5870                        holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
5871                        holder.addChangePayload(null);
5872                    }
5873                }
5874            } else {
5875                // we cannot re-use cached views in this case. Recycle them all
5876                recycleAndClearCachedViews();
5877            }
5878        }
5879
5880        void clearOldPositions() {
5881            final int cachedCount = mCachedViews.size();
5882            for (int i = 0; i < cachedCount; i++) {
5883                final ViewHolder holder = mCachedViews.get(i);
5884                holder.clearOldPosition();
5885            }
5886            final int scrapCount = mAttachedScrap.size();
5887            for (int i = 0; i < scrapCount; i++) {
5888                mAttachedScrap.get(i).clearOldPosition();
5889            }
5890            if (mChangedScrap != null) {
5891                final int changedScrapCount = mChangedScrap.size();
5892                for (int i = 0; i < changedScrapCount; i++) {
5893                    mChangedScrap.get(i).clearOldPosition();
5894                }
5895            }
5896        }
5897
5898        void markItemDecorInsetsDirty() {
5899            final int cachedCount = mCachedViews.size();
5900            for (int i = 0; i < cachedCount; i++) {
5901                final ViewHolder holder = mCachedViews.get(i);
5902                LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
5903                if (layoutParams != null) {
5904                    layoutParams.mInsetsDirty = true;
5905                }
5906            }
5907        }
5908
5909        boolean isPrefetchPositionAttached(int position) {
5910            final int childCount = mChildHelper.getUnfilteredChildCount();
5911            for (int i = 0; i < childCount; i++) {
5912                View attachedView = mChildHelper.getUnfilteredChildAt(i);
5913                ViewHolder holder = getChildViewHolderInt(attachedView);
5914                // TODO: consider ignoring if holder isInvalid
5915                // Note: can use mPosition here because adapter doesn't have pending updates
5916                if (holder.mPosition == position) {
5917                    return true;
5918                }
5919            }
5920            return false;
5921        }
5922
5923        void prefetch(int[] itemPrefetchArray, int viewCount) {
5924            if (viewCount == 0) return;
5925
5926            int childPosition = itemPrefetchArray[viewCount - 1];
5927            if (childPosition < 0) {
5928                throw new IllegalArgumentException("Recycler requested to prefetch invalid view "
5929                        + childPosition);
5930            }
5931
5932            View prefetchView = null;
5933            if (!isPrefetchPositionAttached(childPosition)) {
5934                // only prefetch if child not already attached
5935                prefetchView = getViewForPosition(childPosition);
5936            }
5937            if (viewCount > 1) {
5938                prefetch(itemPrefetchArray, viewCount - 1);
5939            }
5940            if (prefetchView != null) {
5941                recycleView(prefetchView);
5942            }
5943        }
5944    }
5945
5946    /**
5947     * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
5948     * be controlled by the developer.
5949     * <p>
5950     * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
5951     * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
5952     * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
5953     * {@link RecycledViewPool}.
5954     * <p>
5955     * Note that, Recycler never sends Views to this method to be cached. It is developers
5956     * responsibility to decide whether they want to keep their Views in this custom cache or let
5957     * the default recycling policy handle it.
5958     */
5959    public abstract static class ViewCacheExtension {
5960
5961        /**
5962         * Returns a View that can be binded to the given Adapter position.
5963         * <p>
5964         * This method should <b>not</b> create a new View. Instead, it is expected to return
5965         * an already created View that can be re-used for the given type and position.
5966         * If the View is marked as ignored, it should first call
5967         * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
5968         * <p>
5969         * RecyclerView will re-bind the returned View to the position if necessary.
5970         *
5971         * @param recycler The Recycler that can be used to bind the View
5972         * @param position The adapter position
5973         * @param type     The type of the View, defined by adapter
5974         * @return A View that is bound to the given position or NULL if there is no View to re-use
5975         * @see LayoutManager#ignoreView(View)
5976         */
5977        abstract public View getViewForPositionAndType(Recycler recycler, int position, int type);
5978    }
5979
5980    /**
5981     * Base class for an Adapter
5982     *
5983     * <p>Adapters provide a binding from an app-specific data set to views that are displayed
5984     * within a {@link RecyclerView}.</p>
5985     */
5986    public static abstract class Adapter<VH extends ViewHolder> {
5987        private final AdapterDataObservable mObservable = new AdapterDataObservable();
5988        private boolean mHasStableIds = false;
5989
5990        /**
5991         * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
5992         * an item.
5993         * <p>
5994         * This new ViewHolder should be constructed with a new View that can represent the items
5995         * of the given type. You can either create a new View manually or inflate it from an XML
5996         * layout file.
5997         * <p>
5998         * The new ViewHolder will be used to display items of the adapter using
5999         * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
6000         * different items in the data set, it is a good idea to cache references to sub views of
6001         * the View to avoid unnecessary {@link View#findViewById(int)} calls.
6002         *
6003         * @param parent The ViewGroup into which the new View will be added after it is bound to
6004         *               an adapter position.
6005         * @param viewType The view type of the new View.
6006         *
6007         * @return A new ViewHolder that holds a View of the given view type.
6008         * @see #getItemViewType(int)
6009         * @see #onBindViewHolder(ViewHolder, int)
6010         */
6011        public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
6012
6013        /**
6014         * Called by RecyclerView to display the data at the specified position. This method should
6015         * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
6016         * position.
6017         * <p>
6018         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6019         * again if the position of the item changes in the data set unless the item itself is
6020         * invalidated or the new position cannot be determined. For this reason, you should only
6021         * use the <code>position</code> parameter while acquiring the related data item inside
6022         * this method and should not keep a copy of it. If you need the position of an item later
6023         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6024         * have the updated adapter position.
6025         *
6026         * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
6027         * handle efficient partial bind.
6028         *
6029         * @param holder The ViewHolder which should be updated to represent the contents of the
6030         *        item at the given position in the data set.
6031         * @param position The position of the item within the adapter's data set.
6032         */
6033        public abstract void onBindViewHolder(VH holder, int position);
6034
6035        /**
6036         * Called by RecyclerView to display the data at the specified position. This method
6037         * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
6038         * the given position.
6039         * <p>
6040         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6041         * again if the position of the item changes in the data set unless the item itself is
6042         * invalidated or the new position cannot be determined. For this reason, you should only
6043         * use the <code>position</code> parameter while acquiring the related data item inside
6044         * this method and should not keep a copy of it. If you need the position of an item later
6045         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6046         * have the updated adapter position.
6047         * <p>
6048         * Partial bind vs full bind:
6049         * <p>
6050         * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
6051         * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
6052         * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
6053         * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
6054         * Adapter should not assume that the payload passed in notify methods will be received by
6055         * onBindViewHolder().  For example when the view is not attached to the screen, the
6056         * payload in notifyItemChange() will be simply dropped.
6057         *
6058         * @param holder The ViewHolder which should be updated to represent the contents of the
6059         *               item at the given position in the data set.
6060         * @param position The position of the item within the adapter's data set.
6061         * @param payloads A non-null list of merged payloads. Can be empty list if requires full
6062         *                 update.
6063         */
6064        public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
6065            onBindViewHolder(holder, position);
6066        }
6067
6068        /**
6069         * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
6070         * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
6071         *
6072         * @see #onCreateViewHolder(ViewGroup, int)
6073         */
6074        public final VH createViewHolder(ViewGroup parent, int viewType) {
6075            TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
6076            final VH holder = onCreateViewHolder(parent, viewType);
6077            holder.mItemViewType = viewType;
6078            TraceCompat.endSection();
6079            return holder;
6080        }
6081
6082        /**
6083         * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
6084         * {@link ViewHolder} contents with the item at the given position and also sets up some
6085         * private fields to be used by RecyclerView.
6086         *
6087         * @see #onBindViewHolder(ViewHolder, int)
6088         */
6089        public final void bindViewHolder(VH holder, int position) {
6090            holder.mPosition = position;
6091            if (hasStableIds()) {
6092                holder.mItemId = getItemId(position);
6093            }
6094            holder.setFlags(ViewHolder.FLAG_BOUND,
6095                    ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
6096                            | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
6097            TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
6098            onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
6099            holder.clearPayload();
6100            final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
6101            if (layoutParams instanceof RecyclerView.LayoutParams) {
6102                ((LayoutParams) layoutParams).mInsetsDirty = true;
6103            }
6104            TraceCompat.endSection();
6105        }
6106
6107        /**
6108         * Return the view type of the item at <code>position</code> for the purposes
6109         * of view recycling.
6110         *
6111         * <p>The default implementation of this method returns 0, making the assumption of
6112         * a single view type for the adapter. Unlike ListView adapters, types need not
6113         * be contiguous. Consider using id resources to uniquely identify item view types.
6114         *
6115         * @param position position to query
6116         * @return integer value identifying the type of the view needed to represent the item at
6117         *                 <code>position</code>. Type codes need not be contiguous.
6118         */
6119        public int getItemViewType(int position) {
6120            return 0;
6121        }
6122
6123        /**
6124         * Indicates whether each item in the data set can be represented with a unique identifier
6125         * of type {@link java.lang.Long}.
6126         *
6127         * @param hasStableIds Whether items in data set have unique identifiers or not.
6128         * @see #hasStableIds()
6129         * @see #getItemId(int)
6130         */
6131        public void setHasStableIds(boolean hasStableIds) {
6132            if (hasObservers()) {
6133                throw new IllegalStateException("Cannot change whether this adapter has " +
6134                        "stable IDs while the adapter has registered observers.");
6135            }
6136            mHasStableIds = hasStableIds;
6137        }
6138
6139        /**
6140         * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
6141         * would return false this method should return {@link #NO_ID}. The default implementation
6142         * of this method returns {@link #NO_ID}.
6143         *
6144         * @param position Adapter position to query
6145         * @return the stable ID of the item at position
6146         */
6147        public long getItemId(int position) {
6148            return NO_ID;
6149        }
6150
6151        /**
6152         * Returns the total number of items in the data set held by the adapter.
6153         *
6154         * @return The total number of items in this adapter.
6155         */
6156        public abstract int getItemCount();
6157
6158        /**
6159         * Returns true if this adapter publishes a unique <code>long</code> value that can
6160         * act as a key for the item at a given position in the data set. If that item is relocated
6161         * in the data set, the ID returned for that item should be the same.
6162         *
6163         * @return true if this adapter's items have stable IDs
6164         */
6165        public final boolean hasStableIds() {
6166            return mHasStableIds;
6167        }
6168
6169        /**
6170         * Called when a view created by this adapter has been recycled.
6171         *
6172         * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
6173         * needs to be attached to its parent {@link RecyclerView}. This can be because it has
6174         * fallen out of visibility or a set of cached views represented by views still
6175         * attached to the parent RecyclerView. If an item view has large or expensive data
6176         * bound to it such as large bitmaps, this may be a good place to release those
6177         * resources.</p>
6178         * <p>
6179         * RecyclerView calls this method right before clearing ViewHolder's internal data and
6180         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
6181         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
6182         * its adapter position.
6183         *
6184         * @param holder The ViewHolder for the view being recycled
6185         */
6186        public void onViewRecycled(VH holder) {
6187        }
6188
6189        /**
6190         * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
6191         * due to its transient state. Upon receiving this callback, Adapter can clear the
6192         * animation(s) that effect the View's transient state and return <code>true</code> so that
6193         * the View can be recycled. Keep in mind that the View in question is already removed from
6194         * the RecyclerView.
6195         * <p>
6196         * In some cases, it is acceptable to recycle a View although it has transient state. Most
6197         * of the time, this is a case where the transient state will be cleared in
6198         * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
6199         * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
6200         * value of this method to decide whether the View should be recycled or not.
6201         * <p>
6202         * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
6203         * should never receive this callback because RecyclerView keeps those Views as children
6204         * until their animations are complete. This callback is useful when children of the item
6205         * views create animations which may not be easy to implement using an {@link ItemAnimator}.
6206         * <p>
6207         * You should <em>never</em> fix this issue by calling
6208         * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
6209         * <code>holder.itemView.setHasTransientState(true);</code>. Each
6210         * <code>View.setHasTransientState(true)</code> call must be matched by a
6211         * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
6212         * may become inconsistent. You should always prefer to end or cancel animations that are
6213         * triggering the transient state instead of handling it manually.
6214         *
6215         * @param holder The ViewHolder containing the View that could not be recycled due to its
6216         *               transient state.
6217         * @return True if the View should be recycled, false otherwise. Note that if this method
6218         * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
6219         * the View and recycle it regardless. If this method returns <code>false</code>,
6220         * RecyclerView will check the View's transient state again before giving a final decision.
6221         * Default implementation returns false.
6222         */
6223        public boolean onFailedToRecycleView(VH holder) {
6224            return false;
6225        }
6226
6227        /**
6228         * Called when a view created by this adapter has been attached to a window.
6229         *
6230         * <p>This can be used as a reasonable signal that the view is about to be seen
6231         * by the user. If the adapter previously freed any resources in
6232         * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
6233         * those resources should be restored here.</p>
6234         *
6235         * @param holder Holder of the view being attached
6236         */
6237        public void onViewAttachedToWindow(VH holder) {
6238        }
6239
6240        /**
6241         * Called when a view created by this adapter has been detached from its window.
6242         *
6243         * <p>Becoming detached from the window is not necessarily a permanent condition;
6244         * the consumer of an Adapter's views may choose to cache views offscreen while they
6245         * are not visible, attaching and detaching them as appropriate.</p>
6246         *
6247         * @param holder Holder of the view being detached
6248         */
6249        public void onViewDetachedFromWindow(VH holder) {
6250        }
6251
6252        /**
6253         * Returns true if one or more observers are attached to this adapter.
6254         *
6255         * @return true if this adapter has observers
6256         */
6257        public final boolean hasObservers() {
6258            return mObservable.hasObservers();
6259        }
6260
6261        /**
6262         * Register a new observer to listen for data changes.
6263         *
6264         * <p>The adapter may publish a variety of events describing specific changes.
6265         * Not all adapters may support all change types and some may fall back to a generic
6266         * {@link android.support.v7.widget.RecyclerView.AdapterDataObserver#onChanged()
6267         * "something changed"} event if more specific data is not available.</p>
6268         *
6269         * <p>Components registering observers with an adapter are responsible for
6270         * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6271         * unregistering} those observers when finished.</p>
6272         *
6273         * @param observer Observer to register
6274         *
6275         * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6276         */
6277        public void registerAdapterDataObserver(AdapterDataObserver observer) {
6278            mObservable.registerObserver(observer);
6279        }
6280
6281        /**
6282         * Unregister an observer currently listening for data changes.
6283         *
6284         * <p>The unregistered observer will no longer receive events about changes
6285         * to the adapter.</p>
6286         *
6287         * @param observer Observer to unregister
6288         *
6289         * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
6290         */
6291        public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
6292            mObservable.unregisterObserver(observer);
6293        }
6294
6295        /**
6296         * Called by RecyclerView when it starts observing this Adapter.
6297         * <p>
6298         * Keep in mind that same adapter may be observed by multiple RecyclerViews.
6299         *
6300         * @param recyclerView The RecyclerView instance which started observing this adapter.
6301         * @see #onDetachedFromRecyclerView(RecyclerView)
6302         */
6303        public void onAttachedToRecyclerView(RecyclerView recyclerView) {
6304        }
6305
6306        /**
6307         * Called by RecyclerView when it stops observing this Adapter.
6308         *
6309         * @param recyclerView The RecyclerView instance which stopped observing this adapter.
6310         * @see #onAttachedToRecyclerView(RecyclerView)
6311         */
6312        public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
6313        }
6314
6315        /**
6316         * Notify any registered observers that the data set has changed.
6317         *
6318         * <p>There are two different classes of data change events, item changes and structural
6319         * changes. Item changes are when a single item has its data updated but no positional
6320         * changes have occurred. Structural changes are when items are inserted, removed or moved
6321         * within the data set.</p>
6322         *
6323         * <p>This event does not specify what about the data set has changed, forcing
6324         * any observers to assume that all existing items and structure may no longer be valid.
6325         * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
6326         *
6327         * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
6328         * for adapters that report that they have {@link #hasStableIds() stable IDs} when
6329         * this method is used. This can help for the purposes of animation and visual
6330         * object persistence but individual item views will still need to be rebound
6331         * and relaid out.</p>
6332         *
6333         * <p>If you are writing an adapter it will always be more efficient to use the more
6334         * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
6335         * as a last resort.</p>
6336         *
6337         * @see #notifyItemChanged(int)
6338         * @see #notifyItemInserted(int)
6339         * @see #notifyItemRemoved(int)
6340         * @see #notifyItemRangeChanged(int, int)
6341         * @see #notifyItemRangeInserted(int, int)
6342         * @see #notifyItemRangeRemoved(int, int)
6343         */
6344        public final void notifyDataSetChanged() {
6345            mObservable.notifyChanged();
6346        }
6347
6348        /**
6349         * Notify any registered observers that the item at <code>position</code> has changed.
6350         * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
6351         *
6352         * <p>This is an item change event, not a structural change event. It indicates that any
6353         * reflection of the data at <code>position</code> is out of date and should be updated.
6354         * The item at <code>position</code> retains the same identity.</p>
6355         *
6356         * @param position Position of the item that has changed
6357         *
6358         * @see #notifyItemRangeChanged(int, int)
6359         */
6360        public final void notifyItemChanged(int position) {
6361            mObservable.notifyItemRangeChanged(position, 1);
6362        }
6363
6364        /**
6365         * Notify any registered observers that the item at <code>position</code> has changed with an
6366         * optional payload object.
6367         *
6368         * <p>This is an item change event, not a structural change event. It indicates that any
6369         * reflection of the data at <code>position</code> is out of date and should be updated.
6370         * The item at <code>position</code> retains the same identity.
6371         * </p>
6372         *
6373         * <p>
6374         * Client can optionally pass a payload for partial change. These payloads will be merged
6375         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6376         * item is already represented by a ViewHolder and it will be rebound to the same
6377         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6378         * payloads on that item and prevent future payload until
6379         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6380         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6381         * attached, the payload will be simply dropped.
6382         *
6383         * @param position Position of the item that has changed
6384         * @param payload Optional parameter, use null to identify a "full" update
6385         *
6386         * @see #notifyItemRangeChanged(int, int)
6387         */
6388        public final void notifyItemChanged(int position, Object payload) {
6389            mObservable.notifyItemRangeChanged(position, 1, payload);
6390        }
6391
6392        /**
6393         * Notify any registered observers that the <code>itemCount</code> items starting at
6394         * position <code>positionStart</code> have changed.
6395         * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
6396         *
6397         * <p>This is an item change event, not a structural change event. It indicates that
6398         * any reflection of the data in the given position range is out of date and should
6399         * be updated. The items in the given range retain the same identity.</p>
6400         *
6401         * @param positionStart Position of the first item that has changed
6402         * @param itemCount Number of items that have changed
6403         *
6404         * @see #notifyItemChanged(int)
6405         */
6406        public final void notifyItemRangeChanged(int positionStart, int itemCount) {
6407            mObservable.notifyItemRangeChanged(positionStart, itemCount);
6408        }
6409
6410        /**
6411         * Notify any registered observers that the <code>itemCount</code> items starting at
6412         * position <code>positionStart</code> have changed. An optional payload can be
6413         * passed to each changed item.
6414         *
6415         * <p>This is an item change event, not a structural change event. It indicates that any
6416         * reflection of the data in the given position range is out of date and should be updated.
6417         * The items in the given range retain the same identity.
6418         * </p>
6419         *
6420         * <p>
6421         * Client can optionally pass a payload for partial change. These payloads will be merged
6422         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6423         * item is already represented by a ViewHolder and it will be rebound to the same
6424         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6425         * payloads on that item and prevent future payload until
6426         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6427         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6428         * attached, the payload will be simply dropped.
6429         *
6430         * @param positionStart Position of the first item that has changed
6431         * @param itemCount Number of items that have changed
6432         * @param payload  Optional parameter, use null to identify a "full" update
6433         *
6434         * @see #notifyItemChanged(int)
6435         */
6436        public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
6437            mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
6438        }
6439
6440        /**
6441         * Notify any registered observers that the item reflected at <code>position</code>
6442         * has been newly inserted. The item previously at <code>position</code> is now at
6443         * position <code>position + 1</code>.
6444         *
6445         * <p>This is a structural change event. Representations of other existing items in the
6446         * data set are still considered up to date and will not be rebound, though their
6447         * positions may be altered.</p>
6448         *
6449         * @param position Position of the newly inserted item in the data set
6450         *
6451         * @see #notifyItemRangeInserted(int, int)
6452         */
6453        public final void notifyItemInserted(int position) {
6454            mObservable.notifyItemRangeInserted(position, 1);
6455        }
6456
6457        /**
6458         * Notify any registered observers that the item reflected at <code>fromPosition</code>
6459         * has been moved to <code>toPosition</code>.
6460         *
6461         * <p>This is a structural change event. Representations of other existing items in the
6462         * data set are still considered up to date and will not be rebound, though their
6463         * positions may be altered.</p>
6464         *
6465         * @param fromPosition Previous position of the item.
6466         * @param toPosition New position of the item.
6467         */
6468        public final void notifyItemMoved(int fromPosition, int toPosition) {
6469            mObservable.notifyItemMoved(fromPosition, toPosition);
6470        }
6471
6472        /**
6473         * Notify any registered observers that the currently reflected <code>itemCount</code>
6474         * items starting at <code>positionStart</code> have been newly inserted. The items
6475         * previously located at <code>positionStart</code> and beyond can now be found starting
6476         * at position <code>positionStart + itemCount</code>.
6477         *
6478         * <p>This is a structural change event. Representations of other existing items in the
6479         * data set are still considered up to date and will not be rebound, though their positions
6480         * may be altered.</p>
6481         *
6482         * @param positionStart Position of the first item that was inserted
6483         * @param itemCount Number of items inserted
6484         *
6485         * @see #notifyItemInserted(int)
6486         */
6487        public final void notifyItemRangeInserted(int positionStart, int itemCount) {
6488            mObservable.notifyItemRangeInserted(positionStart, itemCount);
6489        }
6490
6491        /**
6492         * Notify any registered observers that the item previously located at <code>position</code>
6493         * has been removed from the data set. The items previously located at and after
6494         * <code>position</code> may now be found at <code>oldPosition - 1</code>.
6495         *
6496         * <p>This is a structural change event. Representations of other existing items in the
6497         * data set are still considered up to date and will not be rebound, though their positions
6498         * may be altered.</p>
6499         *
6500         * @param position Position of the item that has now been removed
6501         *
6502         * @see #notifyItemRangeRemoved(int, int)
6503         */
6504        public final void notifyItemRemoved(int position) {
6505            mObservable.notifyItemRangeRemoved(position, 1);
6506        }
6507
6508        /**
6509         * Notify any registered observers that the <code>itemCount</code> items previously
6510         * located at <code>positionStart</code> have been removed from the data set. The items
6511         * previously located at and after <code>positionStart + itemCount</code> may now be found
6512         * at <code>oldPosition - itemCount</code>.
6513         *
6514         * <p>This is a structural change event. Representations of other existing items in the data
6515         * set are still considered up to date and will not be rebound, though their positions
6516         * may be altered.</p>
6517         *
6518         * @param positionStart Previous position of the first item that was removed
6519         * @param itemCount Number of items removed from the data set
6520         */
6521        public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
6522            mObservable.notifyItemRangeRemoved(positionStart, itemCount);
6523        }
6524    }
6525
6526    void dispatchChildDetached(View child) {
6527        final ViewHolder viewHolder = getChildViewHolderInt(child);
6528        onChildDetachedFromWindow(child);
6529        if (mAdapter != null && viewHolder != null) {
6530            mAdapter.onViewDetachedFromWindow(viewHolder);
6531        }
6532        if (mOnChildAttachStateListeners != null) {
6533            final int cnt = mOnChildAttachStateListeners.size();
6534            for (int i = cnt - 1; i >= 0; i--) {
6535                mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
6536            }
6537        }
6538    }
6539
6540    void dispatchChildAttached(View child) {
6541        final ViewHolder viewHolder = getChildViewHolderInt(child);
6542        onChildAttachedToWindow(child);
6543        if (mAdapter != null && viewHolder != null) {
6544            mAdapter.onViewAttachedToWindow(viewHolder);
6545        }
6546        if (mOnChildAttachStateListeners != null) {
6547            final int cnt = mOnChildAttachStateListeners.size();
6548            for (int i = cnt - 1; i >= 0; i--) {
6549                mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
6550            }
6551        }
6552    }
6553
6554    /**
6555     * A <code>LayoutManager</code> is responsible for measuring and positioning item views
6556     * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
6557     * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
6558     * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
6559     * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
6560     * layout managers are provided for general use.
6561     * <p/>
6562     * If the LayoutManager specifies a default constructor or one with the signature
6563     * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
6564     * instantiate and set the LayoutManager when being inflated. Most used properties can
6565     * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
6566     * a LayoutManager specifies both constructors, the non-default constructor will take
6567     * precedence.
6568     *
6569     */
6570    public static abstract class LayoutManager {
6571        ChildHelper mChildHelper;
6572        RecyclerView mRecyclerView;
6573
6574        @Nullable
6575        SmoothScroller mSmoothScroller;
6576
6577        boolean mRequestedSimpleAnimations = false;
6578
6579        boolean mIsAttachedToWindow = false;
6580
6581        boolean mAutoMeasure = false;
6582
6583        /**
6584         * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
6585         * if the space that will be given to it is already larger than what it has measured before.
6586         */
6587        private boolean mMeasurementCacheEnabled = true;
6588
6589        private boolean mItemPrefetchEnabled = true;
6590
6591        /**
6592         * These measure specs might be the measure specs that were passed into RecyclerView's
6593         * onMeasure method OR fake measure specs created by the RecyclerView.
6594         * For example, when a layout is run, RecyclerView always sets these specs to be
6595         * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
6596         * <p>
6597         * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
6598         * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
6599         * corrupt values. Older platforms have no responsibility to provide a size if they set
6600         * mode to unspecified.
6601         */
6602        private int mWidthMode, mHeightMode;
6603        private int mWidth, mHeight;
6604
6605        void setRecyclerView(RecyclerView recyclerView) {
6606            if (recyclerView == null) {
6607                mRecyclerView = null;
6608                mChildHelper = null;
6609                mWidth = 0;
6610                mHeight = 0;
6611            } else {
6612                mRecyclerView = recyclerView;
6613                mChildHelper = recyclerView.mChildHelper;
6614                mWidth = recyclerView.getWidth();
6615                mHeight = recyclerView.getHeight();
6616            }
6617            mWidthMode = MeasureSpec.EXACTLY;
6618            mHeightMode = MeasureSpec.EXACTLY;
6619        }
6620
6621        void setMeasureSpecs(int wSpec, int hSpec) {
6622            mWidth = MeasureSpec.getSize(wSpec);
6623            mWidthMode = MeasureSpec.getMode(wSpec);
6624            if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
6625                mWidth = 0;
6626            }
6627
6628            mHeight = MeasureSpec.getSize(hSpec);
6629            mHeightMode = MeasureSpec.getMode(hSpec);
6630            if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
6631                mHeight = 0;
6632            }
6633        }
6634
6635        /**
6636         * Called after a layout is calculated during a measure pass when using auto-measure.
6637         * <p>
6638         * It simply traverses all children to calculate a bounding box then calls
6639         * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
6640         * if they need to handle the bounding box differently.
6641         * <p>
6642         * For example, GridLayoutManager override that method to ensure that even if a column is
6643         * empty, the GridLayoutManager still measures wide enough to include it.
6644         *
6645         * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
6646         * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
6647         */
6648        void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
6649            final int count = getChildCount();
6650            if (count == 0) {
6651                mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
6652                return;
6653            }
6654            int minX = Integer.MAX_VALUE;
6655            int minY = Integer.MAX_VALUE;
6656            int maxX = Integer.MIN_VALUE;
6657            int maxY = Integer.MIN_VALUE;
6658
6659            for (int i = 0; i < count; i++) {
6660                View child = getChildAt(i);
6661                LayoutParams lp = (LayoutParams) child.getLayoutParams();
6662                final Rect bounds = mRecyclerView.mTempRect;
6663                getDecoratedBoundsWithMargins(child, bounds);
6664                if (bounds.left < minX) {
6665                    minX = bounds.left;
6666                }
6667                if (bounds.right > maxX) {
6668                    maxX = bounds.right;
6669                }
6670                if (bounds.top < minY) {
6671                    minY = bounds.top;
6672                }
6673                if (bounds.bottom > maxY) {
6674                    maxY = bounds.bottom;
6675                }
6676            }
6677            mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
6678            setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
6679        }
6680
6681        /**
6682         * Sets the measured dimensions from the given bounding box of the children and the
6683         * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
6684         * called after the RecyclerView calls
6685         * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a measurement pass.
6686         * <p>
6687         * This method should call {@link #setMeasuredDimension(int, int)}.
6688         * <p>
6689         * The default implementation adds the RecyclerView's padding to the given bounding box
6690         * then caps the value to be within the given measurement specs.
6691         * <p>
6692         * This method is only called if the LayoutManager opted into the auto measurement API.
6693         *
6694         * @param childrenBounds The bounding box of all children
6695         * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
6696         * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
6697         *
6698         * @see #setAutoMeasureEnabled(boolean)
6699         */
6700        public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
6701            int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
6702            int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
6703            int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
6704            int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
6705            setMeasuredDimension(width, height);
6706        }
6707
6708        /**
6709         * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
6710         */
6711        public void requestLayout() {
6712            if(mRecyclerView != null) {
6713                mRecyclerView.requestLayout();
6714            }
6715        }
6716
6717        /**
6718         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
6719         * {@link IllegalStateException} if it <b>is not</b>.
6720         *
6721         * @param message The message for the exception. Can be null.
6722         * @see #assertNotInLayoutOrScroll(String)
6723         */
6724        public void assertInLayoutOrScroll(String message) {
6725            if (mRecyclerView != null) {
6726                mRecyclerView.assertInLayoutOrScroll(message);
6727            }
6728        }
6729
6730        /**
6731         * Chooses a size from the given specs and parameters that is closest to the desired size
6732         * and also complies with the spec.
6733         *
6734         * @param spec The measureSpec
6735         * @param desired The preferred measurement
6736         * @param min The minimum value
6737         *
6738         * @return A size that fits to the given specs
6739         */
6740        public static int chooseSize(int spec, int desired, int min) {
6741            final int mode = View.MeasureSpec.getMode(spec);
6742            final int size = View.MeasureSpec.getSize(spec);
6743            switch (mode) {
6744                case View.MeasureSpec.EXACTLY:
6745                    return size;
6746                case View.MeasureSpec.AT_MOST:
6747                    return Math.min(size, Math.max(desired, min));
6748                case View.MeasureSpec.UNSPECIFIED:
6749                default:
6750                    return Math.max(desired, min);
6751            }
6752        }
6753
6754        /**
6755         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
6756         * {@link IllegalStateException} if it <b>is</b>.
6757         *
6758         * @param message The message for the exception. Can be null.
6759         * @see #assertInLayoutOrScroll(String)
6760         */
6761        public void assertNotInLayoutOrScroll(String message) {
6762            if (mRecyclerView != null) {
6763                mRecyclerView.assertNotInLayoutOrScroll(message);
6764            }
6765        }
6766
6767        /**
6768         * Defines whether the layout should be measured by the RecyclerView or the LayoutManager
6769         * wants to handle the layout measurements itself.
6770         * <p>
6771         * This method is usually called by the LayoutManager with value {@code true} if it wants
6772         * to support WRAP_CONTENT. If you are using a public LayoutManager but want to customize
6773         * the measurement logic, you can call this method with {@code false} and override
6774         * {@link LayoutManager#onMeasure(int, int)} to implement your custom measurement logic.
6775         * <p>
6776         * AutoMeasure is a convenience mechanism for LayoutManagers to easily wrap their content or
6777         * handle various specs provided by the RecyclerView's parent.
6778         * It works by calling {@link LayoutManager#onLayoutChildren(Recycler, State)} during an
6779         * {@link RecyclerView#onMeasure(int, int)} call, then calculating desired dimensions based
6780         * on children's positions. It does this while supporting all existing animation
6781         * capabilities of the RecyclerView.
6782         * <p>
6783         * AutoMeasure works as follows:
6784         * <ol>
6785         * <li>LayoutManager should call {@code setAutoMeasureEnabled(true)} to enable it. All of
6786         * the framework LayoutManagers use {@code auto-measure}.</li>
6787         * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided specs are
6788         * exact, RecyclerView will only call LayoutManager's {@code onMeasure} and return without
6789         * doing any layout calculation.</li>
6790         * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
6791         * layout process in {@code onMeasure} call. It will process all pending Adapter updates and
6792         * decide whether to run a predictive layout or not. If it decides to do so, it will first
6793         * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
6794         * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
6795         * return the width and height of the RecyclerView as of the last layout calculation.
6796         * <p>
6797         * After handling the predictive case, RecyclerView will call
6798         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
6799         * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
6800         * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
6801         * {@link #getWidth()} and {@link #getWidthMode()}.</li>
6802         * <li>After the layout calculation, RecyclerView sets the measured width & height by
6803         * calculating the bounding box for the children (+ RecyclerView's padding). The
6804         * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
6805         * different values. For instance, GridLayoutManager overrides this value to handle the case
6806         * where if it is vertical and has 3 columns but only 2 items, it should still measure its
6807         * width to fit 3 items, not 2.</li>
6808         * <li>Any following on measure call to the RecyclerView will run
6809         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
6810         * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
6811         * take care of which views are actually added / removed / moved / changed for animations so
6812         * that the LayoutManager should not worry about them and handle each
6813         * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.
6814         * </li>
6815         * <li>When measure is complete and RecyclerView's
6816         * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
6817         * whether it already did layout calculations during the measure pass and if so, it re-uses
6818         * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
6819         * if the last measure spec was different from the final dimensions or adapter contents
6820         * have changed between the measure call and the layout call.</li>
6821         * <li>Finally, animations are calculated and run as usual.</li>
6822         * </ol>
6823         *
6824         * @param enabled <code>True</code> if the Layout should be measured by the
6825         *                             RecyclerView, <code>false</code> if the LayoutManager wants
6826         *                             to measure itself.
6827         *
6828         * @see #setMeasuredDimension(Rect, int, int)
6829         * @see #isAutoMeasureEnabled()
6830         */
6831        public void setAutoMeasureEnabled(boolean enabled) {
6832            mAutoMeasure = enabled;
6833        }
6834
6835        /**
6836         * Returns whether the LayoutManager uses the automatic measurement API or not.
6837         *
6838         * @return <code>True</code> if the LayoutManager is measured by the RecyclerView or
6839         * <code>false</code> if it measures itself.
6840         *
6841         * @see #setAutoMeasureEnabled(boolean)
6842         */
6843        public boolean isAutoMeasureEnabled() {
6844            return mAutoMeasure;
6845        }
6846
6847        /**
6848         * Returns whether this LayoutManager supports automatic item animations.
6849         * A LayoutManager wishing to support item animations should obey certain
6850         * rules as outlined in {@link #onLayoutChildren(Recycler, State)}.
6851         * The default return value is <code>false</code>, so subclasses of LayoutManager
6852         * will not get predictive item animations by default.
6853         *
6854         * <p>Whether item animations are enabled in a RecyclerView is determined both
6855         * by the return value from this method and the
6856         * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
6857         * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
6858         * method returns false, then simple item animations will be enabled, in which
6859         * views that are moving onto or off of the screen are simply faded in/out. If
6860         * the RecyclerView has a non-null ItemAnimator and this method returns true,
6861         * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to
6862         * setup up the information needed to more intelligently predict where appearing
6863         * and disappearing views should be animated from/to.</p>
6864         *
6865         * @return true if predictive item animations should be enabled, false otherwise
6866         */
6867        public boolean supportsPredictiveItemAnimations() {
6868            return false;
6869        }
6870
6871        /**
6872         * Sets whether the LayoutManager should be queried for views outside of
6873         * its viewport while the UI thread is idle between frames.
6874         *
6875         * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
6876         * view system traversals on devices running API 21 or greater. Default value is true.</p>
6877         *
6878         * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
6879         * to RenderThread and the starting up its next frame at the next VSync pulse. By
6880         * prefetching out of window views in this time period, delays from inflation and view
6881         * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
6882         *
6883         * <p>While prefetch is enabled, it will have the side effect of expanding the effective
6884         * size of the View cache to hold prefetched views.</p>
6885         *
6886         * @param enabled <code>True</code> if items should be prefetched in between traversals.
6887         *
6888         * @see #isItemPrefetchEnabled()
6889         */
6890        public final void setItemPrefetchEnabled(boolean enabled) {
6891            if (enabled != mItemPrefetchEnabled) {
6892                mItemPrefetchEnabled = enabled;
6893                if (mRecyclerView != null) {
6894                    mRecyclerView.mRecycler.updateViewCacheSize();
6895                }
6896            }
6897        }
6898
6899        /**
6900         * Sets whether the LayoutManager should be queried for views outside of
6901         * its viewport while the UI thread is idle between frames.
6902         *
6903         * @see #setItemPrefetchEnabled(boolean)
6904         *
6905         * @return true if item prefetch is enabled, false otherwise
6906         */
6907        public final boolean isItemPrefetchEnabled() {
6908            return mItemPrefetchEnabled;
6909        }
6910
6911        int getItemPrefetchCount() { return 0; }
6912
6913        int gatherPrefetchIndices(int dx, int dy, State state, int[] outIndices) {
6914            return 0;
6915        }
6916
6917        void dispatchAttachedToWindow(RecyclerView view) {
6918            mIsAttachedToWindow = true;
6919            onAttachedToWindow(view);
6920        }
6921
6922        void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
6923            mIsAttachedToWindow = false;
6924            onDetachedFromWindow(view, recycler);
6925        }
6926
6927        /**
6928         * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
6929         * to a window.
6930         *
6931         * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
6932         * is attached to window.
6933         */
6934        public boolean isAttachedToWindow() {
6935            return mIsAttachedToWindow;
6936        }
6937
6938        /**
6939         * Causes the Runnable to execute on the next animation time step.
6940         * The runnable will be run on the user interface thread.
6941         * <p>
6942         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
6943         *
6944         * @param action The Runnable that will be executed.
6945         *
6946         * @see #removeCallbacks
6947         */
6948        public void postOnAnimation(Runnable action) {
6949            if (mRecyclerView != null) {
6950                ViewCompat.postOnAnimation(mRecyclerView, action);
6951            }
6952        }
6953
6954        /**
6955         * Removes the specified Runnable from the message queue.
6956         * <p>
6957         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
6958         *
6959         * @param action The Runnable to remove from the message handling queue
6960         *
6961         * @return true if RecyclerView could ask the Handler to remove the Runnable,
6962         *         false otherwise. When the returned value is true, the Runnable
6963         *         may or may not have been actually removed from the message queue
6964         *         (for instance, if the Runnable was not in the queue already.)
6965         *
6966         * @see #postOnAnimation
6967         */
6968        public boolean removeCallbacks(Runnable action) {
6969            if (mRecyclerView != null) {
6970                return mRecyclerView.removeCallbacks(action);
6971            }
6972            return false;
6973        }
6974        /**
6975         * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
6976         * is attached to a window.
6977         * <p>
6978         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
6979         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
6980         * not requested on the RecyclerView while it was detached.
6981         * <p>
6982         * Subclass implementations should always call through to the superclass implementation.
6983         *
6984         * @param view The RecyclerView this LayoutManager is bound to
6985         *
6986         * @see #onDetachedFromWindow(RecyclerView, Recycler)
6987         */
6988        @CallSuper
6989        public void onAttachedToWindow(RecyclerView view) {
6990        }
6991
6992        /**
6993         * @deprecated
6994         * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
6995         */
6996        @Deprecated
6997        public void onDetachedFromWindow(RecyclerView view) {
6998
6999        }
7000
7001        /**
7002         * Called when this LayoutManager is detached from its parent RecyclerView or when
7003         * its parent RecyclerView is detached from its window.
7004         * <p>
7005         * LayoutManager should clear all of its View references as another LayoutManager might be
7006         * assigned to the RecyclerView.
7007         * <p>
7008         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7009         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7010         * not requested on the RecyclerView while it was detached.
7011         * <p>
7012         * If your LayoutManager has View references that it cleans in on-detach, it should also
7013         * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
7014         * RecyclerView is re-attached.
7015         * <p>
7016         * Subclass implementations should always call through to the superclass implementation.
7017         *
7018         * @param view The RecyclerView this LayoutManager is bound to
7019         * @param recycler The recycler to use if you prefer to recycle your children instead of
7020         *                 keeping them around.
7021         *
7022         * @see #onAttachedToWindow(RecyclerView)
7023         */
7024        @CallSuper
7025        public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
7026            onDetachedFromWindow(view);
7027        }
7028
7029        /**
7030         * Check if the RecyclerView is configured to clip child views to its padding.
7031         *
7032         * @return true if this RecyclerView clips children to its padding, false otherwise
7033         */
7034        public boolean getClipToPadding() {
7035            return mRecyclerView != null && mRecyclerView.mClipToPadding;
7036        }
7037
7038        /**
7039         * Lay out all relevant child views from the given adapter.
7040         *
7041         * The LayoutManager is in charge of the behavior of item animations. By default,
7042         * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
7043         * item animations are enabled. This means that add/remove operations on the
7044         * adapter will result in animations to add new or appearing items, removed or
7045         * disappearing items, and moved items. If a LayoutManager returns false from
7046         * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
7047         * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
7048         * RecyclerView will have enough information to run those animations in a simple
7049         * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
7050         * simply fade views in and out, whether they are actually added/removed or whether
7051         * they are moved on or off the screen due to other add/remove operations.
7052         *
7053         * <p>A LayoutManager wanting a better item animation experience, where items can be
7054         * animated onto and off of the screen according to where the items exist when they
7055         * are not on screen, then the LayoutManager should return true from
7056         * {@link #supportsPredictiveItemAnimations()} and add additional logic to
7057         * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
7058         * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
7059         * once as a "pre" layout step to determine where items would have been prior to
7060         * a real layout, and again to do the "real" layout. In the pre-layout phase,
7061         * items will remember their pre-layout positions to allow them to be laid out
7062         * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
7063         * be returned from the scrap to help determine correct placement of other items.
7064         * These removed items should not be added to the child list, but should be used
7065         * to help calculate correct positioning of other views, including views that
7066         * were not previously onscreen (referred to as APPEARING views), but whose
7067         * pre-layout offscreen position can be determined given the extra
7068         * information about the pre-layout removed views.</p>
7069         *
7070         * <p>The second layout pass is the real layout in which only non-removed views
7071         * will be used. The only additional requirement during this pass is, if
7072         * {@link #supportsPredictiveItemAnimations()} returns true, to note which
7073         * views exist in the child list prior to layout and which are not there after
7074         * layout (referred to as DISAPPEARING views), and to position/layout those views
7075         * appropriately, without regard to the actual bounds of the RecyclerView. This allows
7076         * the animation system to know the location to which to animate these disappearing
7077         * views.</p>
7078         *
7079         * <p>The default LayoutManager implementations for RecyclerView handle all of these
7080         * requirements for animations already. Clients of RecyclerView can either use one
7081         * of these layout managers directly or look at their implementations of
7082         * onLayoutChildren() to see how they account for the APPEARING and
7083         * DISAPPEARING views.</p>
7084         *
7085         * @param recycler         Recycler to use for fetching potentially cached views for a
7086         *                         position
7087         * @param state            Transient state of RecyclerView
7088         */
7089        public void onLayoutChildren(Recycler recycler, State state) {
7090            Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
7091        }
7092
7093        /**
7094         * Called after a full layout calculation is finished. The layout calculation may include
7095         * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
7096         * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
7097         * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
7098         * <p>
7099         * This is a good place for the LayoutManager to do some cleanup like pending scroll
7100         * position, saved state etc.
7101         *
7102         * @param state Transient state of RecyclerView
7103         */
7104        public void onLayoutCompleted(State state) {
7105        }
7106
7107        /**
7108         * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
7109         *
7110         * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
7111         * to store extra information specific to the layout. Client code should subclass
7112         * {@link RecyclerView.LayoutParams} for this purpose.</p>
7113         *
7114         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7115         * you must also override
7116         * {@link #checkLayoutParams(LayoutParams)},
7117         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7118         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7119         *
7120         * @return A new LayoutParams for a child view
7121         */
7122        public abstract LayoutParams generateDefaultLayoutParams();
7123
7124        /**
7125         * Determines the validity of the supplied LayoutParams object.
7126         *
7127         * <p>This should check to make sure that the object is of the correct type
7128         * and all values are within acceptable ranges. The default implementation
7129         * returns <code>true</code> for non-null params.</p>
7130         *
7131         * @param lp LayoutParams object to check
7132         * @return true if this LayoutParams object is valid, false otherwise
7133         */
7134        public boolean checkLayoutParams(LayoutParams lp) {
7135            return lp != null;
7136        }
7137
7138        /**
7139         * Create a LayoutParams object suitable for this LayoutManager, copying relevant
7140         * values from the supplied LayoutParams object if possible.
7141         *
7142         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7143         * you must also override
7144         * {@link #checkLayoutParams(LayoutParams)},
7145         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7146         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7147         *
7148         * @param lp Source LayoutParams object to copy values from
7149         * @return a new LayoutParams object
7150         */
7151        public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
7152            if (lp instanceof LayoutParams) {
7153                return new LayoutParams((LayoutParams) lp);
7154            } else if (lp instanceof MarginLayoutParams) {
7155                return new LayoutParams((MarginLayoutParams) lp);
7156            } else {
7157                return new LayoutParams(lp);
7158            }
7159        }
7160
7161        /**
7162         * Create a LayoutParams object suitable for this LayoutManager from
7163         * an inflated layout resource.
7164         *
7165         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7166         * you must also override
7167         * {@link #checkLayoutParams(LayoutParams)},
7168         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7169         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7170         *
7171         * @param c Context for obtaining styled attributes
7172         * @param attrs AttributeSet describing the supplied arguments
7173         * @return a new LayoutParams object
7174         */
7175        public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
7176            return new LayoutParams(c, attrs);
7177        }
7178
7179        /**
7180         * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
7181         * The default implementation does nothing and returns 0.
7182         *
7183         * @param dx            distance to scroll by in pixels. X increases as scroll position
7184         *                      approaches the right.
7185         * @param recycler      Recycler to use for fetching potentially cached views for a
7186         *                      position
7187         * @param state         Transient state of RecyclerView
7188         * @return The actual distance scrolled. The return value will be negative if dx was
7189         * negative and scrolling proceeeded in that direction.
7190         * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
7191         */
7192        public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
7193            return 0;
7194        }
7195
7196        /**
7197         * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
7198         * The default implementation does nothing and returns 0.
7199         *
7200         * @param dy            distance to scroll in pixels. Y increases as scroll position
7201         *                      approaches the bottom.
7202         * @param recycler      Recycler to use for fetching potentially cached views for a
7203         *                      position
7204         * @param state         Transient state of RecyclerView
7205         * @return The actual distance scrolled. The return value will be negative if dy was
7206         * negative and scrolling proceeeded in that direction.
7207         * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
7208         */
7209        public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
7210            return 0;
7211        }
7212
7213        /**
7214         * Query if horizontal scrolling is currently supported. The default implementation
7215         * returns false.
7216         *
7217         * @return True if this LayoutManager can scroll the current contents horizontally
7218         */
7219        public boolean canScrollHorizontally() {
7220            return false;
7221        }
7222
7223        /**
7224         * Query if vertical scrolling is currently supported. The default implementation
7225         * returns false.
7226         *
7227         * @return True if this LayoutManager can scroll the current contents vertically
7228         */
7229        public boolean canScrollVertically() {
7230            return false;
7231        }
7232
7233        /**
7234         * Scroll to the specified adapter position.
7235         *
7236         * Actual position of the item on the screen depends on the LayoutManager implementation.
7237         * @param position Scroll to this adapter position.
7238         */
7239        public void scrollToPosition(int position) {
7240            if (DEBUG) {
7241                Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
7242            }
7243        }
7244
7245        /**
7246         * <p>Smooth scroll to the specified adapter position.</p>
7247         * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
7248         * instance and call {@link #startSmoothScroll(SmoothScroller)}.
7249         * </p>
7250         * @param recyclerView The RecyclerView to which this layout manager is attached
7251         * @param state    Current State of RecyclerView
7252         * @param position Scroll to this adapter position.
7253         */
7254        public void smoothScrollToPosition(RecyclerView recyclerView, State state,
7255                int position) {
7256            Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
7257        }
7258
7259        /**
7260         * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
7261         * <p>Calling this method will cancel any previous smooth scroll request.</p>
7262         * @param smoothScroller Instance which defines how smooth scroll should be animated
7263         */
7264        public void startSmoothScroll(SmoothScroller smoothScroller) {
7265            if (mSmoothScroller != null && smoothScroller != mSmoothScroller
7266                    && mSmoothScroller.isRunning()) {
7267                mSmoothScroller.stop();
7268            }
7269            mSmoothScroller = smoothScroller;
7270            mSmoothScroller.start(mRecyclerView, this);
7271        }
7272
7273        /**
7274         * @return true if RecycylerView is currently in the state of smooth scrolling.
7275         */
7276        public boolean isSmoothScrolling() {
7277            return mSmoothScroller != null && mSmoothScroller.isRunning();
7278        }
7279
7280
7281        /**
7282         * Returns the resolved layout direction for this RecyclerView.
7283         *
7284         * @return {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout
7285         * direction is RTL or returns
7286         * {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction
7287         * is not RTL.
7288         */
7289        public int getLayoutDirection() {
7290            return ViewCompat.getLayoutDirection(mRecyclerView);
7291        }
7292
7293        /**
7294         * Ends all animations on the view created by the {@link ItemAnimator}.
7295         *
7296         * @param view The View for which the animations should be ended.
7297         * @see RecyclerView.ItemAnimator#endAnimations()
7298         */
7299        public void endAnimation(View view) {
7300            if (mRecyclerView.mItemAnimator != null) {
7301                mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
7302            }
7303        }
7304
7305        /**
7306         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
7307         * to the layout that is known to be going away, either because it has been
7308         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
7309         * visible portion of the container but is being laid out in order to inform RecyclerView
7310         * in how to animate the item out of view.
7311         * <p>
7312         * Views added via this method are going to be invisible to LayoutManager after the
7313         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
7314         * or won't be included in {@link #getChildCount()} method.
7315         *
7316         * @param child View to add and then remove with animation.
7317         */
7318        public void addDisappearingView(View child) {
7319            addDisappearingView(child, -1);
7320        }
7321
7322        /**
7323         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
7324         * to the layout that is known to be going away, either because it has been
7325         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
7326         * visible portion of the container but is being laid out in order to inform RecyclerView
7327         * in how to animate the item out of view.
7328         * <p>
7329         * Views added via this method are going to be invisible to LayoutManager after the
7330         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
7331         * or won't be included in {@link #getChildCount()} method.
7332         *
7333         * @param child View to add and then remove with animation.
7334         * @param index Index of the view.
7335         */
7336        public void addDisappearingView(View child, int index) {
7337            addViewInt(child, index, true);
7338        }
7339
7340        /**
7341         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
7342         * use this method to add views obtained from a {@link Recycler} using
7343         * {@link Recycler#getViewForPosition(int)}.
7344         *
7345         * @param child View to add
7346         */
7347        public void addView(View child) {
7348            addView(child, -1);
7349        }
7350
7351        /**
7352         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
7353         * use this method to add views obtained from a {@link Recycler} using
7354         * {@link Recycler#getViewForPosition(int)}.
7355         *
7356         * @param child View to add
7357         * @param index Index to add child at
7358         */
7359        public void addView(View child, int index) {
7360            addViewInt(child, index, false);
7361        }
7362
7363        private void addViewInt(View child, int index, boolean disappearing) {
7364            final ViewHolder holder = getChildViewHolderInt(child);
7365            if (disappearing || holder.isRemoved()) {
7366                // these views will be hidden at the end of the layout pass.
7367                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
7368            } else {
7369                // This may look like unnecessary but may happen if layout manager supports
7370                // predictive layouts and adapter removed then re-added the same item.
7371                // In this case, added version will be visible in the post layout (because add is
7372                // deferred) but RV will still bind it to the same View.
7373                // So if a View re-appears in post layout pass, remove it from disappearing list.
7374                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
7375            }
7376            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
7377            if (holder.wasReturnedFromScrap() || holder.isScrap()) {
7378                if (holder.isScrap()) {
7379                    holder.unScrap();
7380                } else {
7381                    holder.clearReturnedFromScrapFlag();
7382                }
7383                mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
7384                if (DISPATCH_TEMP_DETACH) {
7385                    ViewCompat.dispatchFinishTemporaryDetach(child);
7386                }
7387            } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
7388                // ensure in correct position
7389                int currentIndex = mChildHelper.indexOfChild(child);
7390                if (index == -1) {
7391                    index = mChildHelper.getChildCount();
7392                }
7393                if (currentIndex == -1) {
7394                    throw new IllegalStateException("Added View has RecyclerView as parent but"
7395                            + " view is not a real child. Unfiltered index:"
7396                            + mRecyclerView.indexOfChild(child));
7397                }
7398                if (currentIndex != index) {
7399                    mRecyclerView.mLayout.moveView(currentIndex, index);
7400                }
7401            } else {
7402                mChildHelper.addView(child, index, false);
7403                lp.mInsetsDirty = true;
7404                if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
7405                    mSmoothScroller.onChildAttachedToWindow(child);
7406                }
7407            }
7408            if (lp.mPendingInvalidate) {
7409                if (DEBUG) {
7410                    Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
7411                }
7412                holder.itemView.invalidate();
7413                lp.mPendingInvalidate = false;
7414            }
7415        }
7416
7417        /**
7418         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
7419         * use this method to completely remove a child view that is no longer needed.
7420         * LayoutManagers should strongly consider recycling removed views using
7421         * {@link Recycler#recycleView(android.view.View)}.
7422         *
7423         * @param child View to remove
7424         */
7425        public void removeView(View child) {
7426            mChildHelper.removeView(child);
7427        }
7428
7429        /**
7430         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
7431         * use this method to completely remove a child view that is no longer needed.
7432         * LayoutManagers should strongly consider recycling removed views using
7433         * {@link Recycler#recycleView(android.view.View)}.
7434         *
7435         * @param index Index of the child view to remove
7436         */
7437        public void removeViewAt(int index) {
7438            final View child = getChildAt(index);
7439            if (child != null) {
7440                mChildHelper.removeViewAt(index);
7441            }
7442        }
7443
7444        /**
7445         * Remove all views from the currently attached RecyclerView. This will not recycle
7446         * any of the affected views; the LayoutManager is responsible for doing so if desired.
7447         */
7448        public void removeAllViews() {
7449            // Only remove non-animating views
7450            final int childCount = getChildCount();
7451            for (int i = childCount - 1; i >= 0; i--) {
7452                mChildHelper.removeViewAt(i);
7453            }
7454        }
7455
7456        /**
7457         * Returns offset of the RecyclerView's text baseline from the its top boundary.
7458         *
7459         * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
7460         * there is no baseline.
7461         */
7462        public int getBaseline() {
7463            return -1;
7464        }
7465
7466        /**
7467         * Returns the adapter position of the item represented by the given View. This does not
7468         * contain any adapter changes that might have happened after the last layout.
7469         *
7470         * @param view The view to query
7471         * @return The adapter position of the item which is rendered by this View.
7472         */
7473        public int getPosition(View view) {
7474            return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
7475        }
7476
7477        /**
7478         * Returns the View type defined by the adapter.
7479         *
7480         * @param view The view to query
7481         * @return The type of the view assigned by the adapter.
7482         */
7483        public int getItemViewType(View view) {
7484            return getChildViewHolderInt(view).getItemViewType();
7485        }
7486
7487        /**
7488         * Traverses the ancestors of the given view and returns the item view that contains it
7489         * and also a direct child of the LayoutManager.
7490         * <p>
7491         * Note that this method may return null if the view is a child of the RecyclerView but
7492         * not a child of the LayoutManager (e.g. running a disappear animation).
7493         *
7494         * @param view The view that is a descendant of the LayoutManager.
7495         *
7496         * @return The direct child of the LayoutManager which contains the given view or null if
7497         * the provided view is not a descendant of this LayoutManager.
7498         *
7499         * @see RecyclerView#getChildViewHolder(View)
7500         * @see RecyclerView#findContainingViewHolder(View)
7501         */
7502        @Nullable
7503        public View findContainingItemView(View view) {
7504            if (mRecyclerView == null) {
7505                return null;
7506            }
7507            View found = mRecyclerView.findContainingItemView(view);
7508            if (found == null) {
7509                return null;
7510            }
7511            if (mChildHelper.isHidden(found)) {
7512                return null;
7513            }
7514            return found;
7515        }
7516
7517        /**
7518         * Finds the view which represents the given adapter position.
7519         * <p>
7520         * This method traverses each child since it has no information about child order.
7521         * Override this method to improve performance if your LayoutManager keeps data about
7522         * child views.
7523         * <p>
7524         * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
7525         *
7526         * @param position Position of the item in adapter
7527         * @return The child view that represents the given position or null if the position is not
7528         * laid out
7529         */
7530        public View findViewByPosition(int position) {
7531            final int childCount = getChildCount();
7532            for (int i = 0; i < childCount; i++) {
7533                View child = getChildAt(i);
7534                ViewHolder vh = getChildViewHolderInt(child);
7535                if (vh == null) {
7536                    continue;
7537                }
7538                if (vh.getLayoutPosition() == position && !vh.shouldIgnore() &&
7539                        (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
7540                    return child;
7541                }
7542            }
7543            return null;
7544        }
7545
7546        /**
7547         * Temporarily detach a child view.
7548         *
7549         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
7550         * views currently attached to the RecyclerView. Generally LayoutManager implementations
7551         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
7552         * so that the detached view may be rebound and reused.</p>
7553         *
7554         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
7555         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
7556         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
7557         * before the LayoutManager entry point method called by RecyclerView returns.</p>
7558         *
7559         * @param child Child to detach
7560         */
7561        public void detachView(View child) {
7562            final int ind = mChildHelper.indexOfChild(child);
7563            if (ind >= 0) {
7564                detachViewInternal(ind, child);
7565            }
7566        }
7567
7568        /**
7569         * Temporarily detach a child view.
7570         *
7571         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
7572         * views currently attached to the RecyclerView. Generally LayoutManager implementations
7573         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
7574         * so that the detached view may be rebound and reused.</p>
7575         *
7576         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
7577         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
7578         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
7579         * before the LayoutManager entry point method called by RecyclerView returns.</p>
7580         *
7581         * @param index Index of the child to detach
7582         */
7583        public void detachViewAt(int index) {
7584            detachViewInternal(index, getChildAt(index));
7585        }
7586
7587        private void detachViewInternal(int index, View view) {
7588            if (DISPATCH_TEMP_DETACH) {
7589                ViewCompat.dispatchStartTemporaryDetach(view);
7590            }
7591            mChildHelper.detachViewFromParent(index);
7592        }
7593
7594        /**
7595         * Reattach a previously {@link #detachView(android.view.View) detached} view.
7596         * This method should not be used to reattach views that were previously
7597         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
7598         *
7599         * @param child Child to reattach
7600         * @param index Intended child index for child
7601         * @param lp LayoutParams for child
7602         */
7603        public void attachView(View child, int index, LayoutParams lp) {
7604            ViewHolder vh = getChildViewHolderInt(child);
7605            if (vh.isRemoved()) {
7606                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
7607            } else {
7608                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
7609            }
7610            mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
7611            if (DISPATCH_TEMP_DETACH)  {
7612                ViewCompat.dispatchFinishTemporaryDetach(child);
7613            }
7614        }
7615
7616        /**
7617         * Reattach a previously {@link #detachView(android.view.View) detached} view.
7618         * This method should not be used to reattach views that were previously
7619         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
7620         *
7621         * @param child Child to reattach
7622         * @param index Intended child index for child
7623         */
7624        public void attachView(View child, int index) {
7625            attachView(child, index, (LayoutParams) child.getLayoutParams());
7626        }
7627
7628        /**
7629         * Reattach a previously {@link #detachView(android.view.View) detached} view.
7630         * This method should not be used to reattach views that were previously
7631         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
7632         *
7633         * @param child Child to reattach
7634         */
7635        public void attachView(View child) {
7636            attachView(child, -1);
7637        }
7638
7639        /**
7640         * Finish removing a view that was previously temporarily
7641         * {@link #detachView(android.view.View) detached}.
7642         *
7643         * @param child Detached child to remove
7644         */
7645        public void removeDetachedView(View child) {
7646            mRecyclerView.removeDetachedView(child, false);
7647        }
7648
7649        /**
7650         * Moves a View from one position to another.
7651         *
7652         * @param fromIndex The View's initial index
7653         * @param toIndex The View's target index
7654         */
7655        public void moveView(int fromIndex, int toIndex) {
7656            View view = getChildAt(fromIndex);
7657            if (view == null) {
7658                throw new IllegalArgumentException("Cannot move a child from non-existing index:"
7659                        + fromIndex);
7660            }
7661            detachViewAt(fromIndex);
7662            attachView(view, toIndex);
7663        }
7664
7665        /**
7666         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
7667         *
7668         * <p>Scrapping a view allows it to be rebound and reused to show updated or
7669         * different data.</p>
7670         *
7671         * @param child Child to detach and scrap
7672         * @param recycler Recycler to deposit the new scrap view into
7673         */
7674        public void detachAndScrapView(View child, Recycler recycler) {
7675            int index = mChildHelper.indexOfChild(child);
7676            scrapOrRecycleView(recycler, index, child);
7677        }
7678
7679        /**
7680         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
7681         *
7682         * <p>Scrapping a view allows it to be rebound and reused to show updated or
7683         * different data.</p>
7684         *
7685         * @param index Index of child to detach and scrap
7686         * @param recycler Recycler to deposit the new scrap view into
7687         */
7688        public void detachAndScrapViewAt(int index, Recycler recycler) {
7689            final View child = getChildAt(index);
7690            scrapOrRecycleView(recycler, index, child);
7691        }
7692
7693        /**
7694         * Remove a child view and recycle it using the given Recycler.
7695         *
7696         * @param child Child to remove and recycle
7697         * @param recycler Recycler to use to recycle child
7698         */
7699        public void removeAndRecycleView(View child, Recycler recycler) {
7700            removeView(child);
7701            recycler.recycleView(child);
7702        }
7703
7704        /**
7705         * Remove a child view and recycle it using the given Recycler.
7706         *
7707         * @param index Index of child to remove and recycle
7708         * @param recycler Recycler to use to recycle child
7709         */
7710        public void removeAndRecycleViewAt(int index, Recycler recycler) {
7711            final View view = getChildAt(index);
7712            removeViewAt(index);
7713            recycler.recycleView(view);
7714        }
7715
7716        /**
7717         * Return the current number of child views attached to the parent RecyclerView.
7718         * This does not include child views that were temporarily detached and/or scrapped.
7719         *
7720         * @return Number of attached children
7721         */
7722        public int getChildCount() {
7723            return mChildHelper != null ? mChildHelper.getChildCount() : 0;
7724        }
7725
7726        /**
7727         * Return the child view at the given index
7728         * @param index Index of child to return
7729         * @return Child view at index
7730         */
7731        public View getChildAt(int index) {
7732            return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
7733        }
7734
7735        /**
7736         * Return the width measurement spec mode of the RecyclerView.
7737         * <p>
7738         * This value is set only if the LayoutManager opts into the auto measure api via
7739         * {@link #setAutoMeasureEnabled(boolean)}.
7740         * <p>
7741         * When RecyclerView is running a layout, this value is always set to
7742         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
7743         *
7744         * @return Width measure spec mode.
7745         *
7746         * @see View.MeasureSpec#getMode(int)
7747         * @see View#onMeasure(int, int)
7748         */
7749        public int getWidthMode() {
7750            return mWidthMode;
7751        }
7752
7753        /**
7754         * Return the height measurement spec mode of the RecyclerView.
7755         * <p>
7756         * This value is set only if the LayoutManager opts into the auto measure api via
7757         * {@link #setAutoMeasureEnabled(boolean)}.
7758         * <p>
7759         * When RecyclerView is running a layout, this value is always set to
7760         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
7761         *
7762         * @return Height measure spec mode.
7763         *
7764         * @see View.MeasureSpec#getMode(int)
7765         * @see View#onMeasure(int, int)
7766         */
7767        public int getHeightMode() {
7768            return mHeightMode;
7769        }
7770
7771        /**
7772         * Return the width of the parent RecyclerView
7773         *
7774         * @return Width in pixels
7775         */
7776        public int getWidth() {
7777            return mWidth;
7778        }
7779
7780        /**
7781         * Return the height of the parent RecyclerView
7782         *
7783         * @return Height in pixels
7784         */
7785        public int getHeight() {
7786            return mHeight;
7787        }
7788
7789        /**
7790         * Return the left padding of the parent RecyclerView
7791         *
7792         * @return Padding in pixels
7793         */
7794        public int getPaddingLeft() {
7795            return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
7796        }
7797
7798        /**
7799         * Return the top padding of the parent RecyclerView
7800         *
7801         * @return Padding in pixels
7802         */
7803        public int getPaddingTop() {
7804            return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
7805        }
7806
7807        /**
7808         * Return the right padding of the parent RecyclerView
7809         *
7810         * @return Padding in pixels
7811         */
7812        public int getPaddingRight() {
7813            return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
7814        }
7815
7816        /**
7817         * Return the bottom padding of the parent RecyclerView
7818         *
7819         * @return Padding in pixels
7820         */
7821        public int getPaddingBottom() {
7822            return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
7823        }
7824
7825        /**
7826         * Return the start padding of the parent RecyclerView
7827         *
7828         * @return Padding in pixels
7829         */
7830        public int getPaddingStart() {
7831            return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;
7832        }
7833
7834        /**
7835         * Return the end padding of the parent RecyclerView
7836         *
7837         * @return Padding in pixels
7838         */
7839        public int getPaddingEnd() {
7840            return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;
7841        }
7842
7843        /**
7844         * Returns true if the RecyclerView this LayoutManager is bound to has focus.
7845         *
7846         * @return True if the RecyclerView has focus, false otherwise.
7847         * @see View#isFocused()
7848         */
7849        public boolean isFocused() {
7850            return mRecyclerView != null && mRecyclerView.isFocused();
7851        }
7852
7853        /**
7854         * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
7855         *
7856         * @return true if the RecyclerView has or contains focus
7857         * @see View#hasFocus()
7858         */
7859        public boolean hasFocus() {
7860            return mRecyclerView != null && mRecyclerView.hasFocus();
7861        }
7862
7863        /**
7864         * Returns the item View which has or contains focus.
7865         *
7866         * @return A direct child of RecyclerView which has focus or contains the focused child.
7867         */
7868        public View getFocusedChild() {
7869            if (mRecyclerView == null) {
7870                return null;
7871            }
7872            final View focused = mRecyclerView.getFocusedChild();
7873            if (focused == null || mChildHelper.isHidden(focused)) {
7874                return null;
7875            }
7876            return focused;
7877        }
7878
7879        /**
7880         * Returns the number of items in the adapter bound to the parent RecyclerView.
7881         * <p>
7882         * Note that this number is not necessarily equal to {@link State#getItemCount()}. In
7883         * methods where State is available, you should use {@link State#getItemCount()} instead.
7884         * For more details, check the documentation for {@link State#getItemCount()}.
7885         *
7886         * @return The number of items in the bound adapter
7887         * @see State#getItemCount()
7888         */
7889        public int getItemCount() {
7890            final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
7891            return a != null ? a.getItemCount() : 0;
7892        }
7893
7894        /**
7895         * Offset all child views attached to the parent RecyclerView by dx pixels along
7896         * the horizontal axis.
7897         *
7898         * @param dx Pixels to offset by
7899         */
7900        public void offsetChildrenHorizontal(int dx) {
7901            if (mRecyclerView != null) {
7902                mRecyclerView.offsetChildrenHorizontal(dx);
7903            }
7904        }
7905
7906        /**
7907         * Offset all child views attached to the parent RecyclerView by dy pixels along
7908         * the vertical axis.
7909         *
7910         * @param dy Pixels to offset by
7911         */
7912        public void offsetChildrenVertical(int dy) {
7913            if (mRecyclerView != null) {
7914                mRecyclerView.offsetChildrenVertical(dy);
7915            }
7916        }
7917
7918        /**
7919         * Flags a view so that it will not be scrapped or recycled.
7920         * <p>
7921         * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
7922         * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
7923         * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
7924         * ignore the child.
7925         * <p>
7926         * Before this child can be recycled again, you have to call
7927         * {@link #stopIgnoringView(View)}.
7928         * <p>
7929         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
7930         *
7931         * @param view View to ignore.
7932         * @see #stopIgnoringView(View)
7933         */
7934        public void ignoreView(View view) {
7935            if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
7936                // checking this because calling this method on a recycled or detached view may
7937                // cause loss of state.
7938                throw new IllegalArgumentException("View should be fully attached to be ignored");
7939            }
7940            final ViewHolder vh = getChildViewHolderInt(view);
7941            vh.addFlags(ViewHolder.FLAG_IGNORE);
7942            mRecyclerView.mViewInfoStore.removeViewHolder(vh);
7943        }
7944
7945        /**
7946         * View can be scrapped and recycled again.
7947         * <p>
7948         * Note that calling this method removes all information in the view holder.
7949         * <p>
7950         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
7951         *
7952         * @param view View to ignore.
7953         */
7954        public void stopIgnoringView(View view) {
7955            final ViewHolder vh = getChildViewHolderInt(view);
7956            vh.stopIgnoring();
7957            vh.resetInternal();
7958            vh.addFlags(ViewHolder.FLAG_INVALID);
7959        }
7960
7961        /**
7962         * Temporarily detach and scrap all currently attached child views. Views will be scrapped
7963         * into the given Recycler. The Recycler may prefer to reuse scrap views before
7964         * other views that were previously recycled.
7965         *
7966         * @param recycler Recycler to scrap views into
7967         */
7968        public void detachAndScrapAttachedViews(Recycler recycler) {
7969            final int childCount = getChildCount();
7970            for (int i = childCount - 1; i >= 0; i--) {
7971                final View v = getChildAt(i);
7972                scrapOrRecycleView(recycler, i, v);
7973            }
7974        }
7975
7976        private void scrapOrRecycleView(Recycler recycler, int index, View view) {
7977            final ViewHolder viewHolder = getChildViewHolderInt(view);
7978            if (viewHolder.shouldIgnore()) {
7979                if (DEBUG) {
7980                    Log.d(TAG, "ignoring view " + viewHolder);
7981                }
7982                return;
7983            }
7984            if (viewHolder.isInvalid() && !viewHolder.isRemoved() &&
7985                    !mRecyclerView.mAdapter.hasStableIds()) {
7986                removeViewAt(index);
7987                recycler.recycleViewHolderInternal(viewHolder);
7988            } else {
7989                detachViewAt(index);
7990                recycler.scrapView(view);
7991                mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
7992            }
7993        }
7994
7995        /**
7996         * Recycles the scrapped views.
7997         * <p>
7998         * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
7999         * the expected behavior if scrapped views are used for animations. Otherwise, we need to
8000         * call remove and invalidate RecyclerView to ensure UI update.
8001         *
8002         * @param recycler Recycler
8003         */
8004        void removeAndRecycleScrapInt(Recycler recycler) {
8005            final int scrapCount = recycler.getScrapCount();
8006            // Loop backward, recycler might be changed by removeDetachedView()
8007            for (int i = scrapCount - 1; i >= 0; i--) {
8008                final View scrap = recycler.getScrapViewAt(i);
8009                final ViewHolder vh = getChildViewHolderInt(scrap);
8010                if (vh.shouldIgnore()) {
8011                    continue;
8012                }
8013                // If the scrap view is animating, we need to cancel them first. If we cancel it
8014                // here, ItemAnimator callback may recycle it which will cause double recycling.
8015                // To avoid this, we mark it as not recycleable before calling the item animator.
8016                // Since removeDetachedView calls a user API, a common mistake (ending animations on
8017                // the view) may recycle it too, so we guard it before we call user APIs.
8018                vh.setIsRecyclable(false);
8019                if (vh.isTmpDetached()) {
8020                    mRecyclerView.removeDetachedView(scrap, false);
8021                }
8022                if (mRecyclerView.mItemAnimator != null) {
8023                    mRecyclerView.mItemAnimator.endAnimation(vh);
8024                }
8025                vh.setIsRecyclable(true);
8026                recycler.quickRecycleScrapView(scrap);
8027            }
8028            recycler.clearScrap();
8029            if (scrapCount > 0) {
8030                mRecyclerView.invalidate();
8031            }
8032        }
8033
8034
8035        /**
8036         * Measure a child view using standard measurement policy, taking the padding
8037         * of the parent RecyclerView and any added item decorations into account.
8038         *
8039         * <p>If the RecyclerView can be scrolled in either dimension the caller may
8040         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8041         *
8042         * @param child Child view to measure
8043         * @param widthUsed Width in pixels currently consumed by other views, if relevant
8044         * @param heightUsed Height in pixels currently consumed by other views, if relevant
8045         */
8046        public void measureChild(View child, int widthUsed, int heightUsed) {
8047            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8048
8049            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8050            widthUsed += insets.left + insets.right;
8051            heightUsed += insets.top + insets.bottom;
8052            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8053                    getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
8054                    canScrollHorizontally());
8055            final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8056                    getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
8057                    canScrollVertically());
8058            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8059                child.measure(widthSpec, heightSpec);
8060            }
8061        }
8062
8063        /**
8064         * RecyclerView internally does its own View measurement caching which should help with
8065         * WRAP_CONTENT.
8066         * <p>
8067         * Use this method if the View is already measured once in this layout pass.
8068         */
8069        boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8070            return !mMeasurementCacheEnabled
8071                    || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
8072                    || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
8073        }
8074
8075        // we may consider making this public
8076        /**
8077         * RecyclerView internally does its own View measurement caching which should help with
8078         * WRAP_CONTENT.
8079         * <p>
8080         * Use this method if the View is not yet measured and you need to decide whether to
8081         * measure this View or not.
8082         */
8083        boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8084            return child.isLayoutRequested()
8085                    || !mMeasurementCacheEnabled
8086                    || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
8087                    || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
8088        }
8089
8090        /**
8091         * In addition to the View Framework's measurement cache, RecyclerView uses its own
8092         * additional measurement cache for its children to avoid re-measuring them when not
8093         * necessary. It is on by default but it can be turned off via
8094         * {@link #setMeasurementCacheEnabled(boolean)}.
8095         *
8096         * @return True if measurement cache is enabled, false otherwise.
8097         *
8098         * @see #setMeasurementCacheEnabled(boolean)
8099         */
8100        public boolean isMeasurementCacheEnabled() {
8101            return mMeasurementCacheEnabled;
8102        }
8103
8104        /**
8105         * Sets whether RecyclerView should use its own measurement cache for the children. This is
8106         * a more aggressive cache than the framework uses.
8107         *
8108         * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
8109         *
8110         * @see #isMeasurementCacheEnabled()
8111         */
8112        public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
8113            mMeasurementCacheEnabled = measurementCacheEnabled;
8114        }
8115
8116        private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
8117            final int specMode = MeasureSpec.getMode(spec);
8118            final int specSize = MeasureSpec.getSize(spec);
8119            if (dimension > 0 && childSize != dimension) {
8120                return false;
8121            }
8122            switch (specMode) {
8123                case MeasureSpec.UNSPECIFIED:
8124                    return true;
8125                case MeasureSpec.AT_MOST:
8126                    return specSize >= childSize;
8127                case MeasureSpec.EXACTLY:
8128                    return  specSize == childSize;
8129            }
8130            return false;
8131        }
8132
8133        /**
8134         * Measure a child view using standard measurement policy, taking the padding
8135         * of the parent RecyclerView, any added item decorations and the child margins
8136         * into account.
8137         *
8138         * <p>If the RecyclerView can be scrolled in either dimension the caller may
8139         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8140         *
8141         * @param child Child view to measure
8142         * @param widthUsed Width in pixels currently consumed by other views, if relevant
8143         * @param heightUsed Height in pixels currently consumed by other views, if relevant
8144         */
8145        public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
8146            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8147
8148            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8149            widthUsed += insets.left + insets.right;
8150            heightUsed += insets.top + insets.bottom;
8151
8152            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8153                    getPaddingLeft() + getPaddingRight() +
8154                            lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
8155                    canScrollHorizontally());
8156            final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8157                    getPaddingTop() + getPaddingBottom() +
8158                            lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
8159                    canScrollVertically());
8160            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8161                child.measure(widthSpec, heightSpec);
8162            }
8163        }
8164
8165        /**
8166         * Calculate a MeasureSpec value for measuring a child view in one dimension.
8167         *
8168         * @param parentSize Size of the parent view where the child will be placed
8169         * @param padding Total space currently consumed by other elements of the parent
8170         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
8171         *                       Generally obtained from the child view's LayoutParams
8172         * @param canScroll true if the parent RecyclerView can scroll in this dimension
8173         *
8174         * @return a MeasureSpec value for the child view
8175         * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
8176         */
8177        @Deprecated
8178        public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
8179                boolean canScroll) {
8180            int size = Math.max(0, parentSize - padding);
8181            int resultSize = 0;
8182            int resultMode = 0;
8183            if (canScroll) {
8184                if (childDimension >= 0) {
8185                    resultSize = childDimension;
8186                    resultMode = MeasureSpec.EXACTLY;
8187                } else {
8188                    // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
8189                    // instead using UNSPECIFIED.
8190                    resultSize = 0;
8191                    resultMode = MeasureSpec.UNSPECIFIED;
8192                }
8193            } else {
8194                if (childDimension >= 0) {
8195                    resultSize = childDimension;
8196                    resultMode = MeasureSpec.EXACTLY;
8197                } else if (childDimension == LayoutParams.MATCH_PARENT) {
8198                    resultSize = size;
8199                    // TODO this should be my spec.
8200                    resultMode = MeasureSpec.EXACTLY;
8201                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8202                    resultSize = size;
8203                    resultMode = MeasureSpec.AT_MOST;
8204                }
8205            }
8206            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
8207        }
8208
8209        /**
8210         * Calculate a MeasureSpec value for measuring a child view in one dimension.
8211         *
8212         * @param parentSize Size of the parent view where the child will be placed
8213         * @param parentMode The measurement spec mode of the parent
8214         * @param padding Total space currently consumed by other elements of parent
8215         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
8216         *                       Generally obtained from the child view's LayoutParams
8217         * @param canScroll true if the parent RecyclerView can scroll in this dimension
8218         *
8219         * @return a MeasureSpec value for the child view
8220         */
8221        public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
8222                int childDimension, boolean canScroll) {
8223            int size = Math.max(0, parentSize - padding);
8224            int resultSize = 0;
8225            int resultMode = 0;
8226            if (childDimension >= 0) {
8227                resultSize = childDimension;
8228                resultMode = MeasureSpec.EXACTLY;
8229            } else if (canScroll) {
8230                 if (childDimension == LayoutParams.MATCH_PARENT){
8231                    switch (parentMode) {
8232                        case MeasureSpec.AT_MOST:
8233                        case MeasureSpec.EXACTLY:
8234                            resultSize = size;
8235                            resultMode = parentMode;
8236                            break;
8237                        case MeasureSpec.UNSPECIFIED:
8238                            resultSize = 0;
8239                            resultMode = MeasureSpec.UNSPECIFIED;
8240                            break;
8241                    }
8242                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8243                    resultSize = 0;
8244                    resultMode = MeasureSpec.UNSPECIFIED;
8245                }
8246            } else {
8247                if (childDimension == LayoutParams.MATCH_PARENT) {
8248                    resultSize = size;
8249                    resultMode = parentMode;
8250                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8251                    resultSize = size;
8252                    if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
8253                        resultMode = MeasureSpec.AT_MOST;
8254                    } else {
8255                        resultMode = MeasureSpec.UNSPECIFIED;
8256                    }
8257
8258                }
8259            }
8260            //noinspection WrongConstant
8261            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
8262        }
8263
8264        /**
8265         * Returns the measured width of the given child, plus the additional size of
8266         * any insets applied by {@link ItemDecoration ItemDecorations}.
8267         *
8268         * @param child Child view to query
8269         * @return child's measured width plus <code>ItemDecoration</code> insets
8270         *
8271         * @see View#getMeasuredWidth()
8272         */
8273        public int getDecoratedMeasuredWidth(View child) {
8274            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8275            return child.getMeasuredWidth() + insets.left + insets.right;
8276        }
8277
8278        /**
8279         * Returns the measured height of the given child, plus the additional size of
8280         * any insets applied by {@link ItemDecoration ItemDecorations}.
8281         *
8282         * @param child Child view to query
8283         * @return child's measured height plus <code>ItemDecoration</code> insets
8284         *
8285         * @see View#getMeasuredHeight()
8286         */
8287        public int getDecoratedMeasuredHeight(View child) {
8288            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8289            return child.getMeasuredHeight() + insets.top + insets.bottom;
8290        }
8291
8292        /**
8293         * Lay out the given child view within the RecyclerView using coordinates that
8294         * include any current {@link ItemDecoration ItemDecorations}.
8295         *
8296         * <p>LayoutManagers should prefer working in sizes and coordinates that include
8297         * item decoration insets whenever possible. This allows the LayoutManager to effectively
8298         * ignore decoration insets within measurement and layout code. See the following
8299         * methods:</p>
8300         * <ul>
8301         *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
8302         *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
8303         *     <li>{@link #measureChild(View, int, int)}</li>
8304         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
8305         *     <li>{@link #getDecoratedLeft(View)}</li>
8306         *     <li>{@link #getDecoratedTop(View)}</li>
8307         *     <li>{@link #getDecoratedRight(View)}</li>
8308         *     <li>{@link #getDecoratedBottom(View)}</li>
8309         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
8310         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
8311         * </ul>
8312         *
8313         * @param child Child to lay out
8314         * @param left Left edge, with item decoration insets included
8315         * @param top Top edge, with item decoration insets included
8316         * @param right Right edge, with item decoration insets included
8317         * @param bottom Bottom edge, with item decoration insets included
8318         *
8319         * @see View#layout(int, int, int, int)
8320         * @see #layoutDecoratedWithMargins(View, int, int, int, int)
8321         */
8322        public void layoutDecorated(View child, int left, int top, int right, int bottom) {
8323            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8324            child.layout(left + insets.left, top + insets.top, right - insets.right,
8325                    bottom - insets.bottom);
8326        }
8327
8328        /**
8329         * Lay out the given child view within the RecyclerView using coordinates that
8330         * include any current {@link ItemDecoration ItemDecorations} and margins.
8331         *
8332         * <p>LayoutManagers should prefer working in sizes and coordinates that include
8333         * item decoration insets whenever possible. This allows the LayoutManager to effectively
8334         * ignore decoration insets within measurement and layout code. See the following
8335         * methods:</p>
8336         * <ul>
8337         *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
8338         *     <li>{@link #measureChild(View, int, int)}</li>
8339         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
8340         *     <li>{@link #getDecoratedLeft(View)}</li>
8341         *     <li>{@link #getDecoratedTop(View)}</li>
8342         *     <li>{@link #getDecoratedRight(View)}</li>
8343         *     <li>{@link #getDecoratedBottom(View)}</li>
8344         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
8345         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
8346         * </ul>
8347         *
8348         * @param child Child to lay out
8349         * @param left Left edge, with item decoration insets and left margin included
8350         * @param top Top edge, with item decoration insets and top margin included
8351         * @param right Right edge, with item decoration insets and right margin included
8352         * @param bottom Bottom edge, with item decoration insets and bottom margin included
8353         *
8354         * @see View#layout(int, int, int, int)
8355         * @see #layoutDecorated(View, int, int, int, int)
8356         */
8357        public void layoutDecoratedWithMargins(View child, int left, int top, int right,
8358                int bottom) {
8359            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8360            final Rect insets = lp.mDecorInsets;
8361            child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
8362                    right - insets.right - lp.rightMargin,
8363                    bottom - insets.bottom - lp.bottomMargin);
8364        }
8365
8366        /**
8367         * Calculates the bounding box of the View while taking into account its matrix changes
8368         * (translation, scale etc) with respect to the RecyclerView.
8369         * <p>
8370         * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
8371         * the View's matrix so that the decor offsets also go through the same transformation.
8372         *
8373         * @param child The ItemView whose bounding box should be calculated.
8374         * @param includeDecorInsets True if the decor insets should be included in the bounding box
8375         * @param out The rectangle into which the output will be written.
8376         */
8377        public void getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out) {
8378            if (includeDecorInsets) {
8379                Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8380                out.set(-insets.left, -insets.top,
8381                        child.getWidth() + insets.right, child.getHeight() + insets.bottom);
8382            } else {
8383                out.set(0, 0, child.getWidth(), child.getHeight());
8384            }
8385
8386            if (mRecyclerView != null) {
8387                final Matrix childMatrix = ViewCompat.getMatrix(child);
8388                if (childMatrix != null && !childMatrix.isIdentity()) {
8389                    final RectF tempRectF = mRecyclerView.mTempRectF;
8390                    tempRectF.set(out);
8391                    childMatrix.mapRect(tempRectF);
8392                    out.set(
8393                            (int) Math.floor(tempRectF.left),
8394                            (int) Math.floor(tempRectF.top),
8395                            (int) Math.ceil(tempRectF.right),
8396                            (int) Math.ceil(tempRectF.bottom)
8397                    );
8398                }
8399            }
8400            out.offset(child.getLeft(), child.getTop());
8401        }
8402
8403        /**
8404         * Returns the bounds of the view including its decoration and margins.
8405         *
8406         * @param view The view element to check
8407         * @param outBounds A rect that will receive the bounds of the element including its
8408         *                  decoration and margins.
8409         */
8410        public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
8411            RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
8412        }
8413
8414        /**
8415         * Returns the left edge of the given child view within its parent, offset by any applied
8416         * {@link ItemDecoration ItemDecorations}.
8417         *
8418         * @param child Child to query
8419         * @return Child left edge with offsets applied
8420         * @see #getLeftDecorationWidth(View)
8421         */
8422        public int getDecoratedLeft(View child) {
8423            return child.getLeft() - getLeftDecorationWidth(child);
8424        }
8425
8426        /**
8427         * Returns the top edge of the given child view within its parent, offset by any applied
8428         * {@link ItemDecoration ItemDecorations}.
8429         *
8430         * @param child Child to query
8431         * @return Child top edge with offsets applied
8432         * @see #getTopDecorationHeight(View)
8433         */
8434        public int getDecoratedTop(View child) {
8435            return child.getTop() - getTopDecorationHeight(child);
8436        }
8437
8438        /**
8439         * Returns the right edge of the given child view within its parent, offset by any applied
8440         * {@link ItemDecoration ItemDecorations}.
8441         *
8442         * @param child Child to query
8443         * @return Child right edge with offsets applied
8444         * @see #getRightDecorationWidth(View)
8445         */
8446        public int getDecoratedRight(View child) {
8447            return child.getRight() + getRightDecorationWidth(child);
8448        }
8449
8450        /**
8451         * Returns the bottom edge of the given child view within its parent, offset by any applied
8452         * {@link ItemDecoration ItemDecorations}.
8453         *
8454         * @param child Child to query
8455         * @return Child bottom edge with offsets applied
8456         * @see #getBottomDecorationHeight(View)
8457         */
8458        public int getDecoratedBottom(View child) {
8459            return child.getBottom() + getBottomDecorationHeight(child);
8460        }
8461
8462        /**
8463         * Calculates the item decor insets applied to the given child and updates the provided
8464         * Rect instance with the inset values.
8465         * <ul>
8466         *     <li>The Rect's left is set to the total width of left decorations.</li>
8467         *     <li>The Rect's top is set to the total height of top decorations.</li>
8468         *     <li>The Rect's right is set to the total width of right decorations.</li>
8469         *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
8470         * </ul>
8471         * <p>
8472         * Note that item decorations are automatically calculated when one of the LayoutManager's
8473         * measure child methods is called. If you need to measure the child with custom specs via
8474         * {@link View#measure(int, int)}, you can use this method to get decorations.
8475         *
8476         * @param child The child view whose decorations should be calculated
8477         * @param outRect The Rect to hold result values
8478         */
8479        public void calculateItemDecorationsForChild(View child, Rect outRect) {
8480            if (mRecyclerView == null) {
8481                outRect.set(0, 0, 0, 0);
8482                return;
8483            }
8484            Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8485            outRect.set(insets);
8486        }
8487
8488        /**
8489         * Returns the total height of item decorations applied to child's top.
8490         * <p>
8491         * Note that this value is not updated until the View is measured or
8492         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8493         *
8494         * @param child Child to query
8495         * @return The total height of item decorations applied to the child's top.
8496         * @see #getDecoratedTop(View)
8497         * @see #calculateItemDecorationsForChild(View, Rect)
8498         */
8499        public int getTopDecorationHeight(View child) {
8500            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
8501        }
8502
8503        /**
8504         * Returns the total height of item decorations applied to child's bottom.
8505         * <p>
8506         * Note that this value is not updated until the View is measured or
8507         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8508         *
8509         * @param child Child to query
8510         * @return The total height of item decorations applied to the child's bottom.
8511         * @see #getDecoratedBottom(View)
8512         * @see #calculateItemDecorationsForChild(View, Rect)
8513         */
8514        public int getBottomDecorationHeight(View child) {
8515            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
8516        }
8517
8518        /**
8519         * Returns the total width of item decorations applied to child's left.
8520         * <p>
8521         * Note that this value is not updated until the View is measured or
8522         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8523         *
8524         * @param child Child to query
8525         * @return The total width of item decorations applied to the child's left.
8526         * @see #getDecoratedLeft(View)
8527         * @see #calculateItemDecorationsForChild(View, Rect)
8528         */
8529        public int getLeftDecorationWidth(View child) {
8530            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
8531        }
8532
8533        /**
8534         * Returns the total width of item decorations applied to child's right.
8535         * <p>
8536         * Note that this value is not updated until the View is measured or
8537         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8538         *
8539         * @param child Child to query
8540         * @return The total width of item decorations applied to the child's right.
8541         * @see #getDecoratedRight(View)
8542         * @see #calculateItemDecorationsForChild(View, Rect)
8543         */
8544        public int getRightDecorationWidth(View child) {
8545            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
8546        }
8547
8548        /**
8549         * Called when searching for a focusable view in the given direction has failed
8550         * for the current content of the RecyclerView.
8551         *
8552         * <p>This is the LayoutManager's opportunity to populate views in the given direction
8553         * to fulfill the request if it can. The LayoutManager should attach and return
8554         * the view to be focused. The default implementation returns null.</p>
8555         *
8556         * @param focused   The currently focused view
8557         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
8558         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
8559         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
8560         *                  or 0 for not applicable
8561         * @param recycler  The recycler to use for obtaining views for currently offscreen items
8562         * @param state     Transient state of RecyclerView
8563         * @return The chosen view to be focused
8564         */
8565        @Nullable
8566        public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
8567                State state) {
8568            return null;
8569        }
8570
8571        /**
8572         * This method gives a LayoutManager an opportunity to intercept the initial focus search
8573         * before the default behavior of {@link FocusFinder} is used. If this method returns
8574         * null FocusFinder will attempt to find a focusable child view. If it fails
8575         * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
8576         * will be called to give the LayoutManager an opportunity to add new views for items
8577         * that did not have attached views representing them. The LayoutManager should not add
8578         * or remove views from this method.
8579         *
8580         * @param focused The currently focused view
8581         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
8582         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
8583         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
8584         * @return A descendant view to focus or null to fall back to default behavior.
8585         *         The default implementation returns null.
8586         */
8587        public View onInterceptFocusSearch(View focused, int direction) {
8588            return null;
8589        }
8590
8591        /**
8592         * Called when a child of the RecyclerView wants a particular rectangle to be positioned
8593         * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
8594         * android.graphics.Rect, boolean)} for more details.
8595         *
8596         * <p>The base implementation will attempt to perform a standard programmatic scroll
8597         * to bring the given rect into view, within the padded area of the RecyclerView.</p>
8598         *
8599         * @param child The direct child making the request.
8600         * @param rect  The rectangle in the child's coordinates the child
8601         *              wishes to be on the screen.
8602         * @param immediate True to forbid animated or delayed scrolling,
8603         *                  false otherwise
8604         * @return Whether the group scrolled to handle the operation
8605         */
8606        public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
8607                boolean immediate) {
8608            final int parentLeft = getPaddingLeft();
8609            final int parentTop = getPaddingTop();
8610            final int parentRight = getWidth() - getPaddingRight();
8611            final int parentBottom = getHeight() - getPaddingBottom();
8612            final int childLeft = child.getLeft() + rect.left - child.getScrollX();
8613            final int childTop = child.getTop() + rect.top - child.getScrollY();
8614            final int childRight = childLeft + rect.width();
8615            final int childBottom = childTop + rect.height();
8616
8617            final int offScreenLeft = Math.min(0, childLeft - parentLeft);
8618            final int offScreenTop = Math.min(0, childTop - parentTop);
8619            final int offScreenRight = Math.max(0, childRight - parentRight);
8620            final int offScreenBottom = Math.max(0, childBottom - parentBottom);
8621
8622            // Favor the "start" layout direction over the end when bringing one side or the other
8623            // of a large rect into view. If we decide to bring in end because start is already
8624            // visible, limit the scroll such that start won't go out of bounds.
8625            final int dx;
8626            if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) {
8627                dx = offScreenRight != 0 ? offScreenRight
8628                        : Math.max(offScreenLeft, childRight - parentRight);
8629            } else {
8630                dx = offScreenLeft != 0 ? offScreenLeft
8631                        : Math.min(childLeft - parentLeft, offScreenRight);
8632            }
8633
8634            // Favor bringing the top into view over the bottom. If top is already visible and
8635            // we should scroll to make bottom visible, make sure top does not go out of bounds.
8636            final int dy = offScreenTop != 0 ? offScreenTop
8637                    : Math.min(childTop - parentTop, offScreenBottom);
8638
8639            if (dx != 0 || dy != 0) {
8640                if (immediate) {
8641                    parent.scrollBy(dx, dy);
8642                } else {
8643                    parent.smoothScrollBy(dx, dy);
8644                }
8645                return true;
8646            }
8647            return false;
8648        }
8649
8650        /**
8651         * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
8652         */
8653        @Deprecated
8654        public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
8655            // eat the request if we are in the middle of a scroll or layout
8656            return isSmoothScrolling() || parent.isComputingLayout();
8657        }
8658
8659        /**
8660         * Called when a descendant view of the RecyclerView requests focus.
8661         *
8662         * <p>A LayoutManager wishing to keep focused views aligned in a specific
8663         * portion of the view may implement that behavior in an override of this method.</p>
8664         *
8665         * <p>If the LayoutManager executes different behavior that should override the default
8666         * behavior of scrolling the focused child on screen instead of running alongside it,
8667         * this method should return true.</p>
8668         *
8669         * @param parent  The RecyclerView hosting this LayoutManager
8670         * @param state   Current state of RecyclerView
8671         * @param child   Direct child of the RecyclerView containing the newly focused view
8672         * @param focused The newly focused view. This may be the same view as child or it may be
8673         *                null
8674         * @return true if the default scroll behavior should be suppressed
8675         */
8676        public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
8677                View focused) {
8678            return onRequestChildFocus(parent, child, focused);
8679        }
8680
8681        /**
8682         * Called if the RecyclerView this LayoutManager is bound to has a different adapter set.
8683         * The LayoutManager may use this opportunity to clear caches and configure state such
8684         * that it can relayout appropriately with the new data and potentially new view types.
8685         *
8686         * <p>The default implementation removes all currently attached views.</p>
8687         *
8688         * @param oldAdapter The previous adapter instance. Will be null if there was previously no
8689         *                   adapter.
8690         * @param newAdapter The new adapter instance. Might be null if
8691         *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
8692         */
8693        public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
8694        }
8695
8696        /**
8697         * Called to populate focusable views within the RecyclerView.
8698         *
8699         * <p>The LayoutManager implementation should return <code>true</code> if the default
8700         * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
8701         * suppressed.</p>
8702         *
8703         * <p>The default implementation returns <code>false</code> to trigger RecyclerView
8704         * to fall back to the default ViewGroup behavior.</p>
8705         *
8706         * @param recyclerView The RecyclerView hosting this LayoutManager
8707         * @param views List of output views. This method should add valid focusable views
8708         *              to this list.
8709         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
8710         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
8711         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
8712         * @param focusableMode The type of focusables to be added.
8713         *
8714         * @return true to suppress the default behavior, false to add default focusables after
8715         *         this method returns.
8716         *
8717         * @see #FOCUSABLES_ALL
8718         * @see #FOCUSABLES_TOUCH_MODE
8719         */
8720        public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,
8721                int direction, int focusableMode) {
8722            return false;
8723        }
8724
8725        /**
8726         * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving
8727         * detailed information on what has actually changed.
8728         *
8729         * @param recyclerView
8730         */
8731        public void onItemsChanged(RecyclerView recyclerView) {
8732        }
8733
8734        /**
8735         * Called when items have been added to the adapter. The LayoutManager may choose to
8736         * requestLayout if the inserted items would require refreshing the currently visible set
8737         * of child views. (e.g. currently empty space would be filled by appended items, etc.)
8738         *
8739         * @param recyclerView
8740         * @param positionStart
8741         * @param itemCount
8742         */
8743        public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
8744        }
8745
8746        /**
8747         * Called when items have been removed from the adapter.
8748         *
8749         * @param recyclerView
8750         * @param positionStart
8751         * @param itemCount
8752         */
8753        public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
8754        }
8755
8756        /**
8757         * Called when items have been changed in the adapter.
8758         * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
8759         * instead, then this callback will not be invoked.
8760         *
8761         * @param recyclerView
8762         * @param positionStart
8763         * @param itemCount
8764         */
8765        public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
8766        }
8767
8768        /**
8769         * Called when items have been changed in the adapter and with optional payload.
8770         * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
8771         *
8772         * @param recyclerView
8773         * @param positionStart
8774         * @param itemCount
8775         * @param payload
8776         */
8777        public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
8778                Object payload) {
8779            onItemsUpdated(recyclerView, positionStart, itemCount);
8780        }
8781
8782        /**
8783         * Called when an item is moved withing the adapter.
8784         * <p>
8785         * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
8786         * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
8787         * is called.
8788         *
8789         * @param recyclerView
8790         * @param from
8791         * @param to
8792         * @param itemCount
8793         */
8794        public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
8795
8796        }
8797
8798
8799        /**
8800         * <p>Override this method if you want to support scroll bars.</p>
8801         *
8802         * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
8803         *
8804         * <p>Default implementation returns 0.</p>
8805         *
8806         * @param state Current state of RecyclerView
8807         * @return The horizontal extent of the scrollbar's thumb
8808         * @see RecyclerView#computeHorizontalScrollExtent()
8809         */
8810        public int computeHorizontalScrollExtent(State state) {
8811            return 0;
8812        }
8813
8814        /**
8815         * <p>Override this method if you want to support scroll bars.</p>
8816         *
8817         * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
8818         *
8819         * <p>Default implementation returns 0.</p>
8820         *
8821         * @param state Current State of RecyclerView where you can find total item count
8822         * @return The horizontal offset of the scrollbar's thumb
8823         * @see RecyclerView#computeHorizontalScrollOffset()
8824         */
8825        public int computeHorizontalScrollOffset(State state) {
8826            return 0;
8827        }
8828
8829        /**
8830         * <p>Override this method if you want to support scroll bars.</p>
8831         *
8832         * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
8833         *
8834         * <p>Default implementation returns 0.</p>
8835         *
8836         * @param state Current State of RecyclerView where you can find total item count
8837         * @return The total horizontal range represented by the vertical scrollbar
8838         * @see RecyclerView#computeHorizontalScrollRange()
8839         */
8840        public int computeHorizontalScrollRange(State state) {
8841            return 0;
8842        }
8843
8844        /**
8845         * <p>Override this method if you want to support scroll bars.</p>
8846         *
8847         * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
8848         *
8849         * <p>Default implementation returns 0.</p>
8850         *
8851         * @param state Current state of RecyclerView
8852         * @return The vertical extent of the scrollbar's thumb
8853         * @see RecyclerView#computeVerticalScrollExtent()
8854         */
8855        public int computeVerticalScrollExtent(State state) {
8856            return 0;
8857        }
8858
8859        /**
8860         * <p>Override this method if you want to support scroll bars.</p>
8861         *
8862         * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
8863         *
8864         * <p>Default implementation returns 0.</p>
8865         *
8866         * @param state Current State of RecyclerView where you can find total item count
8867         * @return The vertical offset of the scrollbar's thumb
8868         * @see RecyclerView#computeVerticalScrollOffset()
8869         */
8870        public int computeVerticalScrollOffset(State state) {
8871            return 0;
8872        }
8873
8874        /**
8875         * <p>Override this method if you want to support scroll bars.</p>
8876         *
8877         * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
8878         *
8879         * <p>Default implementation returns 0.</p>
8880         *
8881         * @param state Current State of RecyclerView where you can find total item count
8882         * @return The total vertical range represented by the vertical scrollbar
8883         * @see RecyclerView#computeVerticalScrollRange()
8884         */
8885        public int computeVerticalScrollRange(State state) {
8886            return 0;
8887        }
8888
8889        /**
8890         * Measure the attached RecyclerView. Implementations must call
8891         * {@link #setMeasuredDimension(int, int)} before returning.
8892         *
8893         * <p>The default implementation will handle EXACTLY measurements and respect
8894         * the minimum width and height properties of the host RecyclerView if measured
8895         * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
8896         * will consume all available space.</p>
8897         *
8898         * @param recycler Recycler
8899         * @param state Transient state of RecyclerView
8900         * @param widthSpec Width {@link android.view.View.MeasureSpec}
8901         * @param heightSpec Height {@link android.view.View.MeasureSpec}
8902         */
8903        public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
8904            mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
8905        }
8906
8907        /**
8908         * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
8909         * host RecyclerView.
8910         *
8911         * @param widthSize Measured width
8912         * @param heightSize Measured height
8913         */
8914        public void setMeasuredDimension(int widthSize, int heightSize) {
8915            mRecyclerView.setMeasuredDimension(widthSize, heightSize);
8916        }
8917
8918        /**
8919         * @return The host RecyclerView's {@link View#getMinimumWidth()}
8920         */
8921        public int getMinimumWidth() {
8922            return ViewCompat.getMinimumWidth(mRecyclerView);
8923        }
8924
8925        /**
8926         * @return The host RecyclerView's {@link View#getMinimumHeight()}
8927         */
8928        public int getMinimumHeight() {
8929            return ViewCompat.getMinimumHeight(mRecyclerView);
8930        }
8931        /**
8932         * <p>Called when the LayoutManager should save its state. This is a good time to save your
8933         * scroll position, configuration and anything else that may be required to restore the same
8934         * layout state if the LayoutManager is recreated.</p>
8935         * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
8936         * restore. This will let you share information between your LayoutManagers but it is also
8937         * your responsibility to make sure they use the same parcelable class.</p>
8938         *
8939         * @return Necessary information for LayoutManager to be able to restore its state
8940         */
8941        public Parcelable onSaveInstanceState() {
8942            return null;
8943        }
8944
8945
8946        public void onRestoreInstanceState(Parcelable state) {
8947
8948        }
8949
8950        void stopSmoothScroller() {
8951            if (mSmoothScroller != null) {
8952                mSmoothScroller.stop();
8953            }
8954        }
8955
8956        private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
8957            if (mSmoothScroller == smoothScroller) {
8958                mSmoothScroller = null;
8959            }
8960        }
8961
8962        /**
8963         * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
8964         *
8965         * @param state The new scroll state for RecyclerView
8966         */
8967        public void onScrollStateChanged(int state) {
8968        }
8969
8970        /**
8971         * Removes all views and recycles them using the given recycler.
8972         * <p>
8973         * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
8974         * <p>
8975         * If a View is marked as "ignored", it is not removed nor recycled.
8976         *
8977         * @param recycler Recycler to use to recycle children
8978         * @see #removeAndRecycleView(View, Recycler)
8979         * @see #removeAndRecycleViewAt(int, Recycler)
8980         * @see #ignoreView(View)
8981         */
8982        public void removeAndRecycleAllViews(Recycler recycler) {
8983            for (int i = getChildCount() - 1; i >= 0; i--) {
8984                final View view = getChildAt(i);
8985                if (!getChildViewHolderInt(view).shouldIgnore()) {
8986                    removeAndRecycleViewAt(i, recycler);
8987                }
8988            }
8989        }
8990
8991        // called by accessibility delegate
8992        void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
8993            onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
8994        }
8995
8996        /**
8997         * Called by the AccessibilityDelegate when the information about the current layout should
8998         * be populated.
8999         * <p>
9000         * Default implementation adds a {@link
9001         * android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}.
9002         * <p>
9003         * You should override
9004         * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
9005         * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
9006         * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
9007         * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
9008         * more accurate accessibility information.
9009         *
9010         * @param recycler The Recycler that can be used to convert view positions into adapter
9011         *                 positions
9012         * @param state    The current state of RecyclerView
9013         * @param info     The info that should be filled by the LayoutManager
9014         * @see View#onInitializeAccessibilityNodeInfo(
9015         *android.view.accessibility.AccessibilityNodeInfo)
9016         * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9017         * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9018         * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
9019         * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9020         */
9021        public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
9022                AccessibilityNodeInfoCompat info) {
9023            if (ViewCompat.canScrollVertically(mRecyclerView, -1) ||
9024                    ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
9025                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
9026                info.setScrollable(true);
9027            }
9028            if (ViewCompat.canScrollVertically(mRecyclerView, 1) ||
9029                    ViewCompat.canScrollHorizontally(mRecyclerView, 1)) {
9030                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
9031                info.setScrollable(true);
9032            }
9033            final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo
9034                    = AccessibilityNodeInfoCompat.CollectionInfoCompat
9035                    .obtain(getRowCountForAccessibility(recycler, state),
9036                            getColumnCountForAccessibility(recycler, state),
9037                            isLayoutHierarchical(recycler, state),
9038                            getSelectionModeForAccessibility(recycler, state));
9039            info.setCollectionInfo(collectionInfo);
9040        }
9041
9042        // called by accessibility delegate
9043        public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
9044            onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
9045        }
9046
9047        /**
9048         * Called by the accessibility delegate to initialize an accessibility event.
9049         * <p>
9050         * Default implementation adds item count and scroll information to the event.
9051         *
9052         * @param recycler The Recycler that can be used to convert view positions into adapter
9053         *                 positions
9054         * @param state    The current state of RecyclerView
9055         * @param event    The event instance to initialize
9056         * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
9057         */
9058        public void onInitializeAccessibilityEvent(Recycler recycler, State state,
9059                AccessibilityEvent event) {
9060            final AccessibilityRecordCompat record = AccessibilityEventCompat
9061                    .asRecord(event);
9062            if (mRecyclerView == null || record == null) {
9063                return;
9064            }
9065            record.setScrollable(ViewCompat.canScrollVertically(mRecyclerView, 1)
9066                    || ViewCompat.canScrollVertically(mRecyclerView, -1)
9067                    || ViewCompat.canScrollHorizontally(mRecyclerView, -1)
9068                    || ViewCompat.canScrollHorizontally(mRecyclerView, 1));
9069
9070            if (mRecyclerView.mAdapter != null) {
9071                record.setItemCount(mRecyclerView.mAdapter.getItemCount());
9072            }
9073        }
9074
9075        // called by accessibility delegate
9076        void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) {
9077            final ViewHolder vh = getChildViewHolderInt(host);
9078            // avoid trying to create accessibility node info for removed children
9079            if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
9080                onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
9081                        mRecyclerView.mState, host, info);
9082            }
9083        }
9084
9085        /**
9086         * Called by the AccessibilityDelegate when the accessibility information for a specific
9087         * item should be populated.
9088         * <p>
9089         * Default implementation adds basic positioning information about the item.
9090         *
9091         * @param recycler The Recycler that can be used to convert view positions into adapter
9092         *                 positions
9093         * @param state    The current state of RecyclerView
9094         * @param host     The child for which accessibility node info should be populated
9095         * @param info     The info to fill out about the item
9096         * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
9097         * android.view.accessibility.AccessibilityNodeInfo)
9098         */
9099        public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
9100                View host, AccessibilityNodeInfoCompat info) {
9101            int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
9102            int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
9103            final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo
9104                    = AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
9105                    columnIndexGuess, 1, false, false);
9106            info.setCollectionItemInfo(itemInfo);
9107        }
9108
9109        /**
9110         * A LayoutManager can call this method to force RecyclerView to run simple animations in
9111         * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
9112         * change).
9113         * <p>
9114         * Note that, calling this method will not guarantee that RecyclerView will run animations
9115         * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
9116         * not run any animations but will still clear this flag after the layout is complete.
9117         *
9118         */
9119        public void requestSimpleAnimationsInNextLayout() {
9120            mRequestedSimpleAnimations = true;
9121        }
9122
9123        /**
9124         * Returns the selection mode for accessibility. Should be
9125         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE},
9126         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or
9127         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}.
9128         * <p>
9129         * Default implementation returns
9130         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
9131         *
9132         * @param recycler The Recycler that can be used to convert view positions into adapter
9133         *                 positions
9134         * @param state    The current state of RecyclerView
9135         * @return Selection mode for accessibility. Default implementation returns
9136         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
9137         */
9138        public int getSelectionModeForAccessibility(Recycler recycler, State state) {
9139            return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
9140        }
9141
9142        /**
9143         * Returns the number of rows for accessibility.
9144         * <p>
9145         * Default implementation returns the number of items in the adapter if LayoutManager
9146         * supports vertical scrolling or 1 if LayoutManager does not support vertical
9147         * scrolling.
9148         *
9149         * @param recycler The Recycler that can be used to convert view positions into adapter
9150         *                 positions
9151         * @param state    The current state of RecyclerView
9152         * @return The number of rows in LayoutManager for accessibility.
9153         */
9154        public int getRowCountForAccessibility(Recycler recycler, State state) {
9155            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
9156                return 1;
9157            }
9158            return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
9159        }
9160
9161        /**
9162         * Returns the number of columns for accessibility.
9163         * <p>
9164         * Default implementation returns the number of items in the adapter if LayoutManager
9165         * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
9166         * scrolling.
9167         *
9168         * @param recycler The Recycler that can be used to convert view positions into adapter
9169         *                 positions
9170         * @param state    The current state of RecyclerView
9171         * @return The number of rows in LayoutManager for accessibility.
9172         */
9173        public int getColumnCountForAccessibility(Recycler recycler, State state) {
9174            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
9175                return 1;
9176            }
9177            return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
9178        }
9179
9180        /**
9181         * Returns whether layout is hierarchical or not to be used for accessibility.
9182         * <p>
9183         * Default implementation returns false.
9184         *
9185         * @param recycler The Recycler that can be used to convert view positions into adapter
9186         *                 positions
9187         * @param state    The current state of RecyclerView
9188         * @return True if layout is hierarchical.
9189         */
9190        public boolean isLayoutHierarchical(Recycler recycler, State state) {
9191            return false;
9192        }
9193
9194        // called by accessibility delegate
9195        boolean performAccessibilityAction(int action, Bundle args) {
9196            return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
9197                    action, args);
9198        }
9199
9200        /**
9201         * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
9202         *
9203         * @param recycler  The Recycler that can be used to convert view positions into adapter
9204         *                  positions
9205         * @param state     The current state of RecyclerView
9206         * @param action    The action to perform
9207         * @param args      Optional action arguments
9208         * @see View#performAccessibilityAction(int, android.os.Bundle)
9209         */
9210        public boolean performAccessibilityAction(Recycler recycler, State state, int action,
9211                Bundle args) {
9212            if (mRecyclerView == null) {
9213                return false;
9214            }
9215            int vScroll = 0, hScroll = 0;
9216            switch (action) {
9217                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
9218                    if (ViewCompat.canScrollVertically(mRecyclerView, -1)) {
9219                        vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
9220                    }
9221                    if (ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
9222                        hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
9223                    }
9224                    break;
9225                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
9226                    if (ViewCompat.canScrollVertically(mRecyclerView, 1)) {
9227                        vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
9228                    }
9229                    if (ViewCompat.canScrollHorizontally(mRecyclerView, 1)) {
9230                        hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
9231                    }
9232                    break;
9233            }
9234            if (vScroll == 0 && hScroll == 0) {
9235                return false;
9236            }
9237            mRecyclerView.scrollBy(hScroll, vScroll);
9238            return true;
9239        }
9240
9241        // called by accessibility delegate
9242        boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
9243            return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
9244                    view, action, args);
9245        }
9246
9247        /**
9248         * Called by AccessibilityDelegate when an accessibility action is requested on one of the
9249         * children of LayoutManager.
9250         * <p>
9251         * Default implementation does not do anything.
9252         *
9253         * @param recycler The Recycler that can be used to convert view positions into adapter
9254         *                 positions
9255         * @param state    The current state of RecyclerView
9256         * @param view     The child view on which the action is performed
9257         * @param action   The action to perform
9258         * @param args     Optional action arguments
9259         * @return true if action is handled
9260         * @see View#performAccessibilityAction(int, android.os.Bundle)
9261         */
9262        public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
9263                int action, Bundle args) {
9264            return false;
9265        }
9266
9267        /**
9268         * Parse the xml attributes to get the most common properties used by layout managers.
9269         *
9270         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
9271         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
9272         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
9273         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
9274         *
9275         * @return an object containing the properties as specified in the attrs.
9276         */
9277        public static Properties getProperties(Context context, AttributeSet attrs,
9278                int defStyleAttr, int defStyleRes) {
9279            Properties properties = new Properties();
9280            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
9281                    defStyleAttr, defStyleRes);
9282            properties.orientation = a.getInt(R.styleable.RecyclerView_android_orientation, VERTICAL);
9283            properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
9284            properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
9285            properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
9286            a.recycle();
9287            return properties;
9288        }
9289
9290        void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
9291            setMeasureSpecs(
9292                    MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
9293                    MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
9294            );
9295        }
9296
9297        /**
9298         * Internal API to allow LayoutManagers to be measured twice.
9299         * <p>
9300         * This is not public because LayoutManagers should be able to handle their layouts in one
9301         * pass but it is very convenient to make existing LayoutManagers support wrapping content
9302         * when both orientations are undefined.
9303         * <p>
9304         * This API will be removed after default LayoutManagers properly implement wrap content in
9305         * non-scroll orientation.
9306         */
9307        boolean shouldMeasureTwice() {
9308            return false;
9309        }
9310
9311        boolean hasFlexibleChildInBothOrientations() {
9312            final int childCount = getChildCount();
9313            for (int i = 0; i < childCount; i++) {
9314                final View child = getChildAt(i);
9315                final ViewGroup.LayoutParams lp = child.getLayoutParams();
9316                if (lp.width < 0 && lp.height < 0) {
9317                    return true;
9318                }
9319            }
9320            return false;
9321        }
9322
9323        /**
9324         * Some general properties that a LayoutManager may want to use.
9325         */
9326        public static class Properties {
9327            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation */
9328            public int orientation;
9329            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount */
9330            public int spanCount;
9331            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout */
9332            public boolean reverseLayout;
9333            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd */
9334            public boolean stackFromEnd;
9335        }
9336    }
9337
9338    /**
9339     * An ItemDecoration allows the application to add a special drawing and layout offset
9340     * to specific item views from the adapter's data set. This can be useful for drawing dividers
9341     * between items, highlights, visual grouping boundaries and more.
9342     *
9343     * <p>All ItemDecorations are drawn in the order they were added, before the item
9344     * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
9345     * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
9346     * RecyclerView.State)}.</p>
9347     */
9348    public static abstract class ItemDecoration {
9349        /**
9350         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
9351         * Any content drawn by this method will be drawn before the item views are drawn,
9352         * and will thus appear underneath the views.
9353         *
9354         * @param c Canvas to draw into
9355         * @param parent RecyclerView this ItemDecoration is drawing into
9356         * @param state The current state of RecyclerView
9357         */
9358        public void onDraw(Canvas c, RecyclerView parent, State state) {
9359            onDraw(c, parent);
9360        }
9361
9362        /**
9363         * @deprecated
9364         * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
9365         */
9366        @Deprecated
9367        public void onDraw(Canvas c, RecyclerView parent) {
9368        }
9369
9370        /**
9371         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
9372         * Any content drawn by this method will be drawn after the item views are drawn
9373         * and will thus appear over the views.
9374         *
9375         * @param c Canvas to draw into
9376         * @param parent RecyclerView this ItemDecoration is drawing into
9377         * @param state The current state of RecyclerView.
9378         */
9379        public void onDrawOver(Canvas c, RecyclerView parent, State state) {
9380            onDrawOver(c, parent);
9381        }
9382
9383        /**
9384         * @deprecated
9385         * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
9386         */
9387        @Deprecated
9388        public void onDrawOver(Canvas c, RecyclerView parent) {
9389        }
9390
9391
9392        /**
9393         * @deprecated
9394         * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
9395         */
9396        @Deprecated
9397        public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
9398            outRect.set(0, 0, 0, 0);
9399        }
9400
9401        /**
9402         * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
9403         * the number of pixels that the item view should be inset by, similar to padding or margin.
9404         * The default implementation sets the bounds of outRect to 0 and returns.
9405         *
9406         * <p>
9407         * If this ItemDecoration does not affect the positioning of item views, it should set
9408         * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
9409         * before returning.
9410         *
9411         * <p>
9412         * If you need to access Adapter for additional data, you can call
9413         * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
9414         * View.
9415         *
9416         * @param outRect Rect to receive the output.
9417         * @param view    The child view to decorate
9418         * @param parent  RecyclerView this ItemDecoration is decorating
9419         * @param state   The current state of RecyclerView.
9420         */
9421        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
9422            getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
9423                    parent);
9424        }
9425    }
9426
9427    /**
9428     * An OnItemTouchListener allows the application to intercept touch events in progress at the
9429     * view hierarchy level of the RecyclerView before those touch events are considered for
9430     * RecyclerView's own scrolling behavior.
9431     *
9432     * <p>This can be useful for applications that wish to implement various forms of gestural
9433     * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
9434     * a touch interaction already in progress even if the RecyclerView is already handling that
9435     * gesture stream itself for the purposes of scrolling.</p>
9436     *
9437     * @see SimpleOnItemTouchListener
9438     */
9439    public static interface OnItemTouchListener {
9440        /**
9441         * Silently observe and/or take over touch events sent to the RecyclerView
9442         * before they are handled by either the RecyclerView itself or its child views.
9443         *
9444         * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
9445         * in the order in which each listener was added, before any other touch processing
9446         * by the RecyclerView itself or child views occurs.</p>
9447         *
9448         * @param e MotionEvent describing the touch event. All coordinates are in
9449         *          the RecyclerView's coordinate system.
9450         * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
9451         *         to continue with the current behavior and continue observing future events in
9452         *         the gesture.
9453         */
9454        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
9455
9456        /**
9457         * Process a touch event as part of a gesture that was claimed by returning true from
9458         * a previous call to {@link #onInterceptTouchEvent}.
9459         *
9460         * @param e MotionEvent describing the touch event. All coordinates are in
9461         *          the RecyclerView's coordinate system.
9462         */
9463        public void onTouchEvent(RecyclerView rv, MotionEvent e);
9464
9465        /**
9466         * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
9467         * intercept touch events with
9468         * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
9469         *
9470         * @param disallowIntercept True if the child does not want the parent to
9471         *            intercept touch events.
9472         * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
9473         */
9474        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
9475    }
9476
9477    /**
9478     * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies and
9479     * default return values.
9480     * <p>
9481     * You may prefer to extend this class if you don't need to override all methods. Another
9482     * benefit of using this class is future compatibility. As the interface may change, we'll
9483     * always provide a default implementation on this class so that your code won't break when
9484     * you update to a new version of the support library.
9485     */
9486    public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
9487        @Override
9488        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
9489            return false;
9490        }
9491
9492        @Override
9493        public void onTouchEvent(RecyclerView rv, MotionEvent e) {
9494        }
9495
9496        @Override
9497        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
9498        }
9499    }
9500
9501
9502    /**
9503     * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
9504     * has occurred on that RecyclerView.
9505     * <p>
9506     * @see RecyclerView#addOnScrollListener(OnScrollListener)
9507     * @see RecyclerView#clearOnChildAttachStateChangeListeners()
9508     *
9509     */
9510    public abstract static class OnScrollListener {
9511        /**
9512         * Callback method to be invoked when RecyclerView's scroll state changes.
9513         *
9514         * @param recyclerView The RecyclerView whose scroll state has changed.
9515         * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
9516         *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
9517         */
9518        public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
9519
9520        /**
9521         * Callback method to be invoked when the RecyclerView has been scrolled. This will be
9522         * called after the scroll has completed.
9523         * <p>
9524         * This callback will also be called if visible item range changes after a layout
9525         * calculation. In that case, dx and dy will be 0.
9526         *
9527         * @param recyclerView The RecyclerView which scrolled.
9528         * @param dx The amount of horizontal scroll.
9529         * @param dy The amount of vertical scroll.
9530         */
9531        public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
9532    }
9533
9534    /**
9535     * A RecyclerListener can be set on a RecyclerView to receive messages whenever
9536     * a view is recycled.
9537     *
9538     * @see RecyclerView#setRecyclerListener(RecyclerListener)
9539     */
9540    public interface RecyclerListener {
9541
9542        /**
9543         * This method is called whenever the view in the ViewHolder is recycled.
9544         *
9545         * RecyclerView calls this method right before clearing ViewHolder's internal data and
9546         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
9547         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
9548         * its adapter position.
9549         *
9550         * @param holder The ViewHolder containing the view that was recycled
9551         */
9552        public void onViewRecycled(ViewHolder holder);
9553    }
9554
9555    /**
9556     * A Listener interface that can be attached to a RecylcerView to get notified
9557     * whenever a ViewHolder is attached to or detached from RecyclerView.
9558     */
9559    public interface OnChildAttachStateChangeListener {
9560
9561        /**
9562         * Called when a view is attached to the RecyclerView.
9563         *
9564         * @param view The View which is attached to the RecyclerView
9565         */
9566        public void onChildViewAttachedToWindow(View view);
9567
9568        /**
9569         * Called when a view is detached from RecyclerView.
9570         *
9571         * @param view The View which is being detached from the RecyclerView
9572         */
9573        public void onChildViewDetachedFromWindow(View view);
9574    }
9575
9576    /**
9577     * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
9578     *
9579     * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
9580     * potentially expensive {@link View#findViewById(int)} results.</p>
9581     *
9582     * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
9583     * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
9584     * their own custom ViewHolder implementations to store data that makes binding view contents
9585     * easier. Implementations should assume that individual item views will hold strong references
9586     * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
9587     * strong references to extra off-screen item views for caching purposes</p>
9588     */
9589    public static abstract class ViewHolder {
9590        public final View itemView;
9591        int mPosition = NO_POSITION;
9592        int mOldPosition = NO_POSITION;
9593        long mItemId = NO_ID;
9594        int mItemViewType = INVALID_TYPE;
9595        int mPreLayoutPosition = NO_POSITION;
9596
9597        // The item that this holder is shadowing during an item change event/animation
9598        ViewHolder mShadowedHolder = null;
9599        // The item that is shadowing this holder during an item change event/animation
9600        ViewHolder mShadowingHolder = null;
9601
9602        /**
9603         * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
9604         * are all valid.
9605         */
9606        static final int FLAG_BOUND = 1 << 0;
9607
9608        /**
9609         * The data this ViewHolder's view reflects is stale and needs to be rebound
9610         * by the adapter. mPosition and mItemId are consistent.
9611         */
9612        static final int FLAG_UPDATE = 1 << 1;
9613
9614        /**
9615         * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
9616         * are not to be trusted and may no longer match the item view type.
9617         * This ViewHolder must be fully rebound to different data.
9618         */
9619        static final int FLAG_INVALID = 1 << 2;
9620
9621        /**
9622         * This ViewHolder points at data that represents an item previously removed from the
9623         * data set. Its view may still be used for things like outgoing animations.
9624         */
9625        static final int FLAG_REMOVED = 1 << 3;
9626
9627        /**
9628         * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
9629         * and is intended to keep views around during animations.
9630         */
9631        static final int FLAG_NOT_RECYCLABLE = 1 << 4;
9632
9633        /**
9634         * This ViewHolder is returned from scrap which means we are expecting an addView call
9635         * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
9636         * the end of the layout pass and then recycled by RecyclerView if it is not added back to
9637         * the RecyclerView.
9638         */
9639        static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
9640
9641        /**
9642         * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
9643         * it unless LayoutManager is replaced.
9644         * It is still fully visible to the LayoutManager.
9645         */
9646        static final int FLAG_IGNORE = 1 << 7;
9647
9648        /**
9649         * When the View is detached form the parent, we set this flag so that we can take correct
9650         * action when we need to remove it or add it back.
9651         */
9652        static final int FLAG_TMP_DETACHED = 1 << 8;
9653
9654        /**
9655         * Set when we can no longer determine the adapter position of this ViewHolder until it is
9656         * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
9657         * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
9658         * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
9659         * re-calculated.
9660         */
9661        static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
9662
9663        /**
9664         * Set when a addChangePayload(null) is called
9665         */
9666        static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
9667
9668        /**
9669         * Used by ItemAnimator when a ViewHolder's position changes
9670         */
9671        static final int FLAG_MOVED = 1 << 11;
9672
9673        /**
9674         * Used by ItemAnimator when a ViewHolder appears in pre-layout
9675         */
9676        static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
9677
9678        static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
9679
9680        /**
9681         * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
9682         * hidden list (as if it was scrap) without being recycled in between.
9683         *
9684         * When a ViewHolder is hidden, there are 2 paths it can be re-used:
9685         *   a) Animation ends, view is recycled and used from the recycle pool.
9686         *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
9687         *
9688         * This flag is used to represent "case b" where the ViewHolder is reused without being
9689         * recycled (thus "bounced" from the hidden list). This state requires special handling
9690         * because the ViewHolder must be added to pre layout maps for animations as if it was
9691         * already there.
9692         */
9693        static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
9694
9695        private int mFlags;
9696
9697        private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
9698
9699        List<Object> mPayloads = null;
9700        List<Object> mUnmodifiedPayloads = null;
9701
9702        private int mIsRecyclableCount = 0;
9703
9704        // If non-null, view is currently considered scrap and may be reused for other data by the
9705        // scrap container.
9706        private Recycler mScrapContainer = null;
9707        // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
9708        private boolean mInChangeScrap = false;
9709
9710        // Saves isImportantForAccessibility value for the view item while it's in hidden state and
9711        // marked as unimportant for accessibility.
9712        private int mWasImportantForAccessibilityBeforeHidden =
9713                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
9714        // set if we defer the accessibility state change of the view holder
9715        private  int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
9716
9717        /**
9718         * Is set when VH is bound from the adapter and cleaned right before it is sent to
9719         * {@link RecycledViewPool}.
9720         */
9721        RecyclerView mOwnerRecyclerView;
9722
9723        public ViewHolder(View itemView) {
9724            if (itemView == null) {
9725                throw new IllegalArgumentException("itemView may not be null");
9726            }
9727            this.itemView = itemView;
9728        }
9729
9730        void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
9731            addFlags(ViewHolder.FLAG_REMOVED);
9732            offsetPosition(offset, applyToPreLayout);
9733            mPosition = mNewPosition;
9734        }
9735
9736        void offsetPosition(int offset, boolean applyToPreLayout) {
9737            if (mOldPosition == NO_POSITION) {
9738                mOldPosition = mPosition;
9739            }
9740            if (mPreLayoutPosition == NO_POSITION) {
9741                mPreLayoutPosition = mPosition;
9742            }
9743            if (applyToPreLayout) {
9744                mPreLayoutPosition += offset;
9745            }
9746            mPosition += offset;
9747            if (itemView.getLayoutParams() != null) {
9748                ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
9749            }
9750        }
9751
9752        void clearOldPosition() {
9753            mOldPosition = NO_POSITION;
9754            mPreLayoutPosition = NO_POSITION;
9755        }
9756
9757        void saveOldPosition() {
9758            if (mOldPosition == NO_POSITION) {
9759                mOldPosition = mPosition;
9760            }
9761        }
9762
9763        boolean shouldIgnore() {
9764            return (mFlags & FLAG_IGNORE) != 0;
9765        }
9766
9767        /**
9768         * @deprecated This method is deprecated because its meaning is ambiguous due to the async
9769         * handling of adapter updates. Please use {@link #getLayoutPosition()} or
9770         * {@link #getAdapterPosition()} depending on your use case.
9771         *
9772         * @see #getLayoutPosition()
9773         * @see #getAdapterPosition()
9774         */
9775        @Deprecated
9776        public final int getPosition() {
9777            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
9778        }
9779
9780        /**
9781         * Returns the position of the ViewHolder in terms of the latest layout pass.
9782         * <p>
9783         * This position is mostly used by RecyclerView components to be consistent while
9784         * RecyclerView lazily processes adapter updates.
9785         * <p>
9786         * For performance and animation reasons, RecyclerView batches all adapter updates until the
9787         * next layout pass. This may cause mismatches between the Adapter position of the item and
9788         * the position it had in the latest layout calculations.
9789         * <p>
9790         * LayoutManagers should always call this method while doing calculations based on item
9791         * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
9792         * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
9793         * of the item.
9794         * <p>
9795         * If LayoutManager needs to call an external method that requires the adapter position of
9796         * the item, it can use {@link #getAdapterPosition()} or
9797         * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
9798         *
9799         * @return Returns the adapter position of the ViewHolder in the latest layout pass.
9800         * @see #getAdapterPosition()
9801         */
9802        public final int getLayoutPosition() {
9803            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
9804        }
9805
9806        /**
9807         * Returns the Adapter position of the item represented by this ViewHolder.
9808         * <p>
9809         * Note that this might be different than the {@link #getLayoutPosition()} if there are
9810         * pending adapter updates but a new layout pass has not happened yet.
9811         * <p>
9812         * RecyclerView does not handle any adapter updates until the next layout traversal. This
9813         * may create temporary inconsistencies between what user sees on the screen and what
9814         * adapter contents have. This inconsistency is not important since it will be less than
9815         * 16ms but it might be a problem if you want to use ViewHolder position to access the
9816         * adapter. Sometimes, you may need to get the exact adapter position to do
9817         * some actions in response to user events. In that case, you should use this method which
9818         * will calculate the Adapter position of the ViewHolder.
9819         * <p>
9820         * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
9821         * next layout pass, the return value of this method will be {@link #NO_POSITION}.
9822         *
9823         * @return The adapter position of the item if it still exists in the adapter.
9824         * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
9825         * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
9826         * layout pass or the ViewHolder has already been recycled.
9827         */
9828        public final int getAdapterPosition() {
9829            if (mOwnerRecyclerView == null) {
9830                return NO_POSITION;
9831            }
9832            return mOwnerRecyclerView.getAdapterPositionFor(this);
9833        }
9834
9835        /**
9836         * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
9837         * to perform animations.
9838         * <p>
9839         * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
9840         * adapter index in the previous layout.
9841         *
9842         * @return The previous adapter index of the Item represented by this ViewHolder or
9843         * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
9844         * complete).
9845         */
9846        public final int getOldPosition() {
9847            return mOldPosition;
9848        }
9849
9850        /**
9851         * Returns The itemId represented by this ViewHolder.
9852         *
9853         * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
9854         * otherwise
9855         */
9856        public final long getItemId() {
9857            return mItemId;
9858        }
9859
9860        /**
9861         * @return The view type of this ViewHolder.
9862         */
9863        public final int getItemViewType() {
9864            return mItemViewType;
9865        }
9866
9867        boolean isScrap() {
9868            return mScrapContainer != null;
9869        }
9870
9871        void unScrap() {
9872            mScrapContainer.unscrapView(this);
9873        }
9874
9875        boolean wasReturnedFromScrap() {
9876            return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
9877        }
9878
9879        void clearReturnedFromScrapFlag() {
9880            mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
9881        }
9882
9883        void clearTmpDetachFlag() {
9884            mFlags = mFlags & ~FLAG_TMP_DETACHED;
9885        }
9886
9887        void stopIgnoring() {
9888            mFlags = mFlags & ~FLAG_IGNORE;
9889        }
9890
9891        void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
9892            mScrapContainer = recycler;
9893            mInChangeScrap = isChangeScrap;
9894        }
9895
9896        boolean isInvalid() {
9897            return (mFlags & FLAG_INVALID) != 0;
9898        }
9899
9900        boolean needsUpdate() {
9901            return (mFlags & FLAG_UPDATE) != 0;
9902        }
9903
9904        boolean isBound() {
9905            return (mFlags & FLAG_BOUND) != 0;
9906        }
9907
9908        boolean isRemoved() {
9909            return (mFlags & FLAG_REMOVED) != 0;
9910        }
9911
9912        boolean hasAnyOfTheFlags(int flags) {
9913            return (mFlags & flags) != 0;
9914        }
9915
9916        boolean isTmpDetached() {
9917            return (mFlags & FLAG_TMP_DETACHED) != 0;
9918        }
9919
9920        boolean isAdapterPositionUnknown() {
9921            return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
9922        }
9923
9924        void setFlags(int flags, int mask) {
9925            mFlags = (mFlags & ~mask) | (flags & mask);
9926        }
9927
9928        void addFlags(int flags) {
9929            mFlags |= flags;
9930        }
9931
9932        void addChangePayload(Object payload) {
9933            if (payload == null) {
9934                addFlags(FLAG_ADAPTER_FULLUPDATE);
9935            } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
9936                createPayloadsIfNeeded();
9937                mPayloads.add(payload);
9938            }
9939        }
9940
9941        private void createPayloadsIfNeeded() {
9942            if (mPayloads == null) {
9943                mPayloads = new ArrayList<Object>();
9944                mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
9945            }
9946        }
9947
9948        void clearPayload() {
9949            if (mPayloads != null) {
9950                mPayloads.clear();
9951            }
9952            mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
9953        }
9954
9955        List<Object> getUnmodifiedPayloads() {
9956            if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
9957                if (mPayloads == null || mPayloads.size() == 0) {
9958                    // Initial state,  no update being called.
9959                    return FULLUPDATE_PAYLOADS;
9960                }
9961                // there are none-null payloads
9962                return mUnmodifiedPayloads;
9963            } else {
9964                // a full update has been called.
9965                return FULLUPDATE_PAYLOADS;
9966            }
9967        }
9968
9969        void resetInternal() {
9970            mFlags = 0;
9971            mPosition = NO_POSITION;
9972            mOldPosition = NO_POSITION;
9973            mItemId = NO_ID;
9974            mPreLayoutPosition = NO_POSITION;
9975            mIsRecyclableCount = 0;
9976            mShadowedHolder = null;
9977            mShadowingHolder = null;
9978            clearPayload();
9979            mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
9980            mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
9981        }
9982
9983        /**
9984         * Called when the child view enters the hidden state
9985         */
9986        private void onEnteredHiddenState(RecyclerView parent) {
9987            // While the view item is in hidden state, make it invisible for the accessibility.
9988            mWasImportantForAccessibilityBeforeHidden =
9989                    ViewCompat.getImportantForAccessibility(itemView);
9990            parent.setChildImportantForAccessibilityInternal(this,
9991                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
9992        }
9993
9994        /**
9995         * Called when the child view leaves the hidden state
9996         */
9997        private void onLeftHiddenState(RecyclerView parent) {
9998            parent.setChildImportantForAccessibilityInternal(this,
9999                    mWasImportantForAccessibilityBeforeHidden);
10000            mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10001        }
10002
10003        @Override
10004        public String toString() {
10005            final StringBuilder sb = new StringBuilder("ViewHolder{" +
10006                    Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId +
10007                    ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
10008            if (isScrap()) {
10009                sb.append(" scrap ")
10010                        .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
10011            }
10012            if (isInvalid()) sb.append(" invalid");
10013            if (!isBound()) sb.append(" unbound");
10014            if (needsUpdate()) sb.append(" update");
10015            if (isRemoved()) sb.append(" removed");
10016            if (shouldIgnore()) sb.append(" ignored");
10017            if (isTmpDetached()) sb.append(" tmpDetached");
10018            if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
10019            if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
10020
10021            if (itemView.getParent() == null) sb.append(" no parent");
10022            sb.append("}");
10023            return sb.toString();
10024        }
10025
10026        /**
10027         * Informs the recycler whether this item can be recycled. Views which are not
10028         * recyclable will not be reused for other items until setIsRecyclable() is
10029         * later set to true. Calls to setIsRecyclable() should always be paired (one
10030         * call to setIsRecyclabe(false) should always be matched with a later call to
10031         * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
10032         * reference-counted.
10033         *
10034         * @param recyclable Whether this item is available to be recycled. Default value
10035         * is true.
10036         */
10037        public final void setIsRecyclable(boolean recyclable) {
10038            mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
10039            if (mIsRecyclableCount < 0) {
10040                mIsRecyclableCount = 0;
10041                if (DEBUG) {
10042                    throw new RuntimeException("isRecyclable decremented below 0: " +
10043                            "unmatched pair of setIsRecyable() calls for " + this);
10044                }
10045                Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: " +
10046                        "unmatched pair of setIsRecyable() calls for " + this);
10047            } else if (!recyclable && mIsRecyclableCount == 1) {
10048                mFlags |= FLAG_NOT_RECYCLABLE;
10049            } else if (recyclable && mIsRecyclableCount == 0) {
10050                mFlags &= ~FLAG_NOT_RECYCLABLE;
10051            }
10052            if (DEBUG) {
10053                Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
10054            }
10055        }
10056
10057        /**
10058         * @see {@link #setIsRecyclable(boolean)}
10059         *
10060         * @return true if this item is available to be recycled, false otherwise.
10061         */
10062        public final boolean isRecyclable() {
10063            return (mFlags & FLAG_NOT_RECYCLABLE) == 0 &&
10064                    !ViewCompat.hasTransientState(itemView);
10065        }
10066
10067        /**
10068         * Returns whether we have animations referring to this view holder or not.
10069         * This is similar to isRecyclable flag but does not check transient state.
10070         */
10071        private boolean shouldBeKeptAsChild() {
10072            return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
10073        }
10074
10075        /**
10076         * @return True if ViewHolder is not referenced by RecyclerView animations but has
10077         * transient state which will prevent it from being recycled.
10078         */
10079        private boolean doesTransientStatePreventRecycling() {
10080            return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView);
10081        }
10082
10083        boolean isUpdated() {
10084            return (mFlags & FLAG_UPDATE) != 0;
10085        }
10086    }
10087
10088    /**
10089     * This method is here so that we can control the important for a11y changes and test it.
10090     */
10091    @VisibleForTesting
10092    boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
10093            int importantForAccessibility) {
10094        if (isComputingLayout()) {
10095            viewHolder.mPendingAccessibilityState = importantForAccessibility;
10096            mPendingAccessibilityImportanceChange.add(viewHolder);
10097            return false;
10098        }
10099        ViewCompat.setImportantForAccessibility(viewHolder.itemView, importantForAccessibility);
10100        return true;
10101    }
10102
10103    void dispatchPendingImportantForAccessibilityChanges() {
10104        for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i --) {
10105            ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
10106            if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
10107                return;
10108            }
10109            int state = viewHolder.mPendingAccessibilityState;
10110            if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
10111                //noinspection WrongConstant
10112                ViewCompat.setImportantForAccessibility(viewHolder.itemView, state);
10113                viewHolder.mPendingAccessibilityState =
10114                        ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
10115            }
10116        }
10117        mPendingAccessibilityImportanceChange.clear();
10118    }
10119
10120    int getAdapterPositionFor(ViewHolder viewHolder) {
10121        if (viewHolder.hasAnyOfTheFlags( ViewHolder.FLAG_INVALID |
10122                ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
10123                || !viewHolder.isBound()) {
10124            return RecyclerView.NO_POSITION;
10125        }
10126        return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
10127    }
10128
10129    // NestedScrollingChild
10130
10131    @Override
10132    public void setNestedScrollingEnabled(boolean enabled) {
10133        getScrollingChildHelper().setNestedScrollingEnabled(enabled);
10134    }
10135
10136    @Override
10137    public boolean isNestedScrollingEnabled() {
10138        return getScrollingChildHelper().isNestedScrollingEnabled();
10139    }
10140
10141    @Override
10142    public boolean startNestedScroll(int axes) {
10143        return getScrollingChildHelper().startNestedScroll(axes);
10144    }
10145
10146    @Override
10147    public void stopNestedScroll() {
10148        getScrollingChildHelper().stopNestedScroll();
10149    }
10150
10151    @Override
10152    public boolean hasNestedScrollingParent() {
10153        return getScrollingChildHelper().hasNestedScrollingParent();
10154    }
10155
10156    @Override
10157    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
10158            int dyUnconsumed, int[] offsetInWindow) {
10159        return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
10160                dxUnconsumed, dyUnconsumed, offsetInWindow);
10161    }
10162
10163    @Override
10164    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
10165        return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
10166    }
10167
10168    @Override
10169    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
10170        return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
10171    }
10172
10173    @Override
10174    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
10175        return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
10176    }
10177
10178    /**
10179     * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
10180     * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
10181     * to create their own subclass of this <code>LayoutParams</code> class
10182     * to store any additional required per-child view metadata about the layout.
10183     */
10184    public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
10185        ViewHolder mViewHolder;
10186        final Rect mDecorInsets = new Rect();
10187        boolean mInsetsDirty = true;
10188        // Flag is set to true if the view is bound while it is detached from RV.
10189        // In this case, we need to manually call invalidate after view is added to guarantee that
10190        // invalidation is populated through the View hierarchy
10191        boolean mPendingInvalidate = false;
10192
10193        public LayoutParams(Context c, AttributeSet attrs) {
10194            super(c, attrs);
10195        }
10196
10197        public LayoutParams(int width, int height) {
10198            super(width, height);
10199        }
10200
10201        public LayoutParams(MarginLayoutParams source) {
10202            super(source);
10203        }
10204
10205        public LayoutParams(ViewGroup.LayoutParams source) {
10206            super(source);
10207        }
10208
10209        public LayoutParams(LayoutParams source) {
10210            super((ViewGroup.LayoutParams) source);
10211        }
10212
10213        /**
10214         * Returns true if the view this LayoutParams is attached to needs to have its content
10215         * updated from the corresponding adapter.
10216         *
10217         * @return true if the view should have its content updated
10218         */
10219        public boolean viewNeedsUpdate() {
10220            return mViewHolder.needsUpdate();
10221        }
10222
10223        /**
10224         * Returns true if the view this LayoutParams is attached to is now representing
10225         * potentially invalid data. A LayoutManager should scrap/recycle it.
10226         *
10227         * @return true if the view is invalid
10228         */
10229        public boolean isViewInvalid() {
10230            return mViewHolder.isInvalid();
10231        }
10232
10233        /**
10234         * Returns true if the adapter data item corresponding to the view this LayoutParams
10235         * is attached to has been removed from the data set. A LayoutManager may choose to
10236         * treat it differently in order to animate its outgoing or disappearing state.
10237         *
10238         * @return true if the item the view corresponds to was removed from the data set
10239         */
10240        public boolean isItemRemoved() {
10241            return mViewHolder.isRemoved();
10242        }
10243
10244        /**
10245         * Returns true if the adapter data item corresponding to the view this LayoutParams
10246         * is attached to has been changed in the data set. A LayoutManager may choose to
10247         * treat it differently in order to animate its changing state.
10248         *
10249         * @return true if the item the view corresponds to was changed in the data set
10250         */
10251        public boolean isItemChanged() {
10252            return mViewHolder.isUpdated();
10253        }
10254
10255        /**
10256         * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
10257         */
10258        @Deprecated
10259        public int getViewPosition() {
10260            return mViewHolder.getPosition();
10261        }
10262
10263        /**
10264         * Returns the adapter position that the view this LayoutParams is attached to corresponds
10265         * to as of latest layout calculation.
10266         *
10267         * @return the adapter position this view as of latest layout pass
10268         */
10269        public int getViewLayoutPosition() {
10270            return mViewHolder.getLayoutPosition();
10271        }
10272
10273        /**
10274         * Returns the up-to-date adapter position that the view this LayoutParams is attached to
10275         * corresponds to.
10276         *
10277         * @return the up-to-date adapter position this view. It may return
10278         * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
10279         * its up-to-date position cannot be calculated.
10280         */
10281        public int getViewAdapterPosition() {
10282            return mViewHolder.getAdapterPosition();
10283        }
10284    }
10285
10286    /**
10287     * Observer base class for watching changes to an {@link Adapter}.
10288     * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
10289     */
10290    public static abstract class AdapterDataObserver {
10291        public void onChanged() {
10292            // Do nothing
10293        }
10294
10295        public void onItemRangeChanged(int positionStart, int itemCount) {
10296            // do nothing
10297        }
10298
10299        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
10300            // fallback to onItemRangeChanged(positionStart, itemCount) if app
10301            // does not override this method.
10302            onItemRangeChanged(positionStart, itemCount);
10303        }
10304
10305        public void onItemRangeInserted(int positionStart, int itemCount) {
10306            // do nothing
10307        }
10308
10309        public void onItemRangeRemoved(int positionStart, int itemCount) {
10310            // do nothing
10311        }
10312
10313        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
10314            // do nothing
10315        }
10316    }
10317
10318    /**
10319     * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
10320     * provides methods to trigger a programmatic scroll.</p>
10321     *
10322     * @see LinearSmoothScroller
10323     */
10324    public static abstract class SmoothScroller {
10325
10326        private int mTargetPosition = RecyclerView.NO_POSITION;
10327
10328        private RecyclerView mRecyclerView;
10329
10330        private LayoutManager mLayoutManager;
10331
10332        private boolean mPendingInitialRun;
10333
10334        private boolean mRunning;
10335
10336        private View mTargetView;
10337
10338        private final Action mRecyclingAction;
10339
10340        public SmoothScroller() {
10341            mRecyclingAction = new Action(0, 0);
10342        }
10343
10344        /**
10345         * Starts a smooth scroll for the given target position.
10346         * <p>In each animation step, {@link RecyclerView} will check
10347         * for the target view and call either
10348         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
10349         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
10350         * SmoothScroller is stopped.</p>
10351         *
10352         * <p>Note that if RecyclerView finds the target view, it will automatically stop the
10353         * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
10354         * stop calling SmoothScroller in each animation step.</p>
10355         */
10356        void start(RecyclerView recyclerView, LayoutManager layoutManager) {
10357            mRecyclerView = recyclerView;
10358            mLayoutManager = layoutManager;
10359            if (mTargetPosition == RecyclerView.NO_POSITION) {
10360                throw new IllegalArgumentException("Invalid target position");
10361            }
10362            mRecyclerView.mState.mTargetPosition = mTargetPosition;
10363            mRunning = true;
10364            mPendingInitialRun = true;
10365            mTargetView = findViewByPosition(getTargetPosition());
10366            onStart();
10367            mRecyclerView.mViewFlinger.postOnAnimation();
10368        }
10369
10370        public void setTargetPosition(int targetPosition) {
10371            mTargetPosition = targetPosition;
10372        }
10373
10374        /**
10375         * @return The LayoutManager to which this SmoothScroller is attached. Will return
10376         * <code>null</code> after the SmoothScroller is stopped.
10377         */
10378        @Nullable
10379        public LayoutManager getLayoutManager() {
10380            return mLayoutManager;
10381        }
10382
10383        /**
10384         * Stops running the SmoothScroller in each animation callback. Note that this does not
10385         * cancel any existing {@link Action} updated by
10386         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
10387         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
10388         */
10389        final protected void stop() {
10390            if (!mRunning) {
10391                return;
10392            }
10393            onStop();
10394            mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
10395            mTargetView = null;
10396            mTargetPosition = RecyclerView.NO_POSITION;
10397            mPendingInitialRun = false;
10398            mRunning = false;
10399            // trigger a cleanup
10400            mLayoutManager.onSmoothScrollerStopped(this);
10401            // clear references to avoid any potential leak by a custom smooth scroller
10402            mLayoutManager = null;
10403            mRecyclerView = null;
10404        }
10405
10406        /**
10407         * Returns true if SmoothScroller has been started but has not received the first
10408         * animation
10409         * callback yet.
10410         *
10411         * @return True if this SmoothScroller is waiting to start
10412         */
10413        public boolean isPendingInitialRun() {
10414            return mPendingInitialRun;
10415        }
10416
10417
10418        /**
10419         * @return True if SmoothScroller is currently active
10420         */
10421        public boolean isRunning() {
10422            return mRunning;
10423        }
10424
10425        /**
10426         * Returns the adapter position of the target item
10427         *
10428         * @return Adapter position of the target item or
10429         * {@link RecyclerView#NO_POSITION} if no target view is set.
10430         */
10431        public int getTargetPosition() {
10432            return mTargetPosition;
10433        }
10434
10435        private void onAnimation(int dx, int dy) {
10436            final RecyclerView recyclerView = mRecyclerView;
10437            if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
10438                stop();
10439            }
10440            mPendingInitialRun = false;
10441            if (mTargetView != null) {
10442                // verify target position
10443                if (getChildPosition(mTargetView) == mTargetPosition) {
10444                    onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
10445                    mRecyclingAction.runIfNecessary(recyclerView);
10446                    stop();
10447                } else {
10448                    Log.e(TAG, "Passed over target position while smooth scrolling.");
10449                    mTargetView = null;
10450                }
10451            }
10452            if (mRunning) {
10453                onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
10454                boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
10455                mRecyclingAction.runIfNecessary(recyclerView);
10456                if (hadJumpTarget) {
10457                    // It is not stopped so needs to be restarted
10458                    if (mRunning) {
10459                        mPendingInitialRun = true;
10460                        recyclerView.mViewFlinger.postOnAnimation();
10461                    } else {
10462                        stop(); // done
10463                    }
10464                }
10465            }
10466        }
10467
10468        /**
10469         * @see RecyclerView#getChildLayoutPosition(android.view.View)
10470         */
10471        public int getChildPosition(View view) {
10472            return mRecyclerView.getChildLayoutPosition(view);
10473        }
10474
10475        /**
10476         * @see RecyclerView.LayoutManager#getChildCount()
10477         */
10478        public int getChildCount() {
10479            return mRecyclerView.mLayout.getChildCount();
10480        }
10481
10482        /**
10483         * @see RecyclerView.LayoutManager#findViewByPosition(int)
10484         */
10485        public View findViewByPosition(int position) {
10486            return mRecyclerView.mLayout.findViewByPosition(position);
10487        }
10488
10489        /**
10490         * @see RecyclerView#scrollToPosition(int)
10491         * @deprecated Use {@link Action#jumpTo(int)}.
10492         */
10493        @Deprecated
10494        public void instantScrollToPosition(int position) {
10495            mRecyclerView.scrollToPosition(position);
10496        }
10497
10498        protected void onChildAttachedToWindow(View child) {
10499            if (getChildPosition(child) == getTargetPosition()) {
10500                mTargetView = child;
10501                if (DEBUG) {
10502                    Log.d(TAG, "smooth scroll target view has been attached");
10503                }
10504            }
10505        }
10506
10507        /**
10508         * Normalizes the vector.
10509         * @param scrollVector The vector that points to the target scroll position
10510         */
10511        protected void normalize(PointF scrollVector) {
10512            final double magnitude = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y
10513                    * scrollVector.y);
10514            scrollVector.x /= magnitude;
10515            scrollVector.y /= magnitude;
10516        }
10517
10518        /**
10519         * Called when smooth scroll is started. This might be a good time to do setup.
10520         */
10521        abstract protected void onStart();
10522
10523        /**
10524         * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
10525         * @see #stop()
10526         */
10527        abstract protected void onStop();
10528
10529        /**
10530         * <p>RecyclerView will call this method each time it scrolls until it can find the target
10531         * position in the layout.</p>
10532         * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
10533         * provided {@link Action} to define the next scroll.</p>
10534         *
10535         * @param dx        Last scroll amount horizontally
10536         * @param dy        Last scroll amount vertically
10537         * @param state     Transient state of RecyclerView
10538         * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
10539         *                  update this object.
10540         */
10541        abstract protected void onSeekTargetStep(int dx, int dy, State state, Action action);
10542
10543        /**
10544         * Called when the target position is laid out. This is the last callback SmoothScroller
10545         * will receive and it should update the provided {@link Action} to define the scroll
10546         * details towards the target view.
10547         * @param targetView    The view element which render the target position.
10548         * @param state         Transient state of RecyclerView
10549         * @param action        Action instance that you should update to define final scroll action
10550         *                      towards the targetView
10551         */
10552        abstract protected void onTargetFound(View targetView, State state, Action action);
10553
10554        /**
10555         * Holds information about a smooth scroll request by a {@link SmoothScroller}.
10556         */
10557        public static class Action {
10558
10559            public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
10560
10561            private int mDx;
10562
10563            private int mDy;
10564
10565            private int mDuration;
10566
10567            private int mJumpToPosition = NO_POSITION;
10568
10569            private Interpolator mInterpolator;
10570
10571            private boolean changed = false;
10572
10573            // we track this variable to inform custom implementer if they are updating the action
10574            // in every animation callback
10575            private int consecutiveUpdates = 0;
10576
10577            /**
10578             * @param dx Pixels to scroll horizontally
10579             * @param dy Pixels to scroll vertically
10580             */
10581            public Action(int dx, int dy) {
10582                this(dx, dy, UNDEFINED_DURATION, null);
10583            }
10584
10585            /**
10586             * @param dx       Pixels to scroll horizontally
10587             * @param dy       Pixels to scroll vertically
10588             * @param duration Duration of the animation in milliseconds
10589             */
10590            public Action(int dx, int dy, int duration) {
10591                this(dx, dy, duration, null);
10592            }
10593
10594            /**
10595             * @param dx           Pixels to scroll horizontally
10596             * @param dy           Pixels to scroll vertically
10597             * @param duration     Duration of the animation in milliseconds
10598             * @param interpolator Interpolator to be used when calculating scroll position in each
10599             *                     animation step
10600             */
10601            public Action(int dx, int dy, int duration, Interpolator interpolator) {
10602                mDx = dx;
10603                mDy = dy;
10604                mDuration = duration;
10605                mInterpolator = interpolator;
10606            }
10607
10608            /**
10609             * Instead of specifying pixels to scroll, use the target position to jump using
10610             * {@link RecyclerView#scrollToPosition(int)}.
10611             * <p>
10612             * You may prefer using this method if scroll target is really far away and you prefer
10613             * to jump to a location and smooth scroll afterwards.
10614             * <p>
10615             * Note that calling this method takes priority over other update methods such as
10616             * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
10617             * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
10618             * {@link #jumpTo(int)}, the other changes will not be considered for this animation
10619             * frame.
10620             *
10621             * @param targetPosition The target item position to scroll to using instant scrolling.
10622             */
10623            public void jumpTo(int targetPosition) {
10624                mJumpToPosition = targetPosition;
10625            }
10626
10627            boolean hasJumpTarget() {
10628                return mJumpToPosition >= 0;
10629            }
10630
10631            void runIfNecessary(RecyclerView recyclerView) {
10632                if (mJumpToPosition >= 0) {
10633                    final int position = mJumpToPosition;
10634                    mJumpToPosition = NO_POSITION;
10635                    recyclerView.jumpToPositionForSmoothScroller(position);
10636                    changed = false;
10637                    return;
10638                }
10639                if (changed) {
10640                    validate();
10641                    if (mInterpolator == null) {
10642                        if (mDuration == UNDEFINED_DURATION) {
10643                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
10644                        } else {
10645                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
10646                        }
10647                    } else {
10648                        recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration, mInterpolator);
10649                    }
10650                    consecutiveUpdates ++;
10651                    if (consecutiveUpdates > 10) {
10652                        // A new action is being set in every animation step. This looks like a bad
10653                        // implementation. Inform developer.
10654                        Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
10655                                + " you are not changing it unless necessary");
10656                    }
10657                    changed = false;
10658                } else {
10659                    consecutiveUpdates = 0;
10660                }
10661            }
10662
10663            private void validate() {
10664                if (mInterpolator != null && mDuration < 1) {
10665                    throw new IllegalStateException("If you provide an interpolator, you must"
10666                            + " set a positive duration");
10667                } else if (mDuration < 1) {
10668                    throw new IllegalStateException("Scroll duration must be a positive number");
10669                }
10670            }
10671
10672            public int getDx() {
10673                return mDx;
10674            }
10675
10676            public void setDx(int dx) {
10677                changed = true;
10678                mDx = dx;
10679            }
10680
10681            public int getDy() {
10682                return mDy;
10683            }
10684
10685            public void setDy(int dy) {
10686                changed = true;
10687                mDy = dy;
10688            }
10689
10690            public int getDuration() {
10691                return mDuration;
10692            }
10693
10694            public void setDuration(int duration) {
10695                changed = true;
10696                mDuration = duration;
10697            }
10698
10699            public Interpolator getInterpolator() {
10700                return mInterpolator;
10701            }
10702
10703            /**
10704             * Sets the interpolator to calculate scroll steps
10705             * @param interpolator The interpolator to use. If you specify an interpolator, you must
10706             *                     also set the duration.
10707             * @see #setDuration(int)
10708             */
10709            public void setInterpolator(Interpolator interpolator) {
10710                changed = true;
10711                mInterpolator = interpolator;
10712            }
10713
10714            /**
10715             * Updates the action with given parameters.
10716             * @param dx Pixels to scroll horizontally
10717             * @param dy Pixels to scroll vertically
10718             * @param duration Duration of the animation in milliseconds
10719             * @param interpolator Interpolator to be used when calculating scroll position in each
10720             *                     animation step
10721             */
10722            public void update(int dx, int dy, int duration, Interpolator interpolator) {
10723                mDx = dx;
10724                mDy = dy;
10725                mDuration = duration;
10726                mInterpolator = interpolator;
10727                changed = true;
10728            }
10729        }
10730
10731        /**
10732         * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
10733         * to provide a hint to a {@link SmoothScroller} about the location of the target position.
10734         */
10735        public interface ScrollVectorProvider {
10736            /**
10737             * Should calculate the vector that points to the direction where the target position
10738             * can be found.
10739             * <p>
10740             * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
10741             * the target position.
10742             * <p>
10743             * The magnitude of the vector is not important. It is always normalized before being
10744             * used by the {@link LinearSmoothScroller}.
10745             * <p>
10746             * LayoutManager should not check whether the position exists in the adapter or not.
10747             *
10748             * @param targetPosition the target position to which the returned vector should point
10749             *
10750             * @return the scroll vector for a given position.
10751             */
10752            PointF computeScrollVectorForPosition(int targetPosition);
10753        }
10754    }
10755
10756    static class AdapterDataObservable extends Observable<AdapterDataObserver> {
10757        public boolean hasObservers() {
10758            return !mObservers.isEmpty();
10759        }
10760
10761        public void notifyChanged() {
10762            // since onChanged() is implemented by the app, it could do anything, including
10763            // removing itself from {@link mObservers} - and that could cause problems if
10764            // an iterator is used on the ArrayList {@link mObservers}.
10765            // to avoid such problems, just march thru the list in the reverse order.
10766            for (int i = mObservers.size() - 1; i >= 0; i--) {
10767                mObservers.get(i).onChanged();
10768            }
10769        }
10770
10771        public void notifyItemRangeChanged(int positionStart, int itemCount) {
10772            notifyItemRangeChanged(positionStart, itemCount, null);
10773        }
10774
10775        public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
10776            // since onItemRangeChanged() is implemented by the app, it could do anything, including
10777            // removing itself from {@link mObservers} - and that could cause problems if
10778            // an iterator is used on the ArrayList {@link mObservers}.
10779            // to avoid such problems, just march thru the list in the reverse order.
10780            for (int i = mObservers.size() - 1; i >= 0; i--) {
10781                mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
10782            }
10783        }
10784
10785        public void notifyItemRangeInserted(int positionStart, int itemCount) {
10786            // since onItemRangeInserted() is implemented by the app, it could do anything,
10787            // including removing itself from {@link mObservers} - and that could cause problems if
10788            // an iterator is used on the ArrayList {@link mObservers}.
10789            // to avoid such problems, just march thru the list in the reverse order.
10790            for (int i = mObservers.size() - 1; i >= 0; i--) {
10791                mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
10792            }
10793        }
10794
10795        public void notifyItemRangeRemoved(int positionStart, int itemCount) {
10796            // since onItemRangeRemoved() is implemented by the app, it could do anything, including
10797            // removing itself from {@link mObservers} - and that could cause problems if
10798            // an iterator is used on the ArrayList {@link mObservers}.
10799            // to avoid such problems, just march thru the list in the reverse order.
10800            for (int i = mObservers.size() - 1; i >= 0; i--) {
10801                mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
10802            }
10803        }
10804
10805        public void notifyItemMoved(int fromPosition, int toPosition) {
10806            for (int i = mObservers.size() - 1; i >= 0; i--) {
10807                mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
10808            }
10809        }
10810    }
10811
10812    /**
10813     * This is public so that the CREATOR can be access on cold launch.
10814     * @hide
10815     */
10816    @RestrictTo(GROUP_ID)
10817    public static class SavedState extends AbsSavedState {
10818
10819        Parcelable mLayoutState;
10820
10821        /**
10822         * called by CREATOR
10823         */
10824        SavedState(Parcel in, ClassLoader loader) {
10825            super(in, loader);
10826            mLayoutState = in.readParcelable(
10827                    loader != null ? loader : LayoutManager.class.getClassLoader());
10828        }
10829
10830        /**
10831         * Called by onSaveInstanceState
10832         */
10833        SavedState(Parcelable superState) {
10834            super(superState);
10835        }
10836
10837        @Override
10838        public void writeToParcel(Parcel dest, int flags) {
10839            super.writeToParcel(dest, flags);
10840            dest.writeParcelable(mLayoutState, 0);
10841        }
10842
10843        void copyFrom(SavedState other) {
10844            mLayoutState = other.mLayoutState;
10845        }
10846
10847        public static final Creator<SavedState> CREATOR = ParcelableCompat.newCreator(
10848                new ParcelableCompatCreatorCallbacks<SavedState>() {
10849                    @Override
10850                    public SavedState createFromParcel(Parcel in, ClassLoader loader) {
10851                        return new SavedState(in, loader);
10852                    }
10853
10854                    @Override
10855                    public SavedState[] newArray(int size) {
10856                        return new SavedState[size];
10857                    }
10858                });
10859    }
10860    /**
10861     * <p>Contains useful information about the current RecyclerView state like target scroll
10862     * position or view focus. State object can also keep arbitrary data, identified by resource
10863     * ids.</p>
10864     * <p>Often times, RecyclerView components will need to pass information between each other.
10865     * To provide a well defined data bus between components, RecyclerView passes the same State
10866     * object to component callbacks and these components can use it to exchange data.</p>
10867     * <p>If you implement custom components, you can use State's put/get/remove methods to pass
10868     * data between your components without needing to manage their lifecycles.</p>
10869     */
10870    public static class State {
10871        static final int STEP_START = 1;
10872        static final int STEP_LAYOUT = 1 << 1;
10873        static final int STEP_ANIMATIONS = 1 << 2;
10874
10875        void assertLayoutStep(int accepted) {
10876            if ((accepted & mLayoutStep) == 0) {
10877                throw new IllegalStateException("Layout state should be one of "
10878                        + Integer.toBinaryString(accepted) + " but it is "
10879                        + Integer.toBinaryString(mLayoutStep));
10880            }
10881        }
10882
10883        @IntDef(flag = true, value = {
10884                STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
10885        })
10886        @Retention(RetentionPolicy.SOURCE)
10887        @interface LayoutState {}
10888
10889        private int mTargetPosition = RecyclerView.NO_POSITION;
10890
10891        @LayoutState
10892        int mLayoutStep = STEP_START;
10893
10894        private SparseArray<Object> mData;
10895
10896        /**
10897         * Number of items adapter has.
10898         */
10899        int mItemCount = 0;
10900
10901        /**
10902         * Number of items adapter had in the previous layout.
10903         */
10904        int mPreviousLayoutItemCount = 0;
10905
10906        /**
10907         * Number of items that were NOT laid out but has been deleted from the adapter after the
10908         * previous layout.
10909         */
10910        int mDeletedInvisibleItemCountSincePreviousLayout = 0;
10911
10912        boolean mStructureChanged = false;
10913
10914        boolean mInPreLayout = false;
10915
10916        boolean mRunSimpleAnimations = false;
10917
10918        boolean mRunPredictiveAnimations = false;
10919
10920        boolean mTrackOldChangeHolders = false;
10921
10922        boolean mIsMeasuring = false;
10923
10924        /**
10925         * This data is saved before a layout calculation happens. After the layout is finished,
10926         * if the previously focused view has been replaced with another view for the same item, we
10927         * move the focus to the new item automatically.
10928         */
10929        int mFocusedItemPosition;
10930        long mFocusedItemId;
10931        // when a sub child has focus, record its id and see if we can directly request focus on
10932        // that one instead
10933        int mFocusedSubChildId;
10934
10935        State reset() {
10936            mTargetPosition = RecyclerView.NO_POSITION;
10937            if (mData != null) {
10938                mData.clear();
10939            }
10940            mItemCount = 0;
10941            mStructureChanged = false;
10942            mIsMeasuring = false;
10943            return this;
10944        }
10945
10946        /**
10947         * Returns true if the RecyclerView is currently measuring the layout. This value is
10948         * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
10949         * has non-exact measurement specs.
10950         * <p>
10951         * Note that if the LayoutManager supports predictive animations and it is calculating the
10952         * pre-layout step, this value will be {@code false} even if the RecyclerView is in
10953         * {@code onMeasure} call. This is because pre-layout means the previous state of the
10954         * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
10955         * LayoutManager is always guaranteed to receive another call to
10956         * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
10957         *
10958         * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
10959         */
10960        public boolean isMeasuring() {
10961            return mIsMeasuring;
10962        }
10963
10964        /**
10965         * Returns true if
10966         * @return
10967         */
10968        public boolean isPreLayout() {
10969            return mInPreLayout;
10970        }
10971
10972        /**
10973         * Returns whether RecyclerView will run predictive animations in this layout pass
10974         * or not.
10975         *
10976         * @return true if RecyclerView is calculating predictive animations to be run at the end
10977         *         of the layout pass.
10978         */
10979        public boolean willRunPredictiveAnimations() {
10980            return mRunPredictiveAnimations;
10981        }
10982
10983        /**
10984         * Returns whether RecyclerView will run simple animations in this layout pass
10985         * or not.
10986         *
10987         * @return true if RecyclerView is calculating simple animations to be run at the end of
10988         *         the layout pass.
10989         */
10990        public boolean willRunSimpleAnimations() {
10991            return mRunSimpleAnimations;
10992        }
10993
10994        /**
10995         * Removes the mapping from the specified id, if there was any.
10996         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
10997         *                   preserve cross functionality and avoid conflicts.
10998         */
10999        public void remove(int resourceId) {
11000            if (mData == null) {
11001                return;
11002            }
11003            mData.remove(resourceId);
11004        }
11005
11006        /**
11007         * Gets the Object mapped from the specified id, or <code>null</code>
11008         * if no such data exists.
11009         *
11010         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
11011         *                   to
11012         *                   preserve cross functionality and avoid conflicts.
11013         */
11014        public <T> T get(int resourceId) {
11015            if (mData == null) {
11016                return null;
11017            }
11018            return (T) mData.get(resourceId);
11019        }
11020
11021        /**
11022         * Adds a mapping from the specified id to the specified value, replacing the previous
11023         * mapping from the specified key if there was one.
11024         *
11025         * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
11026         *                   preserve cross functionality and avoid conflicts.
11027         * @param data       The data you want to associate with the resourceId.
11028         */
11029        public void put(int resourceId, Object data) {
11030            if (mData == null) {
11031                mData = new SparseArray<Object>();
11032            }
11033            mData.put(resourceId, data);
11034        }
11035
11036        /**
11037         * If scroll is triggered to make a certain item visible, this value will return the
11038         * adapter index of that item.
11039         * @return Adapter index of the target item or
11040         * {@link RecyclerView#NO_POSITION} if there is no target
11041         * position.
11042         */
11043        public int getTargetScrollPosition() {
11044            return mTargetPosition;
11045        }
11046
11047        /**
11048         * Returns if current scroll has a target position.
11049         * @return true if scroll is being triggered to make a certain position visible
11050         * @see #getTargetScrollPosition()
11051         */
11052        public boolean hasTargetScrollPosition() {
11053            return mTargetPosition != RecyclerView.NO_POSITION;
11054        }
11055
11056        /**
11057         * @return true if the structure of the data set has changed since the last call to
11058         *         onLayoutChildren, false otherwise
11059         */
11060        public boolean didStructureChange() {
11061            return mStructureChanged;
11062        }
11063
11064        /**
11065         * Returns the total number of items that can be laid out. Note that this number is not
11066         * necessarily equal to the number of items in the adapter, so you should always use this
11067         * number for your position calculations and never access the adapter directly.
11068         * <p>
11069         * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
11070         * data changes on existing Views. These calculations are used to decide which animations
11071         * should be run.
11072         * <p>
11073         * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
11074         * present the correct state to LayoutManager in pre-layout pass.
11075         * <p>
11076         * For example, a newly added item is not included in pre-layout item count because
11077         * pre-layout reflects the contents of the adapter before the item is added. Behind the
11078         * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
11079         * LayoutManager does not know about the new item's existence in pre-layout. The item will
11080         * be available in second layout pass and will be included in the item count. Similar
11081         * adjustments are made for moved and removed items as well.
11082         * <p>
11083         * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
11084         *
11085         * @return The number of items currently available
11086         * @see LayoutManager#getItemCount()
11087         */
11088        public int getItemCount() {
11089            return mInPreLayout ?
11090                    (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout) :
11091                    mItemCount;
11092        }
11093
11094        @Override
11095        public String toString() {
11096            return "State{" +
11097                    "mTargetPosition=" + mTargetPosition +
11098                    ", mData=" + mData +
11099                    ", mItemCount=" + mItemCount +
11100                    ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount +
11101                    ", mDeletedInvisibleItemCountSincePreviousLayout="
11102                    + mDeletedInvisibleItemCountSincePreviousLayout +
11103                    ", mStructureChanged=" + mStructureChanged +
11104                    ", mInPreLayout=" + mInPreLayout +
11105                    ", mRunSimpleAnimations=" + mRunSimpleAnimations +
11106                    ", mRunPredictiveAnimations=" + mRunPredictiveAnimations +
11107                    '}';
11108        }
11109    }
11110
11111    /**
11112     * This class defines the behavior of fling if the developer wishes to handle it.
11113     * <p>
11114     * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
11115     *
11116     * @see #setOnFlingListener(OnFlingListener)
11117     */
11118    public static abstract class OnFlingListener {
11119
11120        /**
11121         * Override this to handle a fling given the velocities in both x and y directions.
11122         * Note that this method will only be called if the associated {@link LayoutManager}
11123         * supports scrolling and the fling is not handled by nested scrolls first.
11124         *
11125         * @param velocityX the fling velocity on the X axis
11126         * @param velocityY the fling velocity on the Y axis
11127         *
11128         * @return true if the fling washandled, false otherwise.
11129         */
11130        public abstract boolean onFling(int velocityX, int velocityY);
11131    }
11132
11133    /**
11134     * Internal listener that manages items after animations finish. This is how items are
11135     * retained (not recycled) during animations, but allowed to be recycled afterwards.
11136     * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
11137     * method on the animator's listener when it is done animating any item.
11138     */
11139    private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
11140
11141        ItemAnimatorRestoreListener() {
11142        }
11143
11144        @Override
11145        public void onAnimationFinished(ViewHolder item) {
11146            item.setIsRecyclable(true);
11147            if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
11148                item.mShadowedHolder = null;
11149            }
11150            // always null this because an OldViewHolder can never become NewViewHolder w/o being
11151            // recycled.
11152            item.mShadowingHolder = null;
11153            if (!item.shouldBeKeptAsChild()) {
11154                if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
11155                    removeDetachedView(item.itemView, false);
11156                }
11157            }
11158        }
11159    }
11160
11161    /**
11162     * This class defines the animations that take place on items as changes are made
11163     * to the adapter.
11164     *
11165     * Subclasses of ItemAnimator can be used to implement custom animations for actions on
11166     * ViewHolder items. The RecyclerView will manage retaining these items while they
11167     * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
11168     * when a ViewHolder's animation is finished. In other words, there must be a matching
11169     * {@link #dispatchAnimationFinished(ViewHolder)} call for each
11170     * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
11171     * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11172     * animateChange()}
11173     * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
11174     * and
11175     * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11176     * animateDisappearance()} call.
11177     *
11178     * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
11179     *
11180     * @see #setItemAnimator(ItemAnimator)
11181     */
11182    @SuppressWarnings("UnusedParameters")
11183    public static abstract class ItemAnimator {
11184
11185        /**
11186         * The Item represented by this ViewHolder is updated.
11187         * <p>
11188         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11189         */
11190        public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
11191
11192        /**
11193         * The Item represented by this ViewHolder is removed from the adapter.
11194         * <p>
11195         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11196         */
11197        public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
11198
11199        /**
11200         * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
11201         * represented by this ViewHolder is invalid.
11202         * <p>
11203         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11204         */
11205        public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
11206
11207        /**
11208         * The position of the Item represented by this ViewHolder has been changed. This flag is
11209         * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
11210         * any adapter change that may have a side effect on this item. (e.g. The item before this
11211         * one has been removed from the Adapter).
11212         * <p>
11213         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11214         */
11215        public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
11216
11217        /**
11218         * This ViewHolder was not laid out but has been added to the layout in pre-layout state
11219         * by the {@link LayoutManager}. This means that the item was already in the Adapter but
11220         * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
11221         * to add new items in pre-layout to specify their virtual location when they are invisible
11222         * (e.g. to specify the item should <i>animate in</i> from below the visible area).
11223         * <p>
11224         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11225         */
11226        public static final int FLAG_APPEARED_IN_PRE_LAYOUT
11227                = ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
11228
11229        /**
11230         * The set of flags that might be passed to
11231         * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11232         */
11233        @IntDef(flag=true, value={
11234                FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
11235                FLAG_APPEARED_IN_PRE_LAYOUT
11236        })
11237        @Retention(RetentionPolicy.SOURCE)
11238        public @interface AdapterChanges {}
11239        private ItemAnimatorListener mListener = null;
11240        private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
11241                new ArrayList<ItemAnimatorFinishedListener>();
11242
11243        private long mAddDuration = 120;
11244        private long mRemoveDuration = 120;
11245        private long mMoveDuration = 250;
11246        private long mChangeDuration = 250;
11247
11248        /**
11249         * Gets the current duration for which all move animations will run.
11250         *
11251         * @return The current move duration
11252         */
11253        public long getMoveDuration() {
11254            return mMoveDuration;
11255        }
11256
11257        /**
11258         * Sets the duration for which all move animations will run.
11259         *
11260         * @param moveDuration The move duration
11261         */
11262        public void setMoveDuration(long moveDuration) {
11263            mMoveDuration = moveDuration;
11264        }
11265
11266        /**
11267         * Gets the current duration for which all add animations will run.
11268         *
11269         * @return The current add duration
11270         */
11271        public long getAddDuration() {
11272            return mAddDuration;
11273        }
11274
11275        /**
11276         * Sets the duration for which all add animations will run.
11277         *
11278         * @param addDuration The add duration
11279         */
11280        public void setAddDuration(long addDuration) {
11281            mAddDuration = addDuration;
11282        }
11283
11284        /**
11285         * Gets the current duration for which all remove animations will run.
11286         *
11287         * @return The current remove duration
11288         */
11289        public long getRemoveDuration() {
11290            return mRemoveDuration;
11291        }
11292
11293        /**
11294         * Sets the duration for which all remove animations will run.
11295         *
11296         * @param removeDuration The remove duration
11297         */
11298        public void setRemoveDuration(long removeDuration) {
11299            mRemoveDuration = removeDuration;
11300        }
11301
11302        /**
11303         * Gets the current duration for which all change animations will run.
11304         *
11305         * @return The current change duration
11306         */
11307        public long getChangeDuration() {
11308            return mChangeDuration;
11309        }
11310
11311        /**
11312         * Sets the duration for which all change animations will run.
11313         *
11314         * @param changeDuration The change duration
11315         */
11316        public void setChangeDuration(long changeDuration) {
11317            mChangeDuration = changeDuration;
11318        }
11319
11320        /**
11321         * Internal only:
11322         * Sets the listener that must be called when the animator is finished
11323         * animating the item (or immediately if no animation happens). This is set
11324         * internally and is not intended to be set by external code.
11325         *
11326         * @param listener The listener that must be called.
11327         */
11328        void setListener(ItemAnimatorListener listener) {
11329            mListener = listener;
11330        }
11331
11332        /**
11333         * Called by the RecyclerView before the layout begins. Item animator should record
11334         * necessary information about the View before it is potentially rebound, moved or removed.
11335         * <p>
11336         * The data returned from this method will be passed to the related <code>animate**</code>
11337         * methods.
11338         * <p>
11339         * Note that this method may be called after pre-layout phase if LayoutManager adds new
11340         * Views to the layout in pre-layout pass.
11341         * <p>
11342         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
11343         * the View and the adapter change flags.
11344         *
11345         * @param state       The current State of RecyclerView which includes some useful data
11346         *                    about the layout that will be calculated.
11347         * @param viewHolder  The ViewHolder whose information should be recorded.
11348         * @param changeFlags Additional information about what changes happened in the Adapter
11349         *                    about the Item represented by this ViewHolder. For instance, if
11350         *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
11351         * @param payloads    The payload list that was previously passed to
11352         *                    {@link Adapter#notifyItemChanged(int, Object)} or
11353         *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
11354         *
11355         * @return An ItemHolderInfo instance that preserves necessary information about the
11356         * ViewHolder. This object will be passed back to related <code>animate**</code> methods
11357         * after layout is complete.
11358         *
11359         * @see #recordPostLayoutInformation(State, ViewHolder)
11360         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11361         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11362         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11363         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11364         */
11365        public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
11366                @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
11367                @NonNull List<Object> payloads) {
11368            return obtainHolderInfo().setFrom(viewHolder);
11369        }
11370
11371        /**
11372         * Called by the RecyclerView after the layout is complete. Item animator should record
11373         * necessary information about the View's final state.
11374         * <p>
11375         * The data returned from this method will be passed to the related <code>animate**</code>
11376         * methods.
11377         * <p>
11378         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
11379         * the View.
11380         *
11381         * @param state      The current State of RecyclerView which includes some useful data about
11382         *                   the layout that will be calculated.
11383         * @param viewHolder The ViewHolder whose information should be recorded.
11384         *
11385         * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
11386         * This object will be passed back to related <code>animate**</code> methods when
11387         * RecyclerView decides how items should be animated.
11388         *
11389         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11390         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11391         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11392         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11393         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11394         */
11395        public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
11396                @NonNull ViewHolder viewHolder) {
11397            return obtainHolderInfo().setFrom(viewHolder);
11398        }
11399
11400        /**
11401         * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
11402         * <p>
11403         * This means that the View was a child of the LayoutManager when layout started but has
11404         * been removed by the LayoutManager. It might have been removed from the adapter or simply
11405         * become invisible due to other factors. You can distinguish these two cases by checking
11406         * the change flags that were passed to
11407         * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11408         * <p>
11409         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
11410         * animation callback method which will be called by the RecyclerView depends on the
11411         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
11412         * LayoutManager's decision whether to layout the changed version of a disappearing
11413         * ViewHolder or not. RecyclerView will call
11414         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11415         * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
11416         * returns {@code false} from
11417         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
11418         * LayoutManager lays out a new disappearing view that holds the updated information.
11419         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
11420         * <p>
11421         * If LayoutManager supports predictive animations, it might provide a target disappear
11422         * location for the View by laying it out in that location. When that happens,
11423         * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
11424         * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
11425         * <p>
11426         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11427         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11428         * decides not to animate the view).
11429         *
11430         * @param viewHolder    The ViewHolder which should be animated
11431         * @param preLayoutInfo The information that was returned from
11432         *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11433         * @param postLayoutInfo The information that was returned from
11434         *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
11435         *                       null if the LayoutManager did not layout the item.
11436         *
11437         * @return true if a later call to {@link #runPendingAnimations()} is requested,
11438         * false otherwise.
11439         */
11440        public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
11441                @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
11442
11443        /**
11444         * Called by the RecyclerView when a ViewHolder is added to the layout.
11445         * <p>
11446         * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
11447         * but has  been added by the LayoutManager. It might be newly added to the adapter or
11448         * simply become visible due to other factors.
11449         * <p>
11450         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11451         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11452         * decides not to animate the view).
11453         *
11454         * @param viewHolder     The ViewHolder which should be animated
11455         * @param preLayoutInfo  The information that was returned from
11456         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11457         *                       Might be null if Item was just added to the adapter or
11458         *                       LayoutManager does not support predictive animations or it could
11459         *                       not predict that this ViewHolder will become visible.
11460         * @param postLayoutInfo The information that was returned from {@link
11461         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11462         *
11463         * @return true if a later call to {@link #runPendingAnimations()} is requested,
11464         * false otherwise.
11465         */
11466        public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
11467                @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11468
11469        /**
11470         * Called by the RecyclerView when a ViewHolder is present in both before and after the
11471         * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
11472         * for it or a {@link Adapter#notifyDataSetChanged()} call.
11473         * <p>
11474         * This ViewHolder still represents the same data that it was representing when the layout
11475         * started but its position / size may be changed by the LayoutManager.
11476         * <p>
11477         * If the Item's layout position didn't change, RecyclerView still calls this method because
11478         * it does not track this information (or does not necessarily know that an animation is
11479         * not required). Your ItemAnimator should handle this case and if there is nothing to
11480         * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
11481         * <code>false</code>.
11482         * <p>
11483         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11484         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11485         * decides not to animate the view).
11486         *
11487         * @param viewHolder     The ViewHolder which should be animated
11488         * @param preLayoutInfo  The information that was returned from
11489         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11490         * @param postLayoutInfo The information that was returned from {@link
11491         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11492         *
11493         * @return true if a later call to {@link #runPendingAnimations()} is requested,
11494         * false otherwise.
11495         */
11496        public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
11497                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11498
11499        /**
11500         * Called by the RecyclerView when an adapter item is present both before and after the
11501         * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
11502         * for it. This method may also be called when
11503         * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
11504         * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
11505         * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
11506         * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
11507         * called for the new ViewHolder and the old one will be recycled.
11508         * <p>
11509         * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
11510         * a good possibility that item contents didn't really change but it is rebound from the
11511         * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
11512         * screen didn't change and your animator should handle this case as well and avoid creating
11513         * unnecessary animations.
11514         * <p>
11515         * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
11516         * previous presentation of the item as-is and supply a new ViewHolder for the updated
11517         * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
11518         * This is useful if you don't know the contents of the Item and would like
11519         * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
11520         * <p>
11521         * When you are writing a custom item animator for your layout, it might be more performant
11522         * and elegant to re-use the same ViewHolder and animate the content changes manually.
11523         * <p>
11524         * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
11525         * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
11526         * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
11527         * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
11528         * which represent the same Item. In that case, only the new ViewHolder is visible
11529         * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
11530         * <p>
11531         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
11532         * ViewHolder when their animation is complete
11533         * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
11534         * animate the view).
11535         * <p>
11536         *  If oldHolder and newHolder are the same instance, you should call
11537         * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
11538         * <p>
11539         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
11540         * animation callback method which will be called by the RecyclerView depends on the
11541         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
11542         * LayoutManager's decision whether to layout the changed version of a disappearing
11543         * ViewHolder or not. RecyclerView will call
11544         * {@code animateChange} instead of
11545         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11546         * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
11547         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
11548         * LayoutManager lays out a new disappearing view that holds the updated information.
11549         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
11550         *
11551         * @param oldHolder     The ViewHolder before the layout is started, might be the same
11552         *                      instance with newHolder.
11553         * @param newHolder     The ViewHolder after the layout is finished, might be the same
11554         *                      instance with oldHolder.
11555         * @param preLayoutInfo  The information that was returned from
11556         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11557         * @param postLayoutInfo The information that was returned from {@link
11558         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11559         *
11560         * @return true if a later call to {@link #runPendingAnimations()} is requested,
11561         * false otherwise.
11562         */
11563        public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
11564                @NonNull ViewHolder newHolder,
11565                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11566
11567        @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
11568            int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
11569            if (viewHolder.isInvalid()) {
11570                return FLAG_INVALIDATED;
11571            }
11572            if ((flags & FLAG_INVALIDATED) == 0) {
11573                final int oldPos = viewHolder.getOldPosition();
11574                final int pos = viewHolder.getAdapterPosition();
11575                if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos){
11576                    flags |= FLAG_MOVED;
11577                }
11578            }
11579            return flags;
11580        }
11581
11582        /**
11583         * Called when there are pending animations waiting to be started. This state
11584         * is governed by the return values from
11585         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11586         * animateAppearance()},
11587         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11588         * animateChange()}
11589         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11590         * animatePersistence()}, and
11591         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11592         * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
11593         * called later to start the associated animations. runPendingAnimations() will be scheduled
11594         * to be run on the next frame.
11595         */
11596        abstract public void runPendingAnimations();
11597
11598        /**
11599         * Method called when an animation on a view should be ended immediately.
11600         * This could happen when other events, like scrolling, occur, so that
11601         * animating views can be quickly put into their proper end locations.
11602         * Implementations should ensure that any animations running on the item
11603         * are canceled and affected properties are set to their end values.
11604         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
11605         * animation since the animations are effectively done when this method is called.
11606         *
11607         * @param item The item for which an animation should be stopped.
11608         */
11609        abstract public void endAnimation(ViewHolder item);
11610
11611        /**
11612         * Method called when all item animations should be ended immediately.
11613         * This could happen when other events, like scrolling, occur, so that
11614         * animating views can be quickly put into their proper end locations.
11615         * Implementations should ensure that any animations running on any items
11616         * are canceled and affected properties are set to their end values.
11617         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
11618         * animation since the animations are effectively done when this method is called.
11619         */
11620        abstract public void endAnimations();
11621
11622        /**
11623         * Method which returns whether there are any item animations currently running.
11624         * This method can be used to determine whether to delay other actions until
11625         * animations end.
11626         *
11627         * @return true if there are any item animations currently running, false otherwise.
11628         */
11629        abstract public boolean isRunning();
11630
11631        /**
11632         * Method to be called by subclasses when an animation is finished.
11633         * <p>
11634         * For each call RecyclerView makes to
11635         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11636         * animateAppearance()},
11637         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11638         * animatePersistence()}, or
11639         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11640         * animateDisappearance()}, there
11641         * should
11642         * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
11643         * <p>
11644         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11645         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
11646         * and <code>newHolder</code>  (if they are not the same instance).
11647         *
11648         * @param viewHolder The ViewHolder whose animation is finished.
11649         * @see #onAnimationFinished(ViewHolder)
11650         */
11651        public final void dispatchAnimationFinished(ViewHolder viewHolder) {
11652            onAnimationFinished(viewHolder);
11653            if (mListener != null) {
11654                mListener.onAnimationFinished(viewHolder);
11655            }
11656        }
11657
11658        /**
11659         * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
11660         * ItemAnimator.
11661         *
11662         * @param viewHolder The ViewHolder whose animation is finished. There might still be other
11663         *                   animations running on this ViewHolder.
11664         * @see #dispatchAnimationFinished(ViewHolder)
11665         */
11666        public void onAnimationFinished(ViewHolder viewHolder) {
11667        }
11668
11669        /**
11670         * Method to be called by subclasses when an animation is started.
11671         * <p>
11672         * For each call RecyclerView makes to
11673         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11674         * animateAppearance()},
11675         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11676         * animatePersistence()}, or
11677         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11678         * animateDisappearance()}, there should be a matching
11679         * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
11680         * <p>
11681         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11682         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
11683         * and <code>newHolder</code> (if they are not the same instance).
11684         * <p>
11685         * If your ItemAnimator decides not to animate a ViewHolder, it should call
11686         * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
11687         * {@link #dispatchAnimationStarted(ViewHolder)}.
11688         *
11689         * @param viewHolder The ViewHolder whose animation is starting.
11690         * @see #onAnimationStarted(ViewHolder)
11691         */
11692        public final void dispatchAnimationStarted(ViewHolder viewHolder) {
11693            onAnimationStarted(viewHolder);
11694        }
11695
11696        /**
11697         * Called when a new animation is started on the given ViewHolder.
11698         *
11699         * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
11700         *                   might already be animating and this might be another animation.
11701         * @see #dispatchAnimationStarted(ViewHolder)
11702         */
11703        public void onAnimationStarted(ViewHolder viewHolder) {
11704
11705        }
11706
11707        /**
11708         * Like {@link #isRunning()}, this method returns whether there are any item
11709         * animations currently running. Additionally, the listener passed in will be called
11710         * when there are no item animations running, either immediately (before the method
11711         * returns) if no animations are currently running, or when the currently running
11712         * animations are {@link #dispatchAnimationsFinished() finished}.
11713         *
11714         * <p>Note that the listener is transient - it is either called immediately and not
11715         * stored at all, or stored only until it is called when running animations
11716         * are finished sometime later.</p>
11717         *
11718         * @param listener A listener to be called immediately if no animations are running
11719         * or later when currently-running animations have finished. A null listener is
11720         * equivalent to calling {@link #isRunning()}.
11721         * @return true if there are any item animations currently running, false otherwise.
11722         */
11723        public final boolean isRunning(ItemAnimatorFinishedListener listener) {
11724            boolean running = isRunning();
11725            if (listener != null) {
11726                if (!running) {
11727                    listener.onAnimationsFinished();
11728                } else {
11729                    mFinishedListeners.add(listener);
11730                }
11731            }
11732            return running;
11733        }
11734
11735        /**
11736         * When an item is changed, ItemAnimator can decide whether it wants to re-use
11737         * the same ViewHolder for animations or RecyclerView should create a copy of the
11738         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
11739         * <p>
11740         * Note that this method will only be called if the {@link ViewHolder} still has the same
11741         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
11742         * both {@link ViewHolder}s in the
11743         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
11744         * <p>
11745         * If your application is using change payloads, you can override
11746         * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
11747         *
11748         * @param viewHolder The ViewHolder which represents the changed item's old content.
11749         *
11750         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
11751         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
11752         *         ItemAnimator to animate. Default implementation returns <code>true</code>.
11753         *
11754         * @see #canReuseUpdatedViewHolder(ViewHolder, List)
11755         */
11756        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
11757            return true;
11758        }
11759
11760        /**
11761         * When an item is changed, ItemAnimator can decide whether it wants to re-use
11762         * the same ViewHolder for animations or RecyclerView should create a copy of the
11763         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
11764         * <p>
11765         * Note that this method will only be called if the {@link ViewHolder} still has the same
11766         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
11767         * both {@link ViewHolder}s in the
11768         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
11769         *
11770         * @param viewHolder The ViewHolder which represents the changed item's old content.
11771         * @param payloads A non-null list of merged payloads that were sent with change
11772         *                 notifications. Can be empty if the adapter is invalidated via
11773         *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
11774         *                 payloads will be passed into
11775         *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
11776         *                 method <b>if</b> this method returns <code>true</code>.
11777         *
11778         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
11779         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
11780         *         ItemAnimator to animate. Default implementation calls
11781         *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
11782         *
11783         * @see #canReuseUpdatedViewHolder(ViewHolder)
11784         */
11785        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
11786                @NonNull List<Object> payloads) {
11787            return canReuseUpdatedViewHolder(viewHolder);
11788        }
11789
11790        /**
11791         * This method should be called by ItemAnimator implementations to notify
11792         * any listeners that all pending and active item animations are finished.
11793         */
11794        public final void dispatchAnimationsFinished() {
11795            final int count = mFinishedListeners.size();
11796            for (int i = 0; i < count; ++i) {
11797                mFinishedListeners.get(i).onAnimationsFinished();
11798            }
11799            mFinishedListeners.clear();
11800        }
11801
11802        /**
11803         * Returns a new {@link ItemHolderInfo} which will be used to store information about the
11804         * ViewHolder. This information will later be passed into <code>animate**</code> methods.
11805         * <p>
11806         * You can override this method if you want to extend {@link ItemHolderInfo} and provide
11807         * your own instances.
11808         *
11809         * @return A new {@link ItemHolderInfo}.
11810         */
11811        public ItemHolderInfo obtainHolderInfo() {
11812            return new ItemHolderInfo();
11813        }
11814
11815        /**
11816         * The interface to be implemented by listeners to animation events from this
11817         * ItemAnimator. This is used internally and is not intended for developers to
11818         * create directly.
11819         */
11820        interface ItemAnimatorListener {
11821            void onAnimationFinished(ViewHolder item);
11822        }
11823
11824        /**
11825         * This interface is used to inform listeners when all pending or running animations
11826         * in an ItemAnimator are finished. This can be used, for example, to delay an action
11827         * in a data set until currently-running animations are complete.
11828         *
11829         * @see #isRunning(ItemAnimatorFinishedListener)
11830         */
11831        public interface ItemAnimatorFinishedListener {
11832            void onAnimationsFinished();
11833        }
11834
11835        /**
11836         * A simple data structure that holds information about an item's bounds.
11837         * This information is used in calculating item animations. Default implementation of
11838         * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
11839         * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
11840         * structure. You can extend this class if you would like to keep more information about
11841         * the Views.
11842         * <p>
11843         * If you want to provide your own implementation but still use `super` methods to record
11844         * basic information, you can override {@link #obtainHolderInfo()} to provide your own
11845         * instances.
11846         */
11847        public static class ItemHolderInfo {
11848
11849            /**
11850             * The left edge of the View (excluding decorations)
11851             */
11852            public int left;
11853
11854            /**
11855             * The top edge of the View (excluding decorations)
11856             */
11857            public int top;
11858
11859            /**
11860             * The right edge of the View (excluding decorations)
11861             */
11862            public int right;
11863
11864            /**
11865             * The bottom edge of the View (excluding decorations)
11866             */
11867            public int bottom;
11868
11869            /**
11870             * The change flags that were passed to
11871             * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
11872             */
11873            @AdapterChanges
11874            public int changeFlags;
11875
11876            public ItemHolderInfo() {
11877            }
11878
11879            /**
11880             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
11881             * the given ViewHolder. Clears all {@link #changeFlags}.
11882             *
11883             * @param holder The ViewHolder whose bounds should be copied.
11884             * @return This {@link ItemHolderInfo}
11885             */
11886            public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder) {
11887                return setFrom(holder, 0);
11888            }
11889
11890            /**
11891             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
11892             * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
11893             *
11894             * @param holder The ViewHolder whose bounds should be copied.
11895             * @param flags  The adapter change flags that were passed into
11896             *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
11897             *               List)}.
11898             * @return This {@link ItemHolderInfo}
11899             */
11900            public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder,
11901                    @AdapterChanges int flags) {
11902                final View view = holder.itemView;
11903                this.left = view.getLeft();
11904                this.top = view.getTop();
11905                this.right = view.getRight();
11906                this.bottom = view.getBottom();
11907                return this;
11908            }
11909        }
11910    }
11911
11912    @Override
11913    protected int getChildDrawingOrder(int childCount, int i) {
11914        if (mChildDrawingOrderCallback == null) {
11915            return super.getChildDrawingOrder(childCount, i);
11916        } else {
11917            return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
11918        }
11919    }
11920
11921    /**
11922     * A callback interface that can be used to alter the drawing order of RecyclerView children.
11923     * <p>
11924     * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
11925     * that applies to that method also applies to this callback. For example, changing the drawing
11926     * order of two views will not have any effect if their elevation values are different since
11927     * elevation overrides the result of this callback.
11928     */
11929    public interface ChildDrawingOrderCallback {
11930        /**
11931         * Returns the index of the child to draw for this iteration. Override this
11932         * if you want to change the drawing order of children. By default, it
11933         * returns i.
11934         *
11935         * @param i The current iteration.
11936         * @return The index of the child to draw this iteration.
11937         *
11938         * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
11939         */
11940        int onGetChildDrawingOrder(int childCount, int i);
11941    }
11942
11943    private NestedScrollingChildHelper getScrollingChildHelper() {
11944        if (mScrollingChildHelper == null) {
11945            mScrollingChildHelper = new NestedScrollingChildHelper(this);
11946        }
11947        return mScrollingChildHelper;
11948    }
11949}
11950