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