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