1/*
2 * Copyright (C) 2017 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
17package com.android.internal.widget;
18
19import android.annotation.CallSuper;
20import android.annotation.IntDef;
21import android.annotation.NonNull;
22import android.annotation.Nullable;
23import android.content.Context;
24import android.content.res.TypedArray;
25import android.database.Observable;
26import android.graphics.Canvas;
27import android.graphics.Matrix;
28import android.graphics.PointF;
29import android.graphics.Rect;
30import android.graphics.RectF;
31import android.os.Build;
32import android.os.Bundle;
33import android.os.Parcel;
34import android.os.Parcelable;
35import android.os.SystemClock;
36import android.os.Trace;
37import android.util.AttributeSet;
38import android.util.Log;
39import android.util.SparseArray;
40import android.util.TypedValue;
41import android.view.AbsSavedState;
42import android.view.Display;
43import android.view.FocusFinder;
44import android.view.InputDevice;
45import android.view.MotionEvent;
46import android.view.VelocityTracker;
47import android.view.View;
48import android.view.ViewConfiguration;
49import android.view.ViewGroup;
50import android.view.ViewParent;
51import android.view.accessibility.AccessibilityEvent;
52import android.view.accessibility.AccessibilityManager;
53import android.view.accessibility.AccessibilityNodeInfo;
54import android.view.animation.Interpolator;
55import android.widget.EdgeEffect;
56import android.widget.OverScroller;
57
58import com.android.internal.R;
59import com.android.internal.annotations.VisibleForTesting;
60import com.android.internal.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
61
62import java.lang.annotation.Retention;
63import java.lang.annotation.RetentionPolicy;
64import java.lang.ref.WeakReference;
65import java.lang.reflect.Constructor;
66import java.lang.reflect.InvocationTargetException;
67import java.util.ArrayList;
68import java.util.Collections;
69import java.util.List;
70
71/**
72 * A flexible view for providing a limited window into a large data set.
73 *
74 * <h3>Glossary of terms:</h3>
75 *
76 * <ul>
77 *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
78 *     that represent items in a data set.</li>
79 *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
80 *     <li><em>Index:</em> The index of an attached child view as used in a call to
81 *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
82 *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
83 *     to a <em>position</em> within the adapter.</li>
84 *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
85 *     position may be placed in a cache for later reuse to display the same type of data again
86 *     later. This can drastically improve performance by skipping initial layout inflation
87 *     or construction.</li>
88 *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
89 *     state during layout. Scrap views may be reused without becoming fully detached
90 *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
91 *     by the adapter if the view was considered <em>dirty</em>.</li>
92 *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
93 *     being displayed.</li>
94 * </ul>
95 *
96 * <h4>Positions in RecyclerView:</h4>
97 * <p>
98 * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
99 * {@link LayoutManager} to be able to detect data set changes in batches during a layout
100 * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
101 * It also helps with performance because all view bindings happen at the same time and unnecessary
102 * bindings are avoided.
103 * <p>
104 * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
105 * <ul>
106 *     <li>layout position: Position of an item in the latest layout calculation. This is the
107 *     position from the LayoutManager's perspective.</li>
108 *     <li>adapter position: Position of an item in the adapter. This is the position from
109 *     the Adapter's perspective.</li>
110 * </ul>
111 * <p>
112 * These two positions are the same except the time between dispatching <code>adapter.notify*
113 * </code> events and calculating the updated layout.
114 * <p>
115 * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
116 * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
117 * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
118 * last layout calculation. You can rely on these positions to be consistent with what user is
119 * currently seeing on the screen. For example, if you have a list of items on the screen and user
120 * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
121 * is seeing.
122 * <p>
123 * The other set of position related methods are in the form of
124 * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
125 * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
126 * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
127 * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
128 * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
129 * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
130 * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
131 * <code>null</code> results from these methods.
132 * <p>
133 * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
134 * writing an {@link Adapter}, you probably want to use adapter positions.
135 *
136 * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_layoutManager
137 */
138public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
139
140    static final String TAG = "RecyclerView";
141
142    static final boolean DEBUG = false;
143
144    private static final int[]  NESTED_SCROLLING_ATTRS = { android.R.attr.nestedScrollingEnabled };
145
146    private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
147
148    /**
149     * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
150     * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
151     * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
152     * recursively traverses itemView and invalidates display list for each ViewGroup that matches
153     * this criteria.
154     */
155    static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
156            || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
157    /**
158     * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
159     * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
160     * 0 when mode is unspecified.
161     */
162    static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
163
164    static final boolean POST_UPDATES_ON_ANIMATION = Build.VERSION.SDK_INT >= 16;
165
166    /**
167     * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
168     * RenderThread but before the next frame begins. We schedule prefetch work in this window.
169     */
170    private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
171
172    /**
173     * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
174     * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
175     */
176    private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
177
178    /**
179     * on API 15-, a focused child can still be considered a focused child of RV even after
180     * it's being removed or its focusable flag is set to false. This is because when this focused
181     * child is detached, the reference to this child is not removed in clearFocus. API 16 and above
182     * properly handle this case by calling ensureInputFocusOnFirstFocusable or rootViewRequestFocus
183     * to request focus on a new child, which will clear the focus on the old (detached) child as a
184     * side-effect.
185     */
186    private static final boolean IGNORE_DETACHED_FOCUSED_CHILD = Build.VERSION.SDK_INT <= 15;
187
188    static final boolean DISPATCH_TEMP_DETACH = false;
189    public static final int HORIZONTAL = 0;
190    public static final int VERTICAL = 1;
191
192    public static final int NO_POSITION = -1;
193    public static final long NO_ID = -1;
194    public static final int INVALID_TYPE = -1;
195
196    /**
197     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
198     * that the RecyclerView should use the standard touch slop for smooth,
199     * continuous scrolling.
200     */
201    public static final int TOUCH_SLOP_DEFAULT = 0;
202
203    /**
204     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
205     * that the RecyclerView should use the standard touch slop for scrolling
206     * widgets that snap to a page or other coarse-grained barrier.
207     */
208    public static final int TOUCH_SLOP_PAGING = 1;
209
210    static final int MAX_SCROLL_DURATION = 2000;
211
212    /**
213     * RecyclerView is calculating a scroll.
214     * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
215     * it. Try to avoid using EditText, focusable views or handle them with care.
216     */
217    static final String TRACE_SCROLL_TAG = "RV Scroll";
218
219    /**
220     * OnLayout has been called by the View system.
221     * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
222     * update themselves directly. This will cause a full re-layout but when it happens via the
223     * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
224     */
225    private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
226
227    /**
228     * NotifyDataSetChanged or equal has been called.
229     * If this is taking a long time, try sending granular notify adapter changes instead of just
230     * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
231     * might help.
232     */
233    private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
234
235    /**
236     * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
237     * If this is taking a long time, you may have dispatched too many Adapter updates causing too
238     * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
239     * methods.
240     */
241    private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
242
243    /**
244     * RecyclerView is rebinding a View.
245     * If this is taking a lot of time, consider optimizing your layout or make sure you are not
246     * doing extra operations in onBindViewHolder call.
247     */
248    static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
249
250    /**
251     * RecyclerView is attempting to pre-populate off screen views.
252     */
253    static final String TRACE_PREFETCH_TAG = "RV Prefetch";
254
255    /**
256     * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
257     * RecyclerView.
258     */
259    static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
260
261    /**
262     * RecyclerView is creating a new View.
263     * If too many of these present in Systrace:
264     * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
265     * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
266     * > Adapter#onFailedToRecycleView(ViewHolder)})
267     *
268     * - There might be too many item view types.
269     * > Try merging them
270     *
271     * - There might be too many itemChange animations and not enough space in RecyclerPool.
272     * >Try increasing your pool size and item cache size.
273     */
274    static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
275    private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
276            new Class[]{Context.class, AttributeSet.class, int.class, int.class};
277
278    private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
279
280    final Recycler mRecycler = new Recycler();
281
282    private SavedState mPendingSavedState;
283
284    /**
285     * Handles adapter updates
286     */
287    AdapterHelper mAdapterHelper;
288
289    /**
290     * Handles abstraction between LayoutManager children and RecyclerView children
291     */
292    ChildHelper mChildHelper;
293
294    /**
295     * Keeps data about views to be used for animations
296     */
297    final ViewInfoStore mViewInfoStore = new ViewInfoStore();
298
299    /**
300     * Prior to L, there is no way to query this variable which is why we override the setter and
301     * track it here.
302     */
303    boolean mClipToPadding;
304
305    /**
306     * Note: this Runnable is only ever posted if:
307     * 1) We've been through first layout
308     * 2) We know we have a fixed size (mHasFixedSize)
309     * 3) We're attached
310     */
311    final Runnable mUpdateChildViewsRunnable = new Runnable() {
312        @Override
313        public void run() {
314            if (!mFirstLayoutComplete || isLayoutRequested()) {
315                // a layout request will happen, we should not do layout here.
316                return;
317            }
318            if (!mIsAttached) {
319                requestLayout();
320                // if we are not attached yet, mark us as requiring layout and skip
321                return;
322            }
323            if (mLayoutFrozen) {
324                mLayoutRequestEaten = true;
325                return; //we'll process updates when ice age ends.
326            }
327            consumePendingUpdateOperations();
328        }
329    };
330
331    final Rect mTempRect = new Rect();
332    private final Rect mTempRect2 = new Rect();
333    final RectF mTempRectF = new RectF();
334    Adapter mAdapter;
335    @VisibleForTesting LayoutManager mLayout;
336    RecyclerListener mRecyclerListener;
337    final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
338    private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
339            new ArrayList<>();
340    private OnItemTouchListener mActiveOnItemTouchListener;
341    boolean mIsAttached;
342    boolean mHasFixedSize;
343    @VisibleForTesting boolean mFirstLayoutComplete;
344
345    // Counting lock to control whether we should ignore requestLayout calls from children or not.
346    private int mEatRequestLayout = 0;
347
348    boolean mLayoutRequestEaten;
349    boolean mLayoutFrozen;
350    private boolean mIgnoreMotionEventTillDown;
351
352    // binary OR of change events that were eaten during a layout or scroll.
353    private int mEatenAccessibilityChangeFlags;
354    boolean mAdapterUpdateDuringMeasure;
355
356    private final AccessibilityManager mAccessibilityManager;
357    private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
358
359    /**
360     * Set to true when an adapter data set changed notification is received.
361     * In that case, we cannot run any animations since we don't know what happened until layout.
362     *
363     * Attached items are invalid until next layout, at which point layout will animate/replace
364     * items as necessary, building up content from the (effectively) new adapter from scratch.
365     *
366     * Cached items must be discarded when setting this to true, so that the cache may be freely
367     * used by prefetching until the next layout occurs.
368     *
369     * @see #setDataSetChangedAfterLayout()
370     */
371    boolean mDataSetHasChangedAfterLayout = false;
372
373    /**
374     * This variable is incremented during a dispatchLayout and/or scroll.
375     * Some methods should not be called during these periods (e.g. adapter data change).
376     * Doing so will create hard to find bugs so we better check it and throw an exception.
377     *
378     * @see #assertInLayoutOrScroll(String)
379     * @see #assertNotInLayoutOrScroll(String)
380     */
381    private int mLayoutOrScrollCounter = 0;
382
383    /**
384     * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
385     * (for API compatibility).
386     * <p>
387     * It is a bad practice for a developer to update the data in a scroll callback since it is
388     * potentially called during a layout.
389     */
390    private int mDispatchScrollCounter = 0;
391
392    private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
393
394    ItemAnimator mItemAnimator = new DefaultItemAnimator();
395
396    private static final int INVALID_POINTER = -1;
397
398    /**
399     * The RecyclerView is not currently scrolling.
400     * @see #getScrollState()
401     */
402    public static final int SCROLL_STATE_IDLE = 0;
403
404    /**
405     * The RecyclerView is currently being dragged by outside input such as user touch input.
406     * @see #getScrollState()
407     */
408    public static final int SCROLL_STATE_DRAGGING = 1;
409
410    /**
411     * The RecyclerView is currently animating to a final position while not under
412     * outside control.
413     * @see #getScrollState()
414     */
415    public static final int SCROLL_STATE_SETTLING = 2;
416
417    static final long FOREVER_NS = Long.MAX_VALUE;
418
419    // Touch/scrolling handling
420
421    private int mScrollState = SCROLL_STATE_IDLE;
422    private int mScrollPointerId = INVALID_POINTER;
423    private VelocityTracker mVelocityTracker;
424    private int mInitialTouchX;
425    private int mInitialTouchY;
426    private int mLastTouchX;
427    private int mLastTouchY;
428    private int mTouchSlop;
429    private OnFlingListener mOnFlingListener;
430    private final int mMinFlingVelocity;
431    private final int mMaxFlingVelocity;
432    // This value is used when handling generic motion events.
433    private float mScrollFactor = Float.MIN_VALUE;
434    private boolean mPreserveFocusAfterLayout = true;
435
436    final ViewFlinger mViewFlinger = new ViewFlinger();
437
438    GapWorker mGapWorker;
439    GapWorker.LayoutPrefetchRegistryImpl mPrefetchRegistry =
440            ALLOW_THREAD_GAP_WORK ? new GapWorker.LayoutPrefetchRegistryImpl() : null;
441
442    final State mState = new State();
443
444    private OnScrollListener mScrollListener;
445    private List<OnScrollListener> mScrollListeners;
446
447    // For use in item animations
448    boolean mItemsAddedOrRemoved = false;
449    boolean mItemsChanged = false;
450    private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
451            new ItemAnimatorRestoreListener();
452    boolean mPostedAnimatorRunner = false;
453    RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
454    private ChildDrawingOrderCallback mChildDrawingOrderCallback;
455
456    // simple array to keep min and max child position during a layout calculation
457    // preserved not to create a new one in each layout pass
458    private final int[] mMinMaxLayoutPositions = new int[2];
459
460    private final int[] mScrollOffset = new int[2];
461    private final int[] mScrollConsumed = new int[2];
462    private final int[] mNestedOffsets = new int[2];
463
464    /**
465     * These are views that had their a11y importance changed during a layout. We defer these events
466     * until the end of the layout because a11y service may make sync calls back to the RV while
467     * the View's state is undefined.
468     */
469    @VisibleForTesting
470    final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList();
471
472    private Runnable mItemAnimatorRunner = new Runnable() {
473        @Override
474        public void run() {
475            if (mItemAnimator != null) {
476                mItemAnimator.runPendingAnimations();
477            }
478            mPostedAnimatorRunner = false;
479        }
480    };
481
482    static final Interpolator sQuinticInterpolator = new Interpolator() {
483        @Override
484        public float getInterpolation(float t) {
485            t -= 1.0f;
486            return t * t * t * t * t + 1.0f;
487        }
488    };
489
490    /**
491     * The callback to convert view info diffs into animations.
492     */
493    private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
494            new ViewInfoStore.ProcessCallback() {
495        @Override
496        public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
497                @Nullable ItemHolderInfo postInfo) {
498            mRecycler.unscrapView(viewHolder);
499            animateDisappearance(viewHolder, info, postInfo);
500        }
501        @Override
502        public void processAppeared(ViewHolder viewHolder,
503                ItemHolderInfo preInfo, ItemHolderInfo info) {
504            animateAppearance(viewHolder, preInfo, info);
505        }
506
507        @Override
508        public void processPersistent(ViewHolder viewHolder,
509                @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
510            viewHolder.setIsRecyclable(false);
511            if (mDataSetHasChangedAfterLayout) {
512                // since it was rebound, use change instead as we'll be mapping them from
513                // stable ids. If stable ids were false, we would not be running any
514                // animations
515                if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo, postInfo)) {
516                    postAnimationRunner();
517                }
518            } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
519                postAnimationRunner();
520            }
521        }
522        @Override
523        public void unused(ViewHolder viewHolder) {
524            mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
525        }
526    };
527
528    public RecyclerView(Context context) {
529        this(context, null);
530    }
531
532    public RecyclerView(Context context, @Nullable AttributeSet attrs) {
533        this(context, attrs, 0);
534    }
535
536    public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
537        super(context, attrs, defStyle);
538        if (attrs != null) {
539            TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
540            mClipToPadding = a.getBoolean(0, true);
541            a.recycle();
542        } else {
543            mClipToPadding = true;
544        }
545        setScrollContainer(true);
546        setFocusableInTouchMode(true);
547
548        final ViewConfiguration vc = ViewConfiguration.get(context);
549        mTouchSlop = vc.getScaledTouchSlop();
550        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
551        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
552        setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
553
554        mItemAnimator.setListener(mItemAnimatorListener);
555        initAdapterManager();
556        initChildrenHelper();
557        // If not explicitly specified this view is important for accessibility.
558        if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
559            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
560        }
561        mAccessibilityManager = (AccessibilityManager) getContext()
562                .getSystemService(Context.ACCESSIBILITY_SERVICE);
563        setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
564        // Create the layoutManager if specified.
565
566        boolean nestedScrollingEnabled = true;
567
568        if (attrs != null) {
569            int defStyleRes = 0;
570            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
571                    defStyle, defStyleRes);
572            String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
573            int descendantFocusability = a.getInt(
574                    R.styleable.RecyclerView_descendantFocusability, -1);
575            if (descendantFocusability == -1) {
576                setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
577            }
578            a.recycle();
579            createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
580
581            if (Build.VERSION.SDK_INT >= 21) {
582                a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
583                        defStyle, defStyleRes);
584                nestedScrollingEnabled = a.getBoolean(0, true);
585                a.recycle();
586            }
587        } else {
588            setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
589        }
590
591        // Re-set whether nested scrolling is enabled so that it is set on all API levels
592        setNestedScrollingEnabled(nestedScrollingEnabled);
593    }
594
595    /**
596     * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
597     * @return An instance of AccessibilityDelegateCompat used by RecyclerView
598     */
599    public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
600        return mAccessibilityDelegate;
601    }
602
603    /**
604     * Sets the accessibility delegate compatibility implementation used by RecyclerView.
605     * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
606     */
607    public void setAccessibilityDelegateCompat(
608            RecyclerViewAccessibilityDelegate accessibilityDelegate) {
609        mAccessibilityDelegate = accessibilityDelegate;
610        setAccessibilityDelegate(mAccessibilityDelegate);
611    }
612
613    /**
614     * Instantiate and set a LayoutManager, if specified in the attributes.
615     */
616    private void createLayoutManager(Context context, String className, AttributeSet attrs,
617            int defStyleAttr, int defStyleRes) {
618        if (className != null) {
619            className = className.trim();
620            if (className.length() != 0) {  // Can't use isEmpty since it was added in API 9.
621                className = getFullClassName(context, className);
622                try {
623                    ClassLoader classLoader;
624                    if (isInEditMode()) {
625                        // Stupid layoutlib cannot handle simple class loaders.
626                        classLoader = this.getClass().getClassLoader();
627                    } else {
628                        classLoader = context.getClassLoader();
629                    }
630                    Class<? extends LayoutManager> layoutManagerClass =
631                            classLoader.loadClass(className).asSubclass(LayoutManager.class);
632                    Constructor<? extends LayoutManager> constructor;
633                    Object[] constructorArgs = null;
634                    try {
635                        constructor = layoutManagerClass
636                                .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
637                        constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
638                    } catch (NoSuchMethodException e) {
639                        try {
640                            constructor = layoutManagerClass.getConstructor();
641                        } catch (NoSuchMethodException e1) {
642                            e1.initCause(e);
643                            throw new IllegalStateException(attrs.getPositionDescription()
644                                    + ": Error creating LayoutManager " + className, e1);
645                        }
646                    }
647                    constructor.setAccessible(true);
648                    setLayoutManager(constructor.newInstance(constructorArgs));
649                } catch (ClassNotFoundException e) {
650                    throw new IllegalStateException(attrs.getPositionDescription()
651                            + ": Unable to find LayoutManager " + className, e);
652                } catch (InvocationTargetException e) {
653                    throw new IllegalStateException(attrs.getPositionDescription()
654                            + ": Could not instantiate the LayoutManager: " + className, e);
655                } catch (InstantiationException e) {
656                    throw new IllegalStateException(attrs.getPositionDescription()
657                            + ": Could not instantiate the LayoutManager: " + className, e);
658                } catch (IllegalAccessException e) {
659                    throw new IllegalStateException(attrs.getPositionDescription()
660                            + ": Cannot access non-public constructor " + className, e);
661                } catch (ClassCastException e) {
662                    throw new IllegalStateException(attrs.getPositionDescription()
663                            + ": Class is not a LayoutManager " + className, e);
664                }
665            }
666        }
667    }
668
669    private String getFullClassName(Context context, String className) {
670        if (className.charAt(0) == '.') {
671            return context.getPackageName() + className;
672        }
673        if (className.contains(".")) {
674            return className;
675        }
676        return RecyclerView.class.getPackage().getName() + '.' + className;
677    }
678
679    private void initChildrenHelper() {
680        mChildHelper = new ChildHelper(new ChildHelper.Callback() {
681            @Override
682            public int getChildCount() {
683                return RecyclerView.this.getChildCount();
684            }
685
686            @Override
687            public void addView(View child, int index) {
688                RecyclerView.this.addView(child, index);
689                dispatchChildAttached(child);
690            }
691
692            @Override
693            public int indexOfChild(View view) {
694                return RecyclerView.this.indexOfChild(view);
695            }
696
697            @Override
698            public void removeViewAt(int index) {
699                final View child = RecyclerView.this.getChildAt(index);
700                if (child != null) {
701                    dispatchChildDetached(child);
702                }
703                RecyclerView.this.removeViewAt(index);
704            }
705
706            @Override
707            public View getChildAt(int offset) {
708                return RecyclerView.this.getChildAt(offset);
709            }
710
711            @Override
712            public void removeAllViews() {
713                final int count = getChildCount();
714                for (int i = 0; i < count; i++) {
715                    dispatchChildDetached(getChildAt(i));
716                }
717                RecyclerView.this.removeAllViews();
718            }
719
720            @Override
721            public ViewHolder getChildViewHolder(View view) {
722                return getChildViewHolderInt(view);
723            }
724
725            @Override
726            public void attachViewToParent(View child, int index,
727                    ViewGroup.LayoutParams layoutParams) {
728                final ViewHolder vh = getChildViewHolderInt(child);
729                if (vh != null) {
730                    if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
731                        throw new IllegalArgumentException("Called attach on a child which is not"
732                                + " detached: " + vh);
733                    }
734                    if (DEBUG) {
735                        Log.d(TAG, "reAttach " + vh);
736                    }
737                    vh.clearTmpDetachFlag();
738                }
739                RecyclerView.this.attachViewToParent(child, index, layoutParams);
740            }
741
742            @Override
743            public void detachViewFromParent(int offset) {
744                final View view = getChildAt(offset);
745                if (view != null) {
746                    final ViewHolder vh = getChildViewHolderInt(view);
747                    if (vh != null) {
748                        if (vh.isTmpDetached() && !vh.shouldIgnore()) {
749                            throw new IllegalArgumentException("called detach on an already"
750                                    + " detached child " + vh);
751                        }
752                        if (DEBUG) {
753                            Log.d(TAG, "tmpDetach " + vh);
754                        }
755                        vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
756                    }
757                }
758                RecyclerView.this.detachViewFromParent(offset);
759            }
760
761            @Override
762            public void onEnteredHiddenState(View child) {
763                final ViewHolder vh = getChildViewHolderInt(child);
764                if (vh != null) {
765                    vh.onEnteredHiddenState(RecyclerView.this);
766                }
767            }
768
769            @Override
770            public void onLeftHiddenState(View child) {
771                final ViewHolder vh = getChildViewHolderInt(child);
772                if (vh != null) {
773                    vh.onLeftHiddenState(RecyclerView.this);
774                }
775            }
776        });
777    }
778
779    void initAdapterManager() {
780        mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
781            @Override
782            public ViewHolder findViewHolder(int position) {
783                final ViewHolder vh = findViewHolderForPosition(position, true);
784                if (vh == null) {
785                    return null;
786                }
787                // ensure it is not hidden because for adapter helper, the only thing matter is that
788                // LM thinks view is a child.
789                if (mChildHelper.isHidden(vh.itemView)) {
790                    if (DEBUG) {
791                        Log.d(TAG, "assuming view holder cannot be find because it is hidden");
792                    }
793                    return null;
794                }
795                return vh;
796            }
797
798            @Override
799            public void offsetPositionsForRemovingInvisible(int start, int count) {
800                offsetPositionRecordsForRemove(start, count, true);
801                mItemsAddedOrRemoved = true;
802                mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
803            }
804
805            @Override
806            public void offsetPositionsForRemovingLaidOutOrNewView(
807                    int positionStart, int itemCount) {
808                offsetPositionRecordsForRemove(positionStart, itemCount, false);
809                mItemsAddedOrRemoved = true;
810            }
811
812            @Override
813            public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
814                viewRangeUpdate(positionStart, itemCount, payload);
815                mItemsChanged = true;
816            }
817
818            @Override
819            public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
820                dispatchUpdate(op);
821            }
822
823            void dispatchUpdate(AdapterHelper.UpdateOp op) {
824                switch (op.cmd) {
825                    case AdapterHelper.UpdateOp.ADD:
826                        mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
827                        break;
828                    case AdapterHelper.UpdateOp.REMOVE:
829                        mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
830                        break;
831                    case AdapterHelper.UpdateOp.UPDATE:
832                        mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
833                                op.payload);
834                        break;
835                    case AdapterHelper.UpdateOp.MOVE:
836                        mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
837                        break;
838                }
839            }
840
841            @Override
842            public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
843                dispatchUpdate(op);
844            }
845
846            @Override
847            public void offsetPositionsForAdd(int positionStart, int itemCount) {
848                offsetPositionRecordsForInsert(positionStart, itemCount);
849                mItemsAddedOrRemoved = true;
850            }
851
852            @Override
853            public void offsetPositionsForMove(int from, int to) {
854                offsetPositionRecordsForMove(from, to);
855                // should we create mItemsMoved ?
856                mItemsAddedOrRemoved = true;
857            }
858        });
859    }
860
861    /**
862     * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
863     * size is not affected by the adapter contents. RecyclerView can still change its size based
864     * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
865     * size of its children or contents of its adapter (except the number of items in the adapter).
866     * <p>
867     * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
868     * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
869     *
870     * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
871     */
872    public void setHasFixedSize(boolean hasFixedSize) {
873        mHasFixedSize = hasFixedSize;
874    }
875
876    /**
877     * @return true if the app has specified that changes in adapter content cannot change
878     * the size of the RecyclerView itself.
879     */
880    public boolean hasFixedSize() {
881        return mHasFixedSize;
882    }
883
884    @Override
885    public void setClipToPadding(boolean clipToPadding) {
886        if (clipToPadding != mClipToPadding) {
887            invalidateGlows();
888        }
889        mClipToPadding = clipToPadding;
890        super.setClipToPadding(clipToPadding);
891        if (mFirstLayoutComplete) {
892            requestLayout();
893        }
894    }
895
896    /**
897     * Returns whether this RecyclerView will clip its children to its padding, and resize (but
898     * not clip) any EdgeEffect to the padded region, if padding is present.
899     * <p>
900     * By default, children are clipped to the padding of their parent
901     * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
902     *
903     * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
904     *         clip) any EdgeEffect to the padded region, false otherwise.
905     *
906     * @attr name android:clipToPadding
907     */
908    @Override
909    public boolean getClipToPadding() {
910        return mClipToPadding;
911    }
912
913    /**
914     * Configure the scrolling touch slop for a specific use case.
915     *
916     * Set up the RecyclerView's scrolling motion threshold based on common usages.
917     * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
918     *
919     * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
920     *                     the intended usage of this RecyclerView
921     */
922    public void setScrollingTouchSlop(int slopConstant) {
923        final ViewConfiguration vc = ViewConfiguration.get(getContext());
924        switch (slopConstant) {
925            default:
926                Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
927                        + slopConstant + "; using default value");
928                // fall-through
929            case TOUCH_SLOP_DEFAULT:
930                mTouchSlop = vc.getScaledTouchSlop();
931                break;
932
933            case TOUCH_SLOP_PAGING:
934                mTouchSlop = vc.getScaledPagingTouchSlop();
935                break;
936        }
937    }
938
939    /**
940     * Swaps the current adapter with the provided one. It is similar to
941     * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
942     * {@link ViewHolder} and does not clear the RecycledViewPool.
943     * <p>
944     * Note that it still calls onAdapterChanged callbacks.
945     *
946     * @param adapter The new adapter to set, or null to set no adapter.
947     * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
948     *                                      Views. If adapters have stable ids and/or you want to
949     *                                      animate the disappearing views, you may prefer to set
950     *                                      this to false.
951     * @see #setAdapter(Adapter)
952     */
953    public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
954        // bail out if layout is frozen
955        setLayoutFrozen(false);
956        setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
957        setDataSetChangedAfterLayout();
958        requestLayout();
959    }
960    /**
961     * Set a new adapter to provide child views on demand.
962     * <p>
963     * When adapter is changed, all existing views are recycled back to the pool. If the pool has
964     * only one adapter, it will be cleared.
965     *
966     * @param adapter The new adapter to set, or null to set no adapter.
967     * @see #swapAdapter(Adapter, boolean)
968     */
969    public void setAdapter(Adapter adapter) {
970        // bail out if layout is frozen
971        setLayoutFrozen(false);
972        setAdapterInternal(adapter, false, true);
973        requestLayout();
974    }
975
976    /**
977     * Removes and recycles all views - both those currently attached, and those in the Recycler.
978     */
979    void removeAndRecycleViews() {
980        // end all running animations
981        if (mItemAnimator != null) {
982            mItemAnimator.endAnimations();
983        }
984        // Since animations are ended, mLayout.children should be equal to
985        // recyclerView.children. This may not be true if item animator's end does not work as
986        // expected. (e.g. not release children instantly). It is safer to use mLayout's child
987        // count.
988        if (mLayout != null) {
989            mLayout.removeAndRecycleAllViews(mRecycler);
990            mLayout.removeAndRecycleScrapInt(mRecycler);
991        }
992        // we should clear it here before adapters are swapped to ensure correct callbacks.
993        mRecycler.clear();
994    }
995
996    /**
997     * Replaces the current adapter with the new one and triggers listeners.
998     * @param adapter The new adapter
999     * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
1000     *                               item types with the current adapter (helps us avoid cache
1001     *                               invalidation).
1002     * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
1003     *                               compatibleWithPrevious is false, this parameter is ignored.
1004     */
1005    private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
1006            boolean removeAndRecycleViews) {
1007        if (mAdapter != null) {
1008            mAdapter.unregisterAdapterDataObserver(mObserver);
1009            mAdapter.onDetachedFromRecyclerView(this);
1010        }
1011        if (!compatibleWithPrevious || removeAndRecycleViews) {
1012            removeAndRecycleViews();
1013        }
1014        mAdapterHelper.reset();
1015        final Adapter oldAdapter = mAdapter;
1016        mAdapter = adapter;
1017        if (adapter != null) {
1018            adapter.registerAdapterDataObserver(mObserver);
1019            adapter.onAttachedToRecyclerView(this);
1020        }
1021        if (mLayout != null) {
1022            mLayout.onAdapterChanged(oldAdapter, mAdapter);
1023        }
1024        mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
1025        mState.mStructureChanged = true;
1026        markKnownViewsInvalid();
1027    }
1028
1029    /**
1030     * Retrieves the previously set adapter or null if no adapter is set.
1031     *
1032     * @return The previously set adapter
1033     * @see #setAdapter(Adapter)
1034     */
1035    public Adapter getAdapter() {
1036        return mAdapter;
1037    }
1038
1039    /**
1040     * Register a listener that will be notified whenever a child view is recycled.
1041     *
1042     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1043     * that a child view is no longer needed. If an application associates expensive
1044     * or heavyweight data with item views, this may be a good place to release
1045     * or free those resources.</p>
1046     *
1047     * @param listener Listener to register, or null to clear
1048     */
1049    public void setRecyclerListener(RecyclerListener listener) {
1050        mRecyclerListener = listener;
1051    }
1052
1053    /**
1054     * <p>Return the offset of the RecyclerView's text baseline from the its top
1055     * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
1056     * this method returns -1.</p>
1057     *
1058     * @return the offset of the baseline within the RecyclerView's bounds or -1
1059     *         if baseline alignment is not supported
1060     */
1061    @Override
1062    public int getBaseline() {
1063        if (mLayout != null) {
1064            return mLayout.getBaseline();
1065        } else {
1066            return super.getBaseline();
1067        }
1068    }
1069
1070    /**
1071     * Register a listener that will be notified whenever a child view is attached to or detached
1072     * from RecyclerView.
1073     *
1074     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1075     * that a child view is no longer needed. If an application associates expensive
1076     * or heavyweight data with item views, this may be a good place to release
1077     * or free those resources.</p>
1078     *
1079     * @param listener Listener to register
1080     */
1081    public void addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1082        if (mOnChildAttachStateListeners == null) {
1083            mOnChildAttachStateListeners = new ArrayList<>();
1084        }
1085        mOnChildAttachStateListeners.add(listener);
1086    }
1087
1088    /**
1089     * Removes the provided listener from child attached state listeners list.
1090     *
1091     * @param listener Listener to unregister
1092     */
1093    public void removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1094        if (mOnChildAttachStateListeners == null) {
1095            return;
1096        }
1097        mOnChildAttachStateListeners.remove(listener);
1098    }
1099
1100    /**
1101     * Removes all listeners that were added via
1102     * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
1103     */
1104    public void clearOnChildAttachStateChangeListeners() {
1105        if (mOnChildAttachStateListeners != null) {
1106            mOnChildAttachStateListeners.clear();
1107        }
1108    }
1109
1110    /**
1111     * Set the {@link LayoutManager} that this RecyclerView will use.
1112     *
1113     * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
1114     * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
1115     * layout arrangements for child views. These arrangements are controlled by the
1116     * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
1117     *
1118     * <p>Several default strategies are provided for common uses such as lists and grids.</p>
1119     *
1120     * @param layout LayoutManager to use
1121     */
1122    public void setLayoutManager(LayoutManager layout) {
1123        if (layout == mLayout) {
1124            return;
1125        }
1126        stopScroll();
1127        // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
1128        // chance that LayoutManagers will re-use views.
1129        if (mLayout != null) {
1130            // end all running animations
1131            if (mItemAnimator != null) {
1132                mItemAnimator.endAnimations();
1133            }
1134            mLayout.removeAndRecycleAllViews(mRecycler);
1135            mLayout.removeAndRecycleScrapInt(mRecycler);
1136            mRecycler.clear();
1137
1138            if (mIsAttached) {
1139                mLayout.dispatchDetachedFromWindow(this, mRecycler);
1140            }
1141            mLayout.setRecyclerView(null);
1142            mLayout = null;
1143        } else {
1144            mRecycler.clear();
1145        }
1146        // this is just a defensive measure for faulty item animators.
1147        mChildHelper.removeAllViewsUnfiltered();
1148        mLayout = layout;
1149        if (layout != null) {
1150            if (layout.mRecyclerView != null) {
1151                throw new IllegalArgumentException("LayoutManager " + layout
1152                        + " is already attached to a RecyclerView: " + layout.mRecyclerView);
1153            }
1154            mLayout.setRecyclerView(this);
1155            if (mIsAttached) {
1156                mLayout.dispatchAttachedToWindow(this);
1157            }
1158        }
1159        mRecycler.updateViewCacheSize();
1160        requestLayout();
1161    }
1162
1163    /**
1164     * Set a {@link OnFlingListener} for this {@link RecyclerView}.
1165     * <p>
1166     * If the {@link OnFlingListener} is set then it will receive
1167     * calls to {@link #fling(int,int)} and will be able to intercept them.
1168     *
1169     * @param onFlingListener The {@link OnFlingListener} instance.
1170     */
1171    public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
1172        mOnFlingListener = onFlingListener;
1173    }
1174
1175    /**
1176     * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
1177     *
1178     * @return The {@link OnFlingListener} instance currently set (can be null).
1179     */
1180    @Nullable
1181    public OnFlingListener getOnFlingListener() {
1182        return mOnFlingListener;
1183    }
1184
1185    @Override
1186    protected Parcelable onSaveInstanceState() {
1187        SavedState state = new SavedState(super.onSaveInstanceState());
1188        if (mPendingSavedState != null) {
1189            state.copyFrom(mPendingSavedState);
1190        } else if (mLayout != null) {
1191            state.mLayoutState = mLayout.onSaveInstanceState();
1192        } else {
1193            state.mLayoutState = null;
1194        }
1195
1196        return state;
1197    }
1198
1199    @Override
1200    protected void onRestoreInstanceState(Parcelable state) {
1201        if (!(state instanceof SavedState)) {
1202            super.onRestoreInstanceState(state);
1203            return;
1204        }
1205
1206        mPendingSavedState = (SavedState) state;
1207        super.onRestoreInstanceState(mPendingSavedState.getSuperState());
1208        if (mLayout != null && mPendingSavedState.mLayoutState != null) {
1209            mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
1210        }
1211    }
1212
1213    /**
1214     * Override to prevent freezing of any views created by the adapter.
1215     */
1216    @Override
1217    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1218        dispatchFreezeSelfOnly(container);
1219    }
1220
1221    /**
1222     * Override to prevent thawing of any views created by the adapter.
1223     */
1224    @Override
1225    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1226        dispatchThawSelfOnly(container);
1227    }
1228
1229    /**
1230     * Adds a view to the animatingViews list.
1231     * mAnimatingViews holds the child views that are currently being kept around
1232     * purely for the purpose of being animated out of view. They are drawn as a regular
1233     * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
1234     * as they are managed separately from the regular child views.
1235     * @param viewHolder The ViewHolder to be removed
1236     */
1237    private void addAnimatingView(ViewHolder viewHolder) {
1238        final View view = viewHolder.itemView;
1239        final boolean alreadyParented = view.getParent() == this;
1240        mRecycler.unscrapView(getChildViewHolder(view));
1241        if (viewHolder.isTmpDetached()) {
1242            // re-attach
1243            mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
1244        } else if (!alreadyParented) {
1245            mChildHelper.addView(view, true);
1246        } else {
1247            mChildHelper.hide(view);
1248        }
1249    }
1250
1251    /**
1252     * Removes a view from the animatingViews list.
1253     * @param view The view to be removed
1254     * @see #addAnimatingView(RecyclerView.ViewHolder)
1255     * @return true if an animating view is removed
1256     */
1257    boolean removeAnimatingView(View view) {
1258        eatRequestLayout();
1259        final boolean removed = mChildHelper.removeViewIfHidden(view);
1260        if (removed) {
1261            final ViewHolder viewHolder = getChildViewHolderInt(view);
1262            mRecycler.unscrapView(viewHolder);
1263            mRecycler.recycleViewHolderInternal(viewHolder);
1264            if (DEBUG) {
1265                Log.d(TAG, "after removing animated view: " + view + ", " + this);
1266            }
1267        }
1268        // only clear request eaten flag if we removed the view.
1269        resumeRequestLayout(!removed);
1270        return removed;
1271    }
1272
1273    /**
1274     * Return the {@link LayoutManager} currently responsible for
1275     * layout policy for this RecyclerView.
1276     *
1277     * @return The currently bound LayoutManager
1278     */
1279    public LayoutManager getLayoutManager() {
1280        return mLayout;
1281    }
1282
1283    /**
1284     * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
1285     * if no pool is set for this view a new one will be created. See
1286     * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
1287     *
1288     * @return The pool used to store recycled item views for reuse.
1289     * @see #setRecycledViewPool(RecycledViewPool)
1290     */
1291    public RecycledViewPool getRecycledViewPool() {
1292        return mRecycler.getRecycledViewPool();
1293    }
1294
1295    /**
1296     * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
1297     * This can be useful if you have multiple RecyclerViews with adapters that use the same
1298     * view types, for example if you have several data sets with the same kinds of item views
1299     * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
1300     *
1301     * @param pool Pool to set. If this parameter is null a new pool will be created and used.
1302     */
1303    public void setRecycledViewPool(RecycledViewPool pool) {
1304        mRecycler.setRecycledViewPool(pool);
1305    }
1306
1307    /**
1308     * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
1309     *
1310     * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
1311     *
1312     * @see {@link ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)}
1313     */
1314    public void setViewCacheExtension(ViewCacheExtension extension) {
1315        mRecycler.setViewCacheExtension(extension);
1316    }
1317
1318    /**
1319     * Set the number of offscreen views to retain before adding them to the potentially shared
1320     * {@link #getRecycledViewPool() recycled view pool}.
1321     *
1322     * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
1323     * a LayoutManager to reuse those views unmodified without needing to return to the adapter
1324     * to rebind them.</p>
1325     *
1326     * @param size Number of views to cache offscreen before returning them to the general
1327     *             recycled view pool
1328     */
1329    public void setItemViewCacheSize(int size) {
1330        mRecycler.setViewCacheSize(size);
1331    }
1332
1333    /**
1334     * Return the current scrolling state of the RecyclerView.
1335     *
1336     * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
1337     * {@link #SCROLL_STATE_SETTLING}
1338     */
1339    public int getScrollState() {
1340        return mScrollState;
1341    }
1342
1343    void setScrollState(int state) {
1344        if (state == mScrollState) {
1345            return;
1346        }
1347        if (DEBUG) {
1348            Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
1349                    new Exception());
1350        }
1351        mScrollState = state;
1352        if (state != SCROLL_STATE_SETTLING) {
1353            stopScrollersInternal();
1354        }
1355        dispatchOnScrollStateChanged(state);
1356    }
1357
1358    /**
1359     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1360     * affect both measurement and drawing of individual item views.
1361     *
1362     * <p>Item decorations are ordered. Decorations placed earlier in the list will
1363     * be run/queried/drawn first for their effects on item views. Padding added to views
1364     * will be nested; a padding added by an earlier decoration will mean further
1365     * item decorations in the list will be asked to draw/pad within the previous decoration's
1366     * given area.</p>
1367     *
1368     * @param decor Decoration to add
1369     * @param index Position in the decoration chain to insert this decoration at. If this value
1370     *              is negative the decoration will be added at the end.
1371     */
1372    public void addItemDecoration(ItemDecoration decor, int index) {
1373        if (mLayout != null) {
1374            mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
1375                    + " layout");
1376        }
1377        if (mItemDecorations.isEmpty()) {
1378            setWillNotDraw(false);
1379        }
1380        if (index < 0) {
1381            mItemDecorations.add(decor);
1382        } else {
1383            mItemDecorations.add(index, decor);
1384        }
1385        markItemDecorInsetsDirty();
1386        requestLayout();
1387    }
1388
1389    /**
1390     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1391     * affect both measurement and drawing of individual item views.
1392     *
1393     * <p>Item decorations are ordered. Decorations placed earlier in the list will
1394     * be run/queried/drawn first for their effects on item views. Padding added to views
1395     * will be nested; a padding added by an earlier decoration will mean further
1396     * item decorations in the list will be asked to draw/pad within the previous decoration's
1397     * given area.</p>
1398     *
1399     * @param decor Decoration to add
1400     */
1401    public void addItemDecoration(ItemDecoration decor) {
1402        addItemDecoration(decor, -1);
1403    }
1404
1405    /**
1406     * Remove an {@link ItemDecoration} from this RecyclerView.
1407     *
1408     * <p>The given decoration will no longer impact the measurement and drawing of
1409     * item views.</p>
1410     *
1411     * @param decor Decoration to remove
1412     * @see #addItemDecoration(ItemDecoration)
1413     */
1414    public void removeItemDecoration(ItemDecoration decor) {
1415        if (mLayout != null) {
1416            mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
1417                    + " layout");
1418        }
1419        mItemDecorations.remove(decor);
1420        if (mItemDecorations.isEmpty()) {
1421            setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
1422        }
1423        markItemDecorInsetsDirty();
1424        requestLayout();
1425    }
1426
1427    /**
1428     * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
1429     * <p>
1430     * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
1431     * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
1432     * true if childDrawingOrderCallback is not null, false otherwise.
1433     * <p>
1434     * Note that child drawing order may be overridden by View's elevation.
1435     *
1436     * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
1437     *                                  system.
1438     */
1439    public void setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback) {
1440        if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
1441            return;
1442        }
1443        mChildDrawingOrderCallback = childDrawingOrderCallback;
1444        setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
1445    }
1446
1447    /**
1448     * Set a listener that will be notified of any changes in scroll state or position.
1449     *
1450     * @param listener Listener to set or null to clear
1451     *
1452     * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
1453     *             {@link #removeOnScrollListener(OnScrollListener)}
1454     */
1455    @Deprecated
1456    public void setOnScrollListener(OnScrollListener listener) {
1457        mScrollListener = listener;
1458    }
1459
1460    /**
1461     * Add a listener that will be notified of any changes in scroll state or position.
1462     *
1463     * <p>Components that add a listener should take care to remove it when finished.
1464     * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
1465     * to remove all attached listeners.</p>
1466     *
1467     * @param listener listener to set or null to clear
1468     */
1469    public void addOnScrollListener(OnScrollListener listener) {
1470        if (mScrollListeners == null) {
1471            mScrollListeners = new ArrayList<>();
1472        }
1473        mScrollListeners.add(listener);
1474    }
1475
1476    /**
1477     * Remove a listener that was notified of any changes in scroll state or position.
1478     *
1479     * @param listener listener to set or null to clear
1480     */
1481    public void removeOnScrollListener(OnScrollListener listener) {
1482        if (mScrollListeners != null) {
1483            mScrollListeners.remove(listener);
1484        }
1485    }
1486
1487    /**
1488     * Remove all secondary listener that were notified of any changes in scroll state or position.
1489     */
1490    public void clearOnScrollListeners() {
1491        if (mScrollListeners != null) {
1492            mScrollListeners.clear();
1493        }
1494    }
1495
1496    /**
1497     * Convenience method to scroll to a certain position.
1498     *
1499     * RecyclerView does not implement scrolling logic, rather forwards the call to
1500     * {@link com.android.internal.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
1501     * @param position Scroll to this adapter position
1502     * @see com.android.internal.widget.RecyclerView.LayoutManager#scrollToPosition(int)
1503     */
1504    public void scrollToPosition(int position) {
1505        if (mLayoutFrozen) {
1506            return;
1507        }
1508        stopScroll();
1509        if (mLayout == null) {
1510            Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
1511                    + "Call setLayoutManager with a non-null argument.");
1512            return;
1513        }
1514        mLayout.scrollToPosition(position);
1515        awakenScrollBars();
1516    }
1517
1518    void jumpToPositionForSmoothScroller(int position) {
1519        if (mLayout == null) {
1520            return;
1521        }
1522        mLayout.scrollToPosition(position);
1523        awakenScrollBars();
1524    }
1525
1526    /**
1527     * Starts a smooth scroll to an adapter position.
1528     * <p>
1529     * To support smooth scrolling, you must override
1530     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
1531     * {@link SmoothScroller}.
1532     * <p>
1533     * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
1534     * provide a custom smooth scroll logic, override
1535     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
1536     * LayoutManager.
1537     *
1538     * @param position The adapter position to scroll to
1539     * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
1540     */
1541    public void smoothScrollToPosition(int position) {
1542        if (mLayoutFrozen) {
1543            return;
1544        }
1545        if (mLayout == null) {
1546            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
1547                    + "Call setLayoutManager with a non-null argument.");
1548            return;
1549        }
1550        mLayout.smoothScrollToPosition(this, mState, position);
1551    }
1552
1553    @Override
1554    public void scrollTo(int x, int y) {
1555        Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
1556                + "Use scrollToPosition instead");
1557    }
1558
1559    @Override
1560    public void scrollBy(int x, int y) {
1561        if (mLayout == null) {
1562            Log.e(TAG, "Cannot scroll without a LayoutManager set. "
1563                    + "Call setLayoutManager with a non-null argument.");
1564            return;
1565        }
1566        if (mLayoutFrozen) {
1567            return;
1568        }
1569        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1570        final boolean canScrollVertical = mLayout.canScrollVertically();
1571        if (canScrollHorizontal || canScrollVertical) {
1572            scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
1573        }
1574    }
1575
1576    /**
1577     * Helper method reflect data changes to the state.
1578     * <p>
1579     * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
1580     * but data actually changed.
1581     * <p>
1582     * This method consumes all deferred changes to avoid that case.
1583     */
1584    void consumePendingUpdateOperations() {
1585        if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
1586            Trace.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1587            dispatchLayout();
1588            Trace.endSection();
1589            return;
1590        }
1591        if (!mAdapterHelper.hasPendingUpdates()) {
1592            return;
1593        }
1594
1595        // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
1596        // of the visible items is affected and if not, just ignore the change.
1597        if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
1598                .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
1599                        | AdapterHelper.UpdateOp.MOVE)) {
1600            Trace.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
1601            eatRequestLayout();
1602            onEnterLayoutOrScroll();
1603            mAdapterHelper.preProcess();
1604            if (!mLayoutRequestEaten) {
1605                if (hasUpdatedView()) {
1606                    dispatchLayout();
1607                } else {
1608                    // no need to layout, clean state
1609                    mAdapterHelper.consumePostponedUpdates();
1610                }
1611            }
1612            resumeRequestLayout(true);
1613            onExitLayoutOrScroll();
1614            Trace.endSection();
1615        } else if (mAdapterHelper.hasPendingUpdates()) {
1616            Trace.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1617            dispatchLayout();
1618            Trace.endSection();
1619        }
1620    }
1621
1622    /**
1623     * @return True if an existing view holder needs to be updated
1624     */
1625    private boolean hasUpdatedView() {
1626        final int childCount = mChildHelper.getChildCount();
1627        for (int i = 0; i < childCount; i++) {
1628            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
1629            if (holder == null || holder.shouldIgnore()) {
1630                continue;
1631            }
1632            if (holder.isUpdated()) {
1633                return true;
1634            }
1635        }
1636        return false;
1637    }
1638
1639    /**
1640     * Does not perform bounds checking. Used by internal methods that have already validated input.
1641     * <p>
1642     * It also reports any unused scroll request to the related EdgeEffect.
1643     *
1644     * @param x The amount of horizontal scroll request
1645     * @param y The amount of vertical scroll request
1646     * @param ev The originating MotionEvent, or null if not from a touch event.
1647     *
1648     * @return Whether any scroll was consumed in either direction.
1649     */
1650    boolean scrollByInternal(int x, int y, MotionEvent ev) {
1651        int unconsumedX = 0, unconsumedY = 0;
1652        int consumedX = 0, consumedY = 0;
1653
1654        consumePendingUpdateOperations();
1655        if (mAdapter != null) {
1656            eatRequestLayout();
1657            onEnterLayoutOrScroll();
1658            Trace.beginSection(TRACE_SCROLL_TAG);
1659            if (x != 0) {
1660                consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
1661                unconsumedX = x - consumedX;
1662            }
1663            if (y != 0) {
1664                consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
1665                unconsumedY = y - consumedY;
1666            }
1667            Trace.endSection();
1668            repositionShadowingViews();
1669            onExitLayoutOrScroll();
1670            resumeRequestLayout(false);
1671        }
1672        if (!mItemDecorations.isEmpty()) {
1673            invalidate();
1674        }
1675
1676        if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
1677            // Update the last touch co-ords, taking any scroll offset into account
1678            mLastTouchX -= mScrollOffset[0];
1679            mLastTouchY -= mScrollOffset[1];
1680            if (ev != null) {
1681                ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
1682            }
1683            mNestedOffsets[0] += mScrollOffset[0];
1684            mNestedOffsets[1] += mScrollOffset[1];
1685        } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
1686            if (ev != null) {
1687                pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
1688            }
1689            considerReleasingGlowsOnScroll(x, y);
1690        }
1691        if (consumedX != 0 || consumedY != 0) {
1692            dispatchOnScrolled(consumedX, consumedY);
1693        }
1694        if (!awakenScrollBars()) {
1695            invalidate();
1696        }
1697        return consumedX != 0 || consumedY != 0;
1698    }
1699
1700    /**
1701     * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
1702     * range. This value is used to compute the length of the thumb within the scrollbar's track.
1703     * </p>
1704     *
1705     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1706     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
1707     *
1708     * <p>Default implementation returns 0.</p>
1709     *
1710     * <p>If you want to support scroll bars, override
1711     * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
1712     * LayoutManager. </p>
1713     *
1714     * @return The horizontal offset of the scrollbar's thumb
1715     * @see com.android.internal.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset
1716     * (RecyclerView.State)
1717     */
1718    @Override
1719    public int computeHorizontalScrollOffset() {
1720        if (mLayout == null) {
1721            return 0;
1722        }
1723        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
1724    }
1725
1726    /**
1727     * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
1728     * horizontal range. This value is used to compute the length of the thumb within the
1729     * scrollbar's track.</p>
1730     *
1731     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1732     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
1733     *
1734     * <p>Default implementation returns 0.</p>
1735     *
1736     * <p>If you want to support scroll bars, override
1737     * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
1738     * LayoutManager.</p>
1739     *
1740     * @return The horizontal extent of the scrollbar's thumb
1741     * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
1742     */
1743    @Override
1744    public int computeHorizontalScrollExtent() {
1745        if (mLayout == null) {
1746            return 0;
1747        }
1748        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
1749    }
1750
1751    /**
1752     * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
1753     *
1754     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1755     * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
1756     *
1757     * <p>Default implementation returns 0.</p>
1758     *
1759     * <p>If you want to support scroll bars, override
1760     * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
1761     * LayoutManager.</p>
1762     *
1763     * @return The total horizontal range represented by the vertical scrollbar
1764     * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
1765     */
1766    @Override
1767    public int computeHorizontalScrollRange() {
1768        if (mLayout == null) {
1769            return 0;
1770        }
1771        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
1772    }
1773
1774    /**
1775     * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
1776     * This value is used to compute the length of the thumb within the scrollbar's track. </p>
1777     *
1778     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1779     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
1780     *
1781     * <p>Default implementation returns 0.</p>
1782     *
1783     * <p>If you want to support scroll bars, override
1784     * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
1785     * LayoutManager.</p>
1786     *
1787     * @return The vertical offset of the scrollbar's thumb
1788     * @see com.android.internal.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset
1789     * (RecyclerView.State)
1790     */
1791    @Override
1792    public int computeVerticalScrollOffset() {
1793        if (mLayout == null) {
1794            return 0;
1795        }
1796        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
1797    }
1798
1799    /**
1800     * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
1801     * This value is used to compute the length of the thumb within the scrollbar's track.</p>
1802     *
1803     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1804     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
1805     *
1806     * <p>Default implementation returns 0.</p>
1807     *
1808     * <p>If you want to support scroll bars, override
1809     * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
1810     * LayoutManager.</p>
1811     *
1812     * @return The vertical extent of the scrollbar's thumb
1813     * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
1814     */
1815    @Override
1816    public int computeVerticalScrollExtent() {
1817        if (mLayout == null) {
1818            return 0;
1819        }
1820        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
1821    }
1822
1823    /**
1824     * <p>Compute the vertical range that the vertical scrollbar represents.</p>
1825     *
1826     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1827     * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
1828     *
1829     * <p>Default implementation returns 0.</p>
1830     *
1831     * <p>If you want to support scroll bars, override
1832     * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
1833     * LayoutManager.</p>
1834     *
1835     * @return The total vertical range represented by the vertical scrollbar
1836     * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
1837     */
1838    @Override
1839    public int computeVerticalScrollRange() {
1840        if (mLayout == null) {
1841            return 0;
1842        }
1843        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
1844    }
1845
1846
1847    void eatRequestLayout() {
1848        mEatRequestLayout++;
1849        if (mEatRequestLayout == 1 && !mLayoutFrozen) {
1850            mLayoutRequestEaten = false;
1851        }
1852    }
1853
1854    void resumeRequestLayout(boolean performLayoutChildren) {
1855        if (mEatRequestLayout < 1) {
1856            //noinspection PointlessBooleanExpression
1857            if (DEBUG) {
1858                throw new IllegalStateException("invalid eat request layout count");
1859            }
1860            mEatRequestLayout = 1;
1861        }
1862        if (!performLayoutChildren) {
1863            // Reset the layout request eaten counter.
1864            // This is necessary since eatRequest calls can be nested in which case the other
1865            // call will override the inner one.
1866            // for instance:
1867            // eat layout for process adapter updates
1868            //   eat layout for dispatchLayout
1869            //     a bunch of req layout calls arrive
1870
1871            mLayoutRequestEaten = false;
1872        }
1873        if (mEatRequestLayout == 1) {
1874            // when layout is frozen we should delay dispatchLayout()
1875            if (performLayoutChildren && mLayoutRequestEaten && !mLayoutFrozen
1876                    && mLayout != null && mAdapter != null) {
1877                dispatchLayout();
1878            }
1879            if (!mLayoutFrozen) {
1880                mLayoutRequestEaten = false;
1881            }
1882        }
1883        mEatRequestLayout--;
1884    }
1885
1886    /**
1887     * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
1888     * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
1889     * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
1890     * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
1891     * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
1892     * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
1893     * called.
1894     *
1895     * <p>
1896     * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
1897     * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
1898     * RecyclerView, State, int)}.
1899     * <p>
1900     * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
1901     * stop frozen.
1902     * <p>
1903     * Note: Running ItemAnimator is not stopped automatically,  it's caller's
1904     * responsibility to call ItemAnimator.end().
1905     *
1906     * @param frozen   true to freeze layout and scroll, false to re-enable.
1907     */
1908    public void setLayoutFrozen(boolean frozen) {
1909        if (frozen != mLayoutFrozen) {
1910            assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
1911            if (!frozen) {
1912                mLayoutFrozen = false;
1913                if (mLayoutRequestEaten && mLayout != null && mAdapter != null) {
1914                    requestLayout();
1915                }
1916                mLayoutRequestEaten = false;
1917            } else {
1918                final long now = SystemClock.uptimeMillis();
1919                MotionEvent cancelEvent = MotionEvent.obtain(now, now,
1920                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
1921                onTouchEvent(cancelEvent);
1922                mLayoutFrozen = true;
1923                mIgnoreMotionEventTillDown = true;
1924                stopScroll();
1925            }
1926        }
1927    }
1928
1929    /**
1930     * Returns true if layout and scroll are frozen.
1931     *
1932     * @return true if layout and scroll are frozen
1933     * @see #setLayoutFrozen(boolean)
1934     */
1935    public boolean isLayoutFrozen() {
1936        return mLayoutFrozen;
1937    }
1938
1939    /**
1940     * Animate a scroll by the given amount of pixels along either axis.
1941     *
1942     * @param dx Pixels to scroll horizontally
1943     * @param dy Pixels to scroll vertically
1944     */
1945    public void smoothScrollBy(int dx, int dy) {
1946        smoothScrollBy(dx, dy, null);
1947    }
1948
1949    /**
1950     * Animate a scroll by the given amount of pixels along either axis.
1951     *
1952     * @param dx Pixels to scroll horizontally
1953     * @param dy Pixels to scroll vertically
1954     * @param interpolator {@link Interpolator} to be used for scrolling. If it is
1955     *                     {@code null}, RecyclerView is going to use the default interpolator.
1956     */
1957    public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
1958        if (mLayout == null) {
1959            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
1960                    + "Call setLayoutManager with a non-null argument.");
1961            return;
1962        }
1963        if (mLayoutFrozen) {
1964            return;
1965        }
1966        if (!mLayout.canScrollHorizontally()) {
1967            dx = 0;
1968        }
1969        if (!mLayout.canScrollVertically()) {
1970            dy = 0;
1971        }
1972        if (dx != 0 || dy != 0) {
1973            mViewFlinger.smoothScrollBy(dx, dy, interpolator);
1974        }
1975    }
1976
1977    /**
1978     * Begin a standard fling with an initial velocity along each axis in pixels per second.
1979     * If the velocity given is below the system-defined minimum this method will return false
1980     * and no fling will occur.
1981     *
1982     * @param velocityX Initial horizontal velocity in pixels per second
1983     * @param velocityY Initial vertical velocity in pixels per second
1984     * @return true if the fling was started, false if the velocity was too low to fling or
1985     * LayoutManager does not support scrolling in the axis fling is issued.
1986     *
1987     * @see LayoutManager#canScrollVertically()
1988     * @see LayoutManager#canScrollHorizontally()
1989     */
1990    public boolean fling(int velocityX, int velocityY) {
1991        if (mLayout == null) {
1992            Log.e(TAG, "Cannot fling without a LayoutManager set. "
1993                    + "Call setLayoutManager with a non-null argument.");
1994            return false;
1995        }
1996        if (mLayoutFrozen) {
1997            return false;
1998        }
1999
2000        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
2001        final boolean canScrollVertical = mLayout.canScrollVertically();
2002
2003        if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
2004            velocityX = 0;
2005        }
2006        if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
2007            velocityY = 0;
2008        }
2009        if (velocityX == 0 && velocityY == 0) {
2010            // If we don't have any velocity, return false
2011            return false;
2012        }
2013
2014        if (!dispatchNestedPreFling(velocityX, velocityY)) {
2015            final boolean canScroll = canScrollHorizontal || canScrollVertical;
2016            dispatchNestedFling(velocityX, velocityY, canScroll);
2017
2018            if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
2019                return true;
2020            }
2021
2022            if (canScroll) {
2023                velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
2024                velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
2025                mViewFlinger.fling(velocityX, velocityY);
2026                return true;
2027            }
2028        }
2029        return false;
2030    }
2031
2032    /**
2033     * Stop any current scroll in progress, such as one started by
2034     * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
2035     */
2036    public void stopScroll() {
2037        setScrollState(SCROLL_STATE_IDLE);
2038        stopScrollersInternal();
2039    }
2040
2041    /**
2042     * Similar to {@link #stopScroll()} but does not set the state.
2043     */
2044    private void stopScrollersInternal() {
2045        mViewFlinger.stop();
2046        if (mLayout != null) {
2047            mLayout.stopSmoothScroller();
2048        }
2049    }
2050
2051    /**
2052     * Returns the minimum velocity to start a fling.
2053     *
2054     * @return The minimum velocity to start a fling
2055     */
2056    public int getMinFlingVelocity() {
2057        return mMinFlingVelocity;
2058    }
2059
2060
2061    /**
2062     * Returns the maximum fling velocity used by this RecyclerView.
2063     *
2064     * @return The maximum fling velocity used by this RecyclerView.
2065     */
2066    public int getMaxFlingVelocity() {
2067        return mMaxFlingVelocity;
2068    }
2069
2070    /**
2071     * Apply a pull to relevant overscroll glow effects
2072     */
2073    private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
2074        boolean invalidate = false;
2075        if (overscrollX < 0) {
2076            ensureLeftGlow();
2077            mLeftGlow.onPull(-overscrollX / getWidth(), 1f - y  / getHeight());
2078            invalidate = true;
2079        } else if (overscrollX > 0) {
2080            ensureRightGlow();
2081            mRightGlow.onPull(overscrollX / getWidth(), y / getHeight());
2082            invalidate = true;
2083        }
2084
2085        if (overscrollY < 0) {
2086            ensureTopGlow();
2087            mTopGlow.onPull(-overscrollY / getHeight(), x / getWidth());
2088            invalidate = true;
2089        } else if (overscrollY > 0) {
2090            ensureBottomGlow();
2091            mBottomGlow.onPull(overscrollY / getHeight(), 1f - x / getWidth());
2092            invalidate = true;
2093        }
2094
2095        if (invalidate || overscrollX != 0 || overscrollY != 0) {
2096            postInvalidateOnAnimation();
2097        }
2098    }
2099
2100    private void releaseGlows() {
2101        boolean needsInvalidate = false;
2102        if (mLeftGlow != null) {
2103            mLeftGlow.onRelease();
2104            needsInvalidate = true;
2105        }
2106        if (mTopGlow != null) {
2107            mTopGlow.onRelease();
2108            needsInvalidate = true;
2109        }
2110        if (mRightGlow != null) {
2111            mRightGlow.onRelease();
2112            needsInvalidate = true;
2113        }
2114        if (mBottomGlow != null) {
2115            mBottomGlow.onRelease();
2116            needsInvalidate = true;
2117        }
2118        if (needsInvalidate) {
2119            postInvalidateOnAnimation();
2120        }
2121    }
2122
2123    void considerReleasingGlowsOnScroll(int dx, int dy) {
2124        boolean needsInvalidate = false;
2125        if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
2126            mLeftGlow.onRelease();
2127            needsInvalidate = true;
2128        }
2129        if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
2130            mRightGlow.onRelease();
2131            needsInvalidate = true;
2132        }
2133        if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
2134            mTopGlow.onRelease();
2135            needsInvalidate = true;
2136        }
2137        if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
2138            mBottomGlow.onRelease();
2139            needsInvalidate = true;
2140        }
2141        if (needsInvalidate) {
2142            postInvalidateOnAnimation();
2143        }
2144    }
2145
2146    void absorbGlows(int velocityX, int velocityY) {
2147        if (velocityX < 0) {
2148            ensureLeftGlow();
2149            mLeftGlow.onAbsorb(-velocityX);
2150        } else if (velocityX > 0) {
2151            ensureRightGlow();
2152            mRightGlow.onAbsorb(velocityX);
2153        }
2154
2155        if (velocityY < 0) {
2156            ensureTopGlow();
2157            mTopGlow.onAbsorb(-velocityY);
2158        } else if (velocityY > 0) {
2159            ensureBottomGlow();
2160            mBottomGlow.onAbsorb(velocityY);
2161        }
2162
2163        if (velocityX != 0 || velocityY != 0) {
2164            postInvalidateOnAnimation();
2165        }
2166    }
2167
2168    void ensureLeftGlow() {
2169        if (mLeftGlow != null) {
2170            return;
2171        }
2172        mLeftGlow = new EdgeEffect(getContext());
2173        if (mClipToPadding) {
2174            mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2175                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2176        } else {
2177            mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2178        }
2179    }
2180
2181    void ensureRightGlow() {
2182        if (mRightGlow != null) {
2183            return;
2184        }
2185        mRightGlow = new EdgeEffect(getContext());
2186        if (mClipToPadding) {
2187            mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2188                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2189        } else {
2190            mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2191        }
2192    }
2193
2194    void ensureTopGlow() {
2195        if (mTopGlow != null) {
2196            return;
2197        }
2198        mTopGlow = new EdgeEffect(getContext());
2199        if (mClipToPadding) {
2200            mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2201                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2202        } else {
2203            mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2204        }
2205
2206    }
2207
2208    void ensureBottomGlow() {
2209        if (mBottomGlow != null) {
2210            return;
2211        }
2212        mBottomGlow = new EdgeEffect(getContext());
2213        if (mClipToPadding) {
2214            mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2215                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2216        } else {
2217            mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2218        }
2219    }
2220
2221    void invalidateGlows() {
2222        mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
2223    }
2224
2225    /**
2226     * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
2227     * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
2228     * that differs from other ViewGroups.
2229     * <p>
2230     * It first does a focus search within the RecyclerView. If this search finds a View that is in
2231     * the focus direction with respect to the currently focused View, RecyclerView returns that
2232     * child as the next focus target. When it cannot find such child, it calls
2233     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
2234     * in the focus search direction. If LayoutManager adds a View that matches the
2235     * focus search criteria, it will be returned as the focus search result. Otherwise,
2236     * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
2237     * <p>
2238     * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
2239     * is not in the focus direction is still valid focus target which may not be the desired
2240     * behavior if the Adapter has more children in the focus direction. To handle this case,
2241     * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
2242     * focus search in that direction. If there are no Views to gain focus, it will call
2243     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
2244     * focus search with the original (relative) direction. This allows RecyclerView to provide
2245     * better candidates to the focus search while still allowing the view system to take focus from
2246     * the RecyclerView and give it to a more suitable child if such child exists.
2247     *
2248     * @param focused The view that currently has focus
2249     * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
2250     * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
2251     * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
2252     *
2253     * @return A new View that can be the next focus after the focused View
2254     */
2255    @Override
2256    public View focusSearch(View focused, int direction) {
2257        View result = mLayout.onInterceptFocusSearch(focused, direction);
2258        if (result != null) {
2259            return result;
2260        }
2261        final boolean canRunFocusFailure = mAdapter != null && mLayout != null
2262                && !isComputingLayout() && !mLayoutFrozen;
2263
2264        final FocusFinder ff = FocusFinder.getInstance();
2265        if (canRunFocusFailure
2266                && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
2267            // convert direction to absolute direction and see if we have a view there and if not
2268            // tell LayoutManager to add if it can.
2269            boolean needsFocusFailureLayout = false;
2270            if (mLayout.canScrollVertically()) {
2271                final int absDir =
2272                        direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
2273                final View found = ff.findNextFocus(this, focused, absDir);
2274                needsFocusFailureLayout = found == null;
2275                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2276                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2277                    direction = absDir;
2278                }
2279            }
2280            if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
2281                boolean rtl = mLayout.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
2282                final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
2283                        ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2284                final View found = ff.findNextFocus(this, focused, absDir);
2285                needsFocusFailureLayout = found == null;
2286                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2287                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2288                    direction = absDir;
2289                }
2290            }
2291            if (needsFocusFailureLayout) {
2292                consumePendingUpdateOperations();
2293                final View focusedItemView = findContainingItemView(focused);
2294                if (focusedItemView == null) {
2295                    // panic, focused view is not a child anymore, cannot call super.
2296                    return null;
2297                }
2298                eatRequestLayout();
2299                mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2300                resumeRequestLayout(false);
2301            }
2302            result = ff.findNextFocus(this, focused, direction);
2303        } else {
2304            result = ff.findNextFocus(this, focused, direction);
2305            if (result == null && canRunFocusFailure) {
2306                consumePendingUpdateOperations();
2307                final View focusedItemView = findContainingItemView(focused);
2308                if (focusedItemView == null) {
2309                    // panic, focused view is not a child anymore, cannot call super.
2310                    return null;
2311                }
2312                eatRequestLayout();
2313                result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2314                resumeRequestLayout(false);
2315            }
2316        }
2317        return isPreferredNextFocus(focused, result, direction)
2318                ? result : super.focusSearch(focused, direction);
2319    }
2320
2321    /**
2322     * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
2323     * assign it as the next focus View instead of letting view hierarchy decide.
2324     * A good candidate means a View that is aligned in the focus direction wrt the focused View
2325     * and is not the RecyclerView itself.
2326     * When this method returns false, RecyclerView will let the parent make the decision so the
2327     * same View may still get the focus as a result of that search.
2328     */
2329    private boolean isPreferredNextFocus(View focused, View next, int direction) {
2330        if (next == null || next == this) {
2331            return false;
2332        }
2333        if (focused == null) {
2334            return true;
2335        }
2336
2337        if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
2338            final boolean rtl = mLayout.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
2339            final int absHorizontal = (direction == View.FOCUS_FORWARD) ^ rtl
2340                    ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2341            if (isPreferredNextFocusAbsolute(focused, next, absHorizontal)) {
2342                return true;
2343            }
2344            if (direction == View.FOCUS_FORWARD) {
2345                return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_DOWN);
2346            } else {
2347                return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_UP);
2348            }
2349        } else {
2350            return isPreferredNextFocusAbsolute(focused, next, direction);
2351        }
2352
2353    }
2354
2355    /**
2356     * Logic taken from FocusSearch#isCandidate
2357     */
2358    private boolean isPreferredNextFocusAbsolute(View focused, View next, int direction) {
2359        mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2360        mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
2361        offsetDescendantRectToMyCoords(focused, mTempRect);
2362        offsetDescendantRectToMyCoords(next, mTempRect2);
2363        switch (direction) {
2364            case View.FOCUS_LEFT:
2365                return (mTempRect.right > mTempRect2.right
2366                        || mTempRect.left >= mTempRect2.right)
2367                        && mTempRect.left > mTempRect2.left;
2368            case View.FOCUS_RIGHT:
2369                return (mTempRect.left < mTempRect2.left
2370                        || mTempRect.right <= mTempRect2.left)
2371                        && mTempRect.right < mTempRect2.right;
2372            case View.FOCUS_UP:
2373                return (mTempRect.bottom > mTempRect2.bottom
2374                        || mTempRect.top >= mTempRect2.bottom)
2375                        && mTempRect.top > mTempRect2.top;
2376            case View.FOCUS_DOWN:
2377                return (mTempRect.top < mTempRect2.top
2378                        || mTempRect.bottom <= mTempRect2.top)
2379                        && mTempRect.bottom < mTempRect2.bottom;
2380        }
2381        throw new IllegalArgumentException("direction must be absolute. received:" + direction);
2382    }
2383
2384    @Override
2385    public void requestChildFocus(View child, View focused) {
2386        if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
2387            mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2388
2389            // get item decor offsets w/o refreshing. If they are invalid, there will be another
2390            // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
2391            // View in viewport.
2392            final ViewGroup.LayoutParams focusedLayoutParams = focused.getLayoutParams();
2393            if (focusedLayoutParams instanceof LayoutParams) {
2394                // if focused child has item decors, use them. Otherwise, ignore.
2395                final LayoutParams lp = (LayoutParams) focusedLayoutParams;
2396                if (!lp.mInsetsDirty) {
2397                    final Rect insets = lp.mDecorInsets;
2398                    mTempRect.left -= insets.left;
2399                    mTempRect.right += insets.right;
2400                    mTempRect.top -= insets.top;
2401                    mTempRect.bottom += insets.bottom;
2402                }
2403            }
2404
2405            offsetDescendantRectToMyCoords(focused, mTempRect);
2406            offsetRectIntoDescendantCoords(child, mTempRect);
2407            requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete);
2408        }
2409        super.requestChildFocus(child, focused);
2410    }
2411
2412    @Override
2413    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
2414        return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
2415    }
2416
2417    @Override
2418    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
2419        if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
2420            super.addFocusables(views, direction, focusableMode);
2421        }
2422    }
2423
2424    @Override
2425    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
2426        if (isComputingLayout()) {
2427            // if we are in the middle of a layout calculation, don't let any child take focus.
2428            // RV will handle it after layout calculation is finished.
2429            return false;
2430        }
2431        return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
2432    }
2433
2434    @Override
2435    protected void onAttachedToWindow() {
2436        super.onAttachedToWindow();
2437        mLayoutOrScrollCounter = 0;
2438        mIsAttached = true;
2439        mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
2440        if (mLayout != null) {
2441            mLayout.dispatchAttachedToWindow(this);
2442        }
2443        mPostedAnimatorRunner = false;
2444
2445        if (ALLOW_THREAD_GAP_WORK) {
2446            // Register with gap worker
2447            mGapWorker = GapWorker.sGapWorker.get();
2448            if (mGapWorker == null) {
2449                mGapWorker = new GapWorker();
2450
2451                // break 60 fps assumption if data from display appears valid
2452                // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
2453                Display display = getDisplay();
2454                float refreshRate = 60.0f;
2455                if (!isInEditMode() && display != null) {
2456                    float displayRefreshRate = display.getRefreshRate();
2457                    if (displayRefreshRate >= 30.0f) {
2458                        refreshRate = displayRefreshRate;
2459                    }
2460                }
2461                mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
2462                GapWorker.sGapWorker.set(mGapWorker);
2463            }
2464            mGapWorker.add(this);
2465        }
2466    }
2467
2468    @Override
2469    protected void onDetachedFromWindow() {
2470        super.onDetachedFromWindow();
2471        if (mItemAnimator != null) {
2472            mItemAnimator.endAnimations();
2473        }
2474        stopScroll();
2475        mIsAttached = false;
2476        if (mLayout != null) {
2477            mLayout.dispatchDetachedFromWindow(this, mRecycler);
2478        }
2479        mPendingAccessibilityImportanceChange.clear();
2480        removeCallbacks(mItemAnimatorRunner);
2481        mViewInfoStore.onDetach();
2482
2483        if (ALLOW_THREAD_GAP_WORK) {
2484            // Unregister with gap worker
2485            mGapWorker.remove(this);
2486            mGapWorker = null;
2487        }
2488    }
2489
2490    /**
2491     * Returns true if RecyclerView is attached to window.
2492     */
2493    // @override
2494    public boolean isAttachedToWindow() {
2495        return mIsAttached;
2496    }
2497
2498    /**
2499     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2500     * {@link IllegalStateException} if it <b>is not</b>.
2501     *
2502     * @param message The message for the exception. Can be null.
2503     * @see #assertNotInLayoutOrScroll(String)
2504     */
2505    void assertInLayoutOrScroll(String message) {
2506        if (!isComputingLayout()) {
2507            if (message == null) {
2508                throw new IllegalStateException("Cannot call this method unless RecyclerView is "
2509                        + "computing a layout or scrolling");
2510            }
2511            throw new IllegalStateException(message);
2512
2513        }
2514    }
2515
2516    /**
2517     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2518     * {@link IllegalStateException} if it <b>is</b>.
2519     *
2520     * @param message The message for the exception. Can be null.
2521     * @see #assertInLayoutOrScroll(String)
2522     */
2523    void assertNotInLayoutOrScroll(String message) {
2524        if (isComputingLayout()) {
2525            if (message == null) {
2526                throw new IllegalStateException("Cannot call this method while RecyclerView is "
2527                        + "computing a layout or scrolling");
2528            }
2529            throw new IllegalStateException(message);
2530        }
2531        if (mDispatchScrollCounter > 0) {
2532            Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might be run"
2533                    + " during a measure & layout pass where you cannot change the RecyclerView"
2534                    + " data. Any method call that might change the structure of the RecyclerView"
2535                    + " or the adapter contents should be postponed to the next frame.",
2536                    new IllegalStateException(""));
2537        }
2538    }
2539
2540    /**
2541     * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
2542     * to child views or this view's standard scrolling behavior.
2543     *
2544     * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
2545     * returns true from
2546     * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
2547     * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
2548     * for each incoming MotionEvent until the end of the gesture.</p>
2549     *
2550     * @param listener Listener to add
2551     * @see SimpleOnItemTouchListener
2552     */
2553    public void addOnItemTouchListener(OnItemTouchListener listener) {
2554        mOnItemTouchListeners.add(listener);
2555    }
2556
2557    /**
2558     * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
2559     *
2560     * @param listener Listener to remove
2561     */
2562    public void removeOnItemTouchListener(OnItemTouchListener listener) {
2563        mOnItemTouchListeners.remove(listener);
2564        if (mActiveOnItemTouchListener == listener) {
2565            mActiveOnItemTouchListener = null;
2566        }
2567    }
2568
2569    private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
2570        final int action = e.getAction();
2571        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
2572            mActiveOnItemTouchListener = null;
2573        }
2574
2575        final int listenerCount = mOnItemTouchListeners.size();
2576        for (int i = 0; i < listenerCount; i++) {
2577            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2578            if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
2579                mActiveOnItemTouchListener = listener;
2580                return true;
2581            }
2582        }
2583        return false;
2584    }
2585
2586    private boolean dispatchOnItemTouch(MotionEvent e) {
2587        final int action = e.getAction();
2588        if (mActiveOnItemTouchListener != null) {
2589            if (action == MotionEvent.ACTION_DOWN) {
2590                // Stale state from a previous gesture, we're starting a new one. Clear it.
2591                mActiveOnItemTouchListener = null;
2592            } else {
2593                mActiveOnItemTouchListener.onTouchEvent(this, e);
2594                if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
2595                    // Clean up for the next gesture.
2596                    mActiveOnItemTouchListener = null;
2597                }
2598                return true;
2599            }
2600        }
2601
2602        // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
2603        // as called from onInterceptTouchEvent; skip it.
2604        if (action != MotionEvent.ACTION_DOWN) {
2605            final int listenerCount = mOnItemTouchListeners.size();
2606            for (int i = 0; i < listenerCount; i++) {
2607                final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2608                if (listener.onInterceptTouchEvent(this, e)) {
2609                    mActiveOnItemTouchListener = listener;
2610                    return true;
2611                }
2612            }
2613        }
2614        return false;
2615    }
2616
2617    @Override
2618    public boolean onInterceptTouchEvent(MotionEvent e) {
2619        if (mLayoutFrozen) {
2620            // When layout is frozen,  RV does not intercept the motion event.
2621            // A child view e.g. a button may still get the click.
2622            return false;
2623        }
2624        if (dispatchOnItemTouchIntercept(e)) {
2625            cancelTouch();
2626            return true;
2627        }
2628
2629        if (mLayout == null) {
2630            return false;
2631        }
2632
2633        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2634        final boolean canScrollVertically = mLayout.canScrollVertically();
2635
2636        if (mVelocityTracker == null) {
2637            mVelocityTracker = VelocityTracker.obtain();
2638        }
2639        mVelocityTracker.addMovement(e);
2640
2641        final int action = e.getActionMasked();
2642        final int actionIndex = e.getActionIndex();
2643
2644        switch (action) {
2645            case MotionEvent.ACTION_DOWN:
2646                if (mIgnoreMotionEventTillDown) {
2647                    mIgnoreMotionEventTillDown = false;
2648                }
2649                mScrollPointerId = e.getPointerId(0);
2650                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2651                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2652
2653                if (mScrollState == SCROLL_STATE_SETTLING) {
2654                    getParent().requestDisallowInterceptTouchEvent(true);
2655                    setScrollState(SCROLL_STATE_DRAGGING);
2656                }
2657
2658                // Clear the nested offsets
2659                mNestedOffsets[0] = mNestedOffsets[1] = 0;
2660
2661                int nestedScrollAxis = View.SCROLL_AXIS_NONE;
2662                if (canScrollHorizontally) {
2663                    nestedScrollAxis |= View.SCROLL_AXIS_HORIZONTAL;
2664                }
2665                if (canScrollVertically) {
2666                    nestedScrollAxis |= View.SCROLL_AXIS_VERTICAL;
2667                }
2668                startNestedScroll(nestedScrollAxis);
2669                break;
2670
2671            case MotionEvent.ACTION_POINTER_DOWN:
2672                mScrollPointerId = e.getPointerId(actionIndex);
2673                mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2674                mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2675                break;
2676
2677            case MotionEvent.ACTION_MOVE: {
2678                final int index = e.findPointerIndex(mScrollPointerId);
2679                if (index < 0) {
2680                    Log.e(TAG, "Error processing scroll; pointer index for id "
2681                            + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2682                    return false;
2683                }
2684
2685                final int x = (int) (e.getX(index) + 0.5f);
2686                final int y = (int) (e.getY(index) + 0.5f);
2687                if (mScrollState != SCROLL_STATE_DRAGGING) {
2688                    final int dx = x - mInitialTouchX;
2689                    final int dy = y - mInitialTouchY;
2690                    boolean startScroll = false;
2691                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2692                        mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
2693                        startScroll = true;
2694                    }
2695                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2696                        mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
2697                        startScroll = true;
2698                    }
2699                    if (startScroll) {
2700                        setScrollState(SCROLL_STATE_DRAGGING);
2701                    }
2702                }
2703            } break;
2704
2705            case MotionEvent.ACTION_POINTER_UP: {
2706                onPointerUp(e);
2707            } break;
2708
2709            case MotionEvent.ACTION_UP: {
2710                mVelocityTracker.clear();
2711                stopNestedScroll();
2712            } break;
2713
2714            case MotionEvent.ACTION_CANCEL: {
2715                cancelTouch();
2716            }
2717        }
2718        return mScrollState == SCROLL_STATE_DRAGGING;
2719    }
2720
2721    @Override
2722    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2723        final int listenerCount = mOnItemTouchListeners.size();
2724        for (int i = 0; i < listenerCount; i++) {
2725            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2726            listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
2727        }
2728        super.requestDisallowInterceptTouchEvent(disallowIntercept);
2729    }
2730
2731    @Override
2732    public boolean onTouchEvent(MotionEvent e) {
2733        if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
2734            return false;
2735        }
2736        if (dispatchOnItemTouch(e)) {
2737            cancelTouch();
2738            return true;
2739        }
2740
2741        if (mLayout == null) {
2742            return false;
2743        }
2744
2745        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2746        final boolean canScrollVertically = mLayout.canScrollVertically();
2747
2748        if (mVelocityTracker == null) {
2749            mVelocityTracker = VelocityTracker.obtain();
2750        }
2751        boolean eventAddedToVelocityTracker = false;
2752
2753        final MotionEvent vtev = MotionEvent.obtain(e);
2754        final int action = e.getActionMasked();
2755        final int actionIndex = e.getActionIndex();
2756
2757        if (action == MotionEvent.ACTION_DOWN) {
2758            mNestedOffsets[0] = mNestedOffsets[1] = 0;
2759        }
2760        vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
2761
2762        switch (action) {
2763            case MotionEvent.ACTION_DOWN: {
2764                mScrollPointerId = e.getPointerId(0);
2765                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2766                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2767
2768                int nestedScrollAxis = View.SCROLL_AXIS_NONE;
2769                if (canScrollHorizontally) {
2770                    nestedScrollAxis |= View.SCROLL_AXIS_HORIZONTAL;
2771                }
2772                if (canScrollVertically) {
2773                    nestedScrollAxis |= View.SCROLL_AXIS_VERTICAL;
2774                }
2775                startNestedScroll(nestedScrollAxis);
2776            } break;
2777
2778            case MotionEvent.ACTION_POINTER_DOWN: {
2779                mScrollPointerId = e.getPointerId(actionIndex);
2780                mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2781                mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2782            } break;
2783
2784            case MotionEvent.ACTION_MOVE: {
2785                final int index = e.findPointerIndex(mScrollPointerId);
2786                if (index < 0) {
2787                    Log.e(TAG, "Error processing scroll; pointer index for id "
2788                            + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2789                    return false;
2790                }
2791
2792                final int x = (int) (e.getX(index) + 0.5f);
2793                final int y = (int) (e.getY(index) + 0.5f);
2794                int dx = mLastTouchX - x;
2795                int dy = mLastTouchY - y;
2796
2797                if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
2798                    dx -= mScrollConsumed[0];
2799                    dy -= mScrollConsumed[1];
2800                    vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
2801                    // Updated the nested offsets
2802                    mNestedOffsets[0] += mScrollOffset[0];
2803                    mNestedOffsets[1] += mScrollOffset[1];
2804                }
2805
2806                if (mScrollState != SCROLL_STATE_DRAGGING) {
2807                    boolean startScroll = false;
2808                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2809                        if (dx > 0) {
2810                            dx -= mTouchSlop;
2811                        } else {
2812                            dx += mTouchSlop;
2813                        }
2814                        startScroll = true;
2815                    }
2816                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2817                        if (dy > 0) {
2818                            dy -= mTouchSlop;
2819                        } else {
2820                            dy += mTouchSlop;
2821                        }
2822                        startScroll = true;
2823                    }
2824                    if (startScroll) {
2825                        setScrollState(SCROLL_STATE_DRAGGING);
2826                    }
2827                }
2828
2829                if (mScrollState == SCROLL_STATE_DRAGGING) {
2830                    mLastTouchX = x - mScrollOffset[0];
2831                    mLastTouchY = y - mScrollOffset[1];
2832
2833                    if (scrollByInternal(
2834                            canScrollHorizontally ? dx : 0,
2835                            canScrollVertically ? dy : 0,
2836                            vtev)) {
2837                        getParent().requestDisallowInterceptTouchEvent(true);
2838                    }
2839                    if (mGapWorker != null && (dx != 0 || dy != 0)) {
2840                        mGapWorker.postFromTraversal(this, dx, dy);
2841                    }
2842                }
2843            } break;
2844
2845            case MotionEvent.ACTION_POINTER_UP: {
2846                onPointerUp(e);
2847            } break;
2848
2849            case MotionEvent.ACTION_UP: {
2850                mVelocityTracker.addMovement(vtev);
2851                eventAddedToVelocityTracker = true;
2852                mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
2853                final float xvel = canScrollHorizontally
2854                        ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
2855                final float yvel = canScrollVertically
2856                        ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
2857                if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
2858                    setScrollState(SCROLL_STATE_IDLE);
2859                }
2860                resetTouch();
2861            } break;
2862
2863            case MotionEvent.ACTION_CANCEL: {
2864                cancelTouch();
2865            } break;
2866        }
2867
2868        if (!eventAddedToVelocityTracker) {
2869            mVelocityTracker.addMovement(vtev);
2870        }
2871        vtev.recycle();
2872
2873        return true;
2874    }
2875
2876    private void resetTouch() {
2877        if (mVelocityTracker != null) {
2878            mVelocityTracker.clear();
2879        }
2880        stopNestedScroll();
2881        releaseGlows();
2882    }
2883
2884    private void cancelTouch() {
2885        resetTouch();
2886        setScrollState(SCROLL_STATE_IDLE);
2887    }
2888
2889    private void onPointerUp(MotionEvent e) {
2890        final int actionIndex = e.getActionIndex();
2891        if (e.getPointerId(actionIndex) == mScrollPointerId) {
2892            // Pick a new pointer to pick up the slack.
2893            final int newIndex = actionIndex == 0 ? 1 : 0;
2894            mScrollPointerId = e.getPointerId(newIndex);
2895            mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
2896            mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
2897        }
2898    }
2899
2900    // @Override
2901    public boolean onGenericMotionEvent(MotionEvent event) {
2902        if (mLayout == null) {
2903            return false;
2904        }
2905        if (mLayoutFrozen) {
2906            return false;
2907        }
2908        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
2909            if (event.getAction() == MotionEvent.ACTION_SCROLL) {
2910                final float vScroll, hScroll;
2911                if (mLayout.canScrollVertically()) {
2912                    // Inverse the sign of the vertical scroll to align the scroll orientation
2913                    // with AbsListView.
2914                    vScroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
2915                } else {
2916                    vScroll = 0f;
2917                }
2918                if (mLayout.canScrollHorizontally()) {
2919                    hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
2920                } else {
2921                    hScroll = 0f;
2922                }
2923
2924                if (vScroll != 0 || hScroll != 0) {
2925                    final float scrollFactor = getScrollFactor();
2926                    scrollByInternal((int) (hScroll * scrollFactor),
2927                            (int) (vScroll * scrollFactor), event);
2928                }
2929            }
2930        }
2931        return false;
2932    }
2933
2934    /**
2935     * Ported from View.getVerticalScrollFactor.
2936     */
2937    private float getScrollFactor() {
2938        if (mScrollFactor == Float.MIN_VALUE) {
2939            TypedValue outValue = new TypedValue();
2940            if (getContext().getTheme().resolveAttribute(
2941                    android.R.attr.listPreferredItemHeight, outValue, true)) {
2942                mScrollFactor = outValue.getDimension(
2943                        getContext().getResources().getDisplayMetrics());
2944            } else {
2945                return 0; //listPreferredItemHeight is not defined, no generic scrolling
2946            }
2947        }
2948        return mScrollFactor;
2949    }
2950
2951    @Override
2952    protected void onMeasure(int widthSpec, int heightSpec) {
2953        if (mLayout == null) {
2954            defaultOnMeasure(widthSpec, heightSpec);
2955            return;
2956        }
2957        if (mLayout.mAutoMeasure) {
2958            final int widthMode = MeasureSpec.getMode(widthSpec);
2959            final int heightMode = MeasureSpec.getMode(heightSpec);
2960            final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
2961                    && heightMode == MeasureSpec.EXACTLY;
2962            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
2963            if (skipMeasure || mAdapter == null) {
2964                return;
2965            }
2966            if (mState.mLayoutStep == State.STEP_START) {
2967                dispatchLayoutStep1();
2968            }
2969            // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
2970            // consistency
2971            mLayout.setMeasureSpecs(widthSpec, heightSpec);
2972            mState.mIsMeasuring = true;
2973            dispatchLayoutStep2();
2974
2975            // now we can get the width and height from the children.
2976            mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
2977
2978            // if RecyclerView has non-exact width and height and if there is at least one child
2979            // which also has non-exact width & height, we have to re-measure.
2980            if (mLayout.shouldMeasureTwice()) {
2981                mLayout.setMeasureSpecs(
2982                        MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
2983                        MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
2984                mState.mIsMeasuring = true;
2985                dispatchLayoutStep2();
2986                // now we can get the width and height from the children.
2987                mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
2988            }
2989        } else {
2990            if (mHasFixedSize) {
2991                mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
2992                return;
2993            }
2994            // custom onMeasure
2995            if (mAdapterUpdateDuringMeasure) {
2996                eatRequestLayout();
2997                onEnterLayoutOrScroll();
2998                processAdapterUpdatesAndSetAnimationFlags();
2999                onExitLayoutOrScroll();
3000
3001                if (mState.mRunPredictiveAnimations) {
3002                    mState.mInPreLayout = true;
3003                } else {
3004                    // consume remaining updates to provide a consistent state with the layout pass.
3005                    mAdapterHelper.consumeUpdatesInOnePass();
3006                    mState.mInPreLayout = false;
3007                }
3008                mAdapterUpdateDuringMeasure = false;
3009                resumeRequestLayout(false);
3010            }
3011
3012            if (mAdapter != null) {
3013                mState.mItemCount = mAdapter.getItemCount();
3014            } else {
3015                mState.mItemCount = 0;
3016            }
3017            eatRequestLayout();
3018            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3019            resumeRequestLayout(false);
3020            mState.mInPreLayout = false; // clear
3021        }
3022    }
3023
3024    /**
3025     * Used when onMeasure is called before layout manager is set
3026     */
3027    void defaultOnMeasure(int widthSpec, int heightSpec) {
3028        // calling LayoutManager here is not pretty but that API is already public and it is better
3029        // than creating another method since this is internal.
3030        final int width = LayoutManager.chooseSize(widthSpec,
3031                getPaddingLeft() + getPaddingRight(),
3032                getMinimumWidth());
3033        final int height = LayoutManager.chooseSize(heightSpec,
3034                getPaddingTop() + getPaddingBottom(),
3035                getMinimumHeight());
3036
3037        setMeasuredDimension(width, height);
3038    }
3039
3040    @Override
3041    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
3042        super.onSizeChanged(w, h, oldw, oldh);
3043        if (w != oldw || h != oldh) {
3044            invalidateGlows();
3045            // layout's w/h are updated during measure/layout steps.
3046        }
3047    }
3048
3049    /**
3050     * Sets the {@link ItemAnimator} that will handle animations involving changes
3051     * to the items in this RecyclerView. By default, RecyclerView instantiates and
3052     * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
3053     * enabled for the RecyclerView depends on the ItemAnimator and whether
3054     * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
3055     * supports item animations}.
3056     *
3057     * @param animator The ItemAnimator being set. If null, no animations will occur
3058     * when changes occur to the items in this RecyclerView.
3059     */
3060    public void setItemAnimator(ItemAnimator animator) {
3061        if (mItemAnimator != null) {
3062            mItemAnimator.endAnimations();
3063            mItemAnimator.setListener(null);
3064        }
3065        mItemAnimator = animator;
3066        if (mItemAnimator != null) {
3067            mItemAnimator.setListener(mItemAnimatorListener);
3068        }
3069    }
3070
3071    void onEnterLayoutOrScroll() {
3072        mLayoutOrScrollCounter++;
3073    }
3074
3075    void onExitLayoutOrScroll() {
3076        mLayoutOrScrollCounter--;
3077        if (mLayoutOrScrollCounter < 1) {
3078            if (DEBUG && mLayoutOrScrollCounter < 0) {
3079                throw new IllegalStateException("layout or scroll counter cannot go below zero."
3080                        + "Some calls are not matching");
3081            }
3082            mLayoutOrScrollCounter = 0;
3083            dispatchContentChangedIfNecessary();
3084            dispatchPendingImportantForAccessibilityChanges();
3085        }
3086    }
3087
3088    boolean isAccessibilityEnabled() {
3089        return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
3090    }
3091
3092    private void dispatchContentChangedIfNecessary() {
3093        final int flags = mEatenAccessibilityChangeFlags;
3094        mEatenAccessibilityChangeFlags = 0;
3095        if (flags != 0 && isAccessibilityEnabled()) {
3096            final AccessibilityEvent event = AccessibilityEvent.obtain();
3097            event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
3098            event.setContentChangeTypes(flags);
3099            sendAccessibilityEventUnchecked(event);
3100        }
3101    }
3102
3103    /**
3104     * Returns whether RecyclerView is currently computing a layout.
3105     * <p>
3106     * If this method returns true, it means that RecyclerView is in a lockdown state and any
3107     * attempt to update adapter contents will result in an exception because adapter contents
3108     * cannot be changed while RecyclerView is trying to compute the layout.
3109     * <p>
3110     * It is very unlikely that your code will be running during this state as it is
3111     * called by the framework when a layout traversal happens or RecyclerView starts to scroll
3112     * in response to system events (touch, accessibility etc).
3113     * <p>
3114     * This case may happen if you have some custom logic to change adapter contents in
3115     * response to a View callback (e.g. focus change callback) which might be triggered during a
3116     * layout calculation. In these cases, you should just postpone the change using a Handler or a
3117     * similar mechanism.
3118     *
3119     * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
3120     *         otherwise
3121     */
3122    public boolean isComputingLayout() {
3123        return mLayoutOrScrollCounter > 0;
3124    }
3125
3126    /**
3127     * Returns true if an accessibility event should not be dispatched now. This happens when an
3128     * accessibility request arrives while RecyclerView does not have a stable state which is very
3129     * hard to handle for a LayoutManager. Instead, this method records necessary information about
3130     * the event and dispatches a window change event after the critical section is finished.
3131     *
3132     * @return True if the accessibility event should be postponed.
3133     */
3134    boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
3135        if (isComputingLayout()) {
3136            int type = 0;
3137            if (event != null) {
3138                type = event.getContentChangeTypes();
3139            }
3140            if (type == 0) {
3141                type = AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
3142            }
3143            mEatenAccessibilityChangeFlags |= type;
3144            return true;
3145        }
3146        return false;
3147    }
3148
3149    @Override
3150    public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
3151        if (shouldDeferAccessibilityEvent(event)) {
3152            return;
3153        }
3154        super.sendAccessibilityEventUnchecked(event);
3155    }
3156
3157    /**
3158     * Gets the current ItemAnimator for this RecyclerView. A null return value
3159     * indicates that there is no animator and that item changes will happen without
3160     * any animations. By default, RecyclerView instantiates and
3161     * uses an instance of {@link DefaultItemAnimator}.
3162     *
3163     * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
3164     * when changes occur to the items in this RecyclerView.
3165     */
3166    public ItemAnimator getItemAnimator() {
3167        return mItemAnimator;
3168    }
3169
3170    /**
3171     * Post a runnable to the next frame to run pending item animations. Only the first such
3172     * request will be posted, governed by the mPostedAnimatorRunner flag.
3173     */
3174    void postAnimationRunner() {
3175        if (!mPostedAnimatorRunner && mIsAttached) {
3176            postOnAnimation(mItemAnimatorRunner);
3177            mPostedAnimatorRunner = true;
3178        }
3179    }
3180
3181    private boolean predictiveItemAnimationsEnabled() {
3182        return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
3183    }
3184
3185    /**
3186     * Consumes adapter updates and calculates which type of animations we want to run.
3187     * Called in onMeasure and dispatchLayout.
3188     * <p>
3189     * This method may process only the pre-layout state of updates or all of them.
3190     */
3191    private void processAdapterUpdatesAndSetAnimationFlags() {
3192        if (mDataSetHasChangedAfterLayout) {
3193            // Processing these items have no value since data set changed unexpectedly.
3194            // Instead, we just reset it.
3195            mAdapterHelper.reset();
3196            mLayout.onItemsChanged(this);
3197        }
3198        // simple animations are a subset of advanced animations (which will cause a
3199        // pre-layout step)
3200        // If layout supports predictive animations, pre-process to decide if we want to run them
3201        if (predictiveItemAnimationsEnabled()) {
3202            mAdapterHelper.preProcess();
3203        } else {
3204            mAdapterHelper.consumeUpdatesInOnePass();
3205        }
3206        boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
3207        mState.mRunSimpleAnimations = mFirstLayoutComplete
3208                && mItemAnimator != null
3209                && (mDataSetHasChangedAfterLayout
3210                        || animationTypeSupported
3211                        || mLayout.mRequestedSimpleAnimations)
3212                && (!mDataSetHasChangedAfterLayout
3213                        || mAdapter.hasStableIds());
3214        mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
3215                && animationTypeSupported
3216                && !mDataSetHasChangedAfterLayout
3217                && predictiveItemAnimationsEnabled();
3218    }
3219
3220    /**
3221     * Wrapper around layoutChildren() that handles animating changes caused by layout.
3222     * Animations work on the assumption that there are five different kinds of items
3223     * in play:
3224     * PERSISTENT: items are visible before and after layout
3225     * REMOVED: items were visible before layout and were removed by the app
3226     * ADDED: items did not exist before layout and were added by the app
3227     * DISAPPEARING: items exist in the data set before/after, but changed from
3228     * visible to non-visible in the process of layout (they were moved off
3229     * screen as a side-effect of other changes)
3230     * APPEARING: items exist in the data set before/after, but changed from
3231     * non-visible to visible in the process of layout (they were moved on
3232     * screen as a side-effect of other changes)
3233     * The overall approach figures out what items exist before/after layout and
3234     * infers one of the five above states for each of the items. Then the animations
3235     * are set up accordingly:
3236     * PERSISTENT views are animated via
3237     * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3238     * DISAPPEARING views are animated via
3239     * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3240     * APPEARING views are animated via
3241     * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3242     * and changed views are animated via
3243     * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
3244     */
3245    void dispatchLayout() {
3246        if (mAdapter == null) {
3247            Log.e(TAG, "No adapter attached; skipping layout");
3248            // leave the state in START
3249            return;
3250        }
3251        if (mLayout == null) {
3252            Log.e(TAG, "No layout manager attached; skipping layout");
3253            // leave the state in START
3254            return;
3255        }
3256        mState.mIsMeasuring = false;
3257        if (mState.mLayoutStep == State.STEP_START) {
3258            dispatchLayoutStep1();
3259            mLayout.setExactMeasureSpecsFrom(this);
3260            dispatchLayoutStep2();
3261        } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
3262                || mLayout.getHeight() != getHeight()) {
3263            // First 2 steps are done in onMeasure but looks like we have to run again due to
3264            // changed size.
3265            mLayout.setExactMeasureSpecsFrom(this);
3266            dispatchLayoutStep2();
3267        } else {
3268            // always make sure we sync them (to ensure mode is exact)
3269            mLayout.setExactMeasureSpecsFrom(this);
3270        }
3271        dispatchLayoutStep3();
3272    }
3273
3274    private void saveFocusInfo() {
3275        View child = null;
3276        if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
3277            child = getFocusedChild();
3278        }
3279
3280        final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
3281        if (focusedVh == null) {
3282            resetFocusInfo();
3283        } else {
3284            mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
3285            // mFocusedItemPosition should hold the current adapter position of the previously
3286            // focused item. If the item is removed, we store the previous adapter position of the
3287            // removed item.
3288            mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION
3289                    : (focusedVh.isRemoved() ? focusedVh.mOldPosition
3290                            : focusedVh.getAdapterPosition());
3291            mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
3292        }
3293    }
3294
3295    private void resetFocusInfo() {
3296        mState.mFocusedItemId = NO_ID;
3297        mState.mFocusedItemPosition = NO_POSITION;
3298        mState.mFocusedSubChildId = View.NO_ID;
3299    }
3300
3301    /**
3302     * Finds the best view candidate to request focus on using mFocusedItemPosition index of the
3303     * previously focused item. It first traverses the adapter forward to find a focusable candidate
3304     * and if no such candidate is found, it reverses the focus search direction for the items
3305     * before the mFocusedItemPosition'th index;
3306     * @return The best candidate to request focus on, or null if no such candidate exists. Null
3307     * indicates all the existing adapter items are unfocusable.
3308     */
3309    @Nullable
3310    private View findNextViewToFocus() {
3311        int startFocusSearchIndex = mState.mFocusedItemPosition != -1 ? mState.mFocusedItemPosition
3312                : 0;
3313        ViewHolder nextFocus;
3314        final int itemCount = mState.getItemCount();
3315        for (int i = startFocusSearchIndex; i < itemCount; i++) {
3316            nextFocus = findViewHolderForAdapterPosition(i);
3317            if (nextFocus == null) {
3318                break;
3319            }
3320            if (nextFocus.itemView.hasFocusable()) {
3321                return nextFocus.itemView;
3322            }
3323        }
3324        final int limit = Math.min(itemCount, startFocusSearchIndex);
3325        for (int i = limit - 1; i >= 0; i--) {
3326            nextFocus = findViewHolderForAdapterPosition(i);
3327            if (nextFocus == null) {
3328                return null;
3329            }
3330            if (nextFocus.itemView.hasFocusable()) {
3331                return nextFocus.itemView;
3332            }
3333        }
3334        return null;
3335    }
3336
3337    private void recoverFocusFromState() {
3338        if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()
3339                || getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
3340                || (getDescendantFocusability() == FOCUS_BEFORE_DESCENDANTS && isFocused())) {
3341            // No-op if either of these cases happens:
3342            // 1. RV has no focus, or 2. RV blocks focus to its children, or 3. RV takes focus
3343            // before its children and is focused (i.e. it already stole the focus away from its
3344            // descendants).
3345            return;
3346        }
3347        // only recover focus if RV itself has the focus or the focused view is hidden
3348        if (!isFocused()) {
3349            final View focusedChild = getFocusedChild();
3350            if (IGNORE_DETACHED_FOCUSED_CHILD
3351                    && (focusedChild.getParent() == null || !focusedChild.hasFocus())) {
3352                // Special handling of API 15-. A focused child can be invalid because mFocus is not
3353                // cleared when the child is detached (mParent = null),
3354                // This happens because clearFocus on API 15- does not invalidate mFocus of its
3355                // parent when this child is detached.
3356                // For API 16+, this is not an issue because requestFocus takes care of clearing the
3357                // prior detached focused child. For API 15- the problem happens in 2 cases because
3358                // clearChild does not call clearChildFocus on RV: 1. setFocusable(false) is called
3359                // for the current focused item which calls clearChild or 2. when the prior focused
3360                // child is removed, removeDetachedView called in layout step 3 which calls
3361                // clearChild. We should ignore this invalid focused child in all our calculations
3362                // for the next view to receive focus, and apply the focus recovery logic instead.
3363                if (mChildHelper.getChildCount() == 0) {
3364                    // No children left. Request focus on the RV itself since one of its children
3365                    // was holding focus previously.
3366                    requestFocus();
3367                    return;
3368                }
3369            } else if (!mChildHelper.isHidden(focusedChild)) {
3370                // If the currently focused child is hidden, apply the focus recovery logic.
3371                // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
3372                return;
3373            }
3374        }
3375        ViewHolder focusTarget = null;
3376        // RV first attempts to locate the previously focused item to request focus on using
3377        // mFocusedItemId. If such an item no longer exists, it then makes a best-effort attempt to
3378        // find the next best candidate to request focus on based on mFocusedItemPosition.
3379        if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
3380            focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
3381        }
3382        View viewToFocus = null;
3383        if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView)
3384                || !focusTarget.itemView.hasFocusable()) {
3385            if (mChildHelper.getChildCount() > 0) {
3386                // At this point, RV has focus and either of these conditions are true:
3387                // 1. There's no previously focused item either because RV received focused before
3388                // layout, or the previously focused item was removed, or RV doesn't have stable IDs
3389                // 2. Previous focus child is hidden, or 3. Previous focused child is no longer
3390                // focusable. In either of these cases, we make sure that RV still passes down the
3391                // focus to one of its focusable children using a best-effort algorithm.
3392                viewToFocus = findNextViewToFocus();
3393            }
3394        } else {
3395            // looks like the focused item has been replaced with another view that represents the
3396            // same item in the adapter. Request focus on that.
3397            viewToFocus = focusTarget.itemView;
3398        }
3399
3400        if (viewToFocus != null) {
3401            if (mState.mFocusedSubChildId != NO_ID) {
3402                View child = viewToFocus.findViewById(mState.mFocusedSubChildId);
3403                if (child != null && child.isFocusable()) {
3404                    viewToFocus = child;
3405                }
3406            }
3407            viewToFocus.requestFocus();
3408        }
3409    }
3410
3411    private int getDeepestFocusedViewWithId(View view) {
3412        int lastKnownId = view.getId();
3413        while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
3414            view = ((ViewGroup) view).getFocusedChild();
3415            final int id = view.getId();
3416            if (id != View.NO_ID) {
3417                lastKnownId = view.getId();
3418            }
3419        }
3420        return lastKnownId;
3421    }
3422
3423    /**
3424     * The first step of a layout where we;
3425     * - process adapter updates
3426     * - decide which animation should run
3427     * - save information about current views
3428     * - If necessary, run predictive layout and save its information
3429     */
3430    private void dispatchLayoutStep1() {
3431        mState.assertLayoutStep(State.STEP_START);
3432        mState.mIsMeasuring = false;
3433        eatRequestLayout();
3434        mViewInfoStore.clear();
3435        onEnterLayoutOrScroll();
3436        processAdapterUpdatesAndSetAnimationFlags();
3437        saveFocusInfo();
3438        mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
3439        mItemsAddedOrRemoved = mItemsChanged = false;
3440        mState.mInPreLayout = mState.mRunPredictiveAnimations;
3441        mState.mItemCount = mAdapter.getItemCount();
3442        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3443
3444        if (mState.mRunSimpleAnimations) {
3445            // Step 0: Find out where all non-removed items are, pre-layout
3446            int count = mChildHelper.getChildCount();
3447            for (int i = 0; i < count; ++i) {
3448                final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3449                if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
3450                    continue;
3451                }
3452                final ItemHolderInfo animationInfo = mItemAnimator
3453                        .recordPreLayoutInformation(mState, holder,
3454                                ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
3455                                holder.getUnmodifiedPayloads());
3456                mViewInfoStore.addToPreLayout(holder, animationInfo);
3457                if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
3458                        && !holder.shouldIgnore() && !holder.isInvalid()) {
3459                    long key = getChangedHolderKey(holder);
3460                    // This is NOT the only place where a ViewHolder is added to old change holders
3461                    // list. There is another case where:
3462                    //    * A VH is currently hidden but not deleted
3463                    //    * The hidden item is changed in the adapter
3464                    //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
3465                    // When this case is detected, RV will un-hide that view and add to the old
3466                    // change holders list.
3467                    mViewInfoStore.addToOldChangeHolders(key, holder);
3468                }
3469            }
3470        }
3471        if (mState.mRunPredictiveAnimations) {
3472            // Step 1: run prelayout: This will use the old positions of items. The layout manager
3473            // is expected to layout everything, even removed items (though not to add removed
3474            // items back to the container). This gives the pre-layout position of APPEARING views
3475            // which come into existence as part of the real layout.
3476
3477            // Save old positions so that LayoutManager can run its mapping logic.
3478            saveOldPositions();
3479            final boolean didStructureChange = mState.mStructureChanged;
3480            mState.mStructureChanged = false;
3481            // temporarily disable flag because we are asking for previous layout
3482            mLayout.onLayoutChildren(mRecycler, mState);
3483            mState.mStructureChanged = didStructureChange;
3484
3485            for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
3486                final View child = mChildHelper.getChildAt(i);
3487                final ViewHolder viewHolder = getChildViewHolderInt(child);
3488                if (viewHolder.shouldIgnore()) {
3489                    continue;
3490                }
3491                if (!mViewInfoStore.isInPreLayout(viewHolder)) {
3492                    int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
3493                    boolean wasHidden = viewHolder
3494                            .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3495                    if (!wasHidden) {
3496                        flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
3497                    }
3498                    final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
3499                            mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
3500                    if (wasHidden) {
3501                        recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
3502                    } else {
3503                        mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
3504                    }
3505                }
3506            }
3507            // we don't process disappearing list because they may re-appear in post layout pass.
3508            clearOldPositions();
3509        } else {
3510            clearOldPositions();
3511        }
3512        onExitLayoutOrScroll();
3513        resumeRequestLayout(false);
3514        mState.mLayoutStep = State.STEP_LAYOUT;
3515    }
3516
3517    /**
3518     * The second layout step where we do the actual layout of the views for the final state.
3519     * This step might be run multiple times if necessary (e.g. measure).
3520     */
3521    private void dispatchLayoutStep2() {
3522        eatRequestLayout();
3523        onEnterLayoutOrScroll();
3524        mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
3525        mAdapterHelper.consumeUpdatesInOnePass();
3526        mState.mItemCount = mAdapter.getItemCount();
3527        mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
3528
3529        // Step 2: Run layout
3530        mState.mInPreLayout = false;
3531        mLayout.onLayoutChildren(mRecycler, mState);
3532
3533        mState.mStructureChanged = false;
3534        mPendingSavedState = null;
3535
3536        // onLayoutChildren may have caused client code to disable item animations; re-check
3537        mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
3538        mState.mLayoutStep = State.STEP_ANIMATIONS;
3539        onExitLayoutOrScroll();
3540        resumeRequestLayout(false);
3541    }
3542
3543    /**
3544     * The final step of the layout where we save the information about views for animations,
3545     * trigger animations and do any necessary cleanup.
3546     */
3547    private void dispatchLayoutStep3() {
3548        mState.assertLayoutStep(State.STEP_ANIMATIONS);
3549        eatRequestLayout();
3550        onEnterLayoutOrScroll();
3551        mState.mLayoutStep = State.STEP_START;
3552        if (mState.mRunSimpleAnimations) {
3553            // Step 3: Find out where things are now, and process change animations.
3554            // traverse list in reverse because we may call animateChange in the loop which may
3555            // remove the target view holder.
3556            for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
3557                ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3558                if (holder.shouldIgnore()) {
3559                    continue;
3560                }
3561                long key = getChangedHolderKey(holder);
3562                final ItemHolderInfo animationInfo = mItemAnimator
3563                        .recordPostLayoutInformation(mState, holder);
3564                ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
3565                if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
3566                    // run a change animation
3567
3568                    // If an Item is CHANGED but the updated version is disappearing, it creates
3569                    // a conflicting case.
3570                    // Since a view that is marked as disappearing is likely to be going out of
3571                    // bounds, we run a change animation. Both views will be cleaned automatically
3572                    // once their animations finish.
3573                    // On the other hand, if it is the same view holder instance, we run a
3574                    // disappearing animation instead because we are not going to rebind the updated
3575                    // VH unless it is enforced by the layout manager.
3576                    final boolean oldDisappearing = mViewInfoStore.isDisappearing(
3577                            oldChangeViewHolder);
3578                    final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
3579                    if (oldDisappearing && oldChangeViewHolder == holder) {
3580                        // run disappear animation instead of change
3581                        mViewInfoStore.addToPostLayout(holder, animationInfo);
3582                    } else {
3583                        final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
3584                                oldChangeViewHolder);
3585                        // we add and remove so that any post info is merged.
3586                        mViewInfoStore.addToPostLayout(holder, animationInfo);
3587                        ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
3588                        if (preInfo == null) {
3589                            handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
3590                        } else {
3591                            animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
3592                                    oldDisappearing, newDisappearing);
3593                        }
3594                    }
3595                } else {
3596                    mViewInfoStore.addToPostLayout(holder, animationInfo);
3597                }
3598            }
3599
3600            // Step 4: Process view info lists and trigger animations
3601            mViewInfoStore.process(mViewInfoProcessCallback);
3602        }
3603
3604        mLayout.removeAndRecycleScrapInt(mRecycler);
3605        mState.mPreviousLayoutItemCount = mState.mItemCount;
3606        mDataSetHasChangedAfterLayout = false;
3607        mState.mRunSimpleAnimations = false;
3608
3609        mState.mRunPredictiveAnimations = false;
3610        mLayout.mRequestedSimpleAnimations = false;
3611        if (mRecycler.mChangedScrap != null) {
3612            mRecycler.mChangedScrap.clear();
3613        }
3614        if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
3615            // Initial prefetch has expanded cache, so reset until next prefetch.
3616            // This prevents initial prefetches from expanding the cache permanently.
3617            mLayout.mPrefetchMaxCountObserved = 0;
3618            mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
3619            mRecycler.updateViewCacheSize();
3620        }
3621
3622        mLayout.onLayoutCompleted(mState);
3623        onExitLayoutOrScroll();
3624        resumeRequestLayout(false);
3625        mViewInfoStore.clear();
3626        if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
3627            dispatchOnScrolled(0, 0);
3628        }
3629        recoverFocusFromState();
3630        resetFocusInfo();
3631    }
3632
3633    /**
3634     * This handles the case where there is an unexpected VH missing in the pre-layout map.
3635     * <p>
3636     * We might be able to detect the error in the application which will help the developer to
3637     * resolve the issue.
3638     * <p>
3639     * If it is not an expected error, we at least print an error to notify the developer and ignore
3640     * the animation.
3641     *
3642     * https://code.google.com/p/android/issues/detail?id=193958
3643     *
3644     * @param key The change key
3645     * @param holder Current ViewHolder
3646     * @param oldChangeViewHolder Changed ViewHolder
3647     */
3648    private void handleMissingPreInfoForChangeError(long key,
3649            ViewHolder holder, ViewHolder oldChangeViewHolder) {
3650        // check if two VH have the same key, if so, print that as an error
3651        final int childCount = mChildHelper.getChildCount();
3652        for (int i = 0; i < childCount; i++) {
3653            View view = mChildHelper.getChildAt(i);
3654            ViewHolder other = getChildViewHolderInt(view);
3655            if (other == holder) {
3656                continue;
3657            }
3658            final long otherKey = getChangedHolderKey(other);
3659            if (otherKey == key) {
3660                if (mAdapter != null && mAdapter.hasStableIds()) {
3661                    throw new IllegalStateException("Two different ViewHolders have the same stable"
3662                            + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
3663                            + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
3664                } else {
3665                    throw new IllegalStateException("Two different ViewHolders have the same change"
3666                            + " ID. This might happen due to inconsistent Adapter update events or"
3667                            + " if the LayoutManager lays out the same View multiple times."
3668                            + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
3669                }
3670            }
3671        }
3672        // Very unlikely to happen but if it does, notify the developer.
3673        Log.e(TAG, "Problem while matching changed view holders with the new"
3674                + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
3675                + " cannot be found but it is necessary for " + holder);
3676    }
3677
3678    /**
3679     * Records the animation information for a view holder that was bounced from hidden list. It
3680     * also clears the bounce back flag.
3681     */
3682    void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
3683            ItemHolderInfo animationInfo) {
3684        // looks like this view bounced back from hidden list!
3685        viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3686        if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
3687                && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
3688            long key = getChangedHolderKey(viewHolder);
3689            mViewInfoStore.addToOldChangeHolders(key, viewHolder);
3690        }
3691        mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
3692    }
3693
3694    private void findMinMaxChildLayoutPositions(int[] into) {
3695        final int count = mChildHelper.getChildCount();
3696        if (count == 0) {
3697            into[0] = NO_POSITION;
3698            into[1] = NO_POSITION;
3699            return;
3700        }
3701        int minPositionPreLayout = Integer.MAX_VALUE;
3702        int maxPositionPreLayout = Integer.MIN_VALUE;
3703        for (int i = 0; i < count; ++i) {
3704            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3705            if (holder.shouldIgnore()) {
3706                continue;
3707            }
3708            final int pos = holder.getLayoutPosition();
3709            if (pos < minPositionPreLayout) {
3710                minPositionPreLayout = pos;
3711            }
3712            if (pos > maxPositionPreLayout) {
3713                maxPositionPreLayout = pos;
3714            }
3715        }
3716        into[0] = minPositionPreLayout;
3717        into[1] = maxPositionPreLayout;
3718    }
3719
3720    private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
3721        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3722        return mMinMaxLayoutPositions[0] != minPositionPreLayout
3723                || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
3724    }
3725
3726    @Override
3727    protected void removeDetachedView(View child, boolean animate) {
3728        ViewHolder vh = getChildViewHolderInt(child);
3729        if (vh != null) {
3730            if (vh.isTmpDetached()) {
3731                vh.clearTmpDetachFlag();
3732            } else if (!vh.shouldIgnore()) {
3733                throw new IllegalArgumentException("Called removeDetachedView with a view which"
3734                        + " is not flagged as tmp detached." + vh);
3735            }
3736        }
3737        dispatchChildDetached(child);
3738        super.removeDetachedView(child, animate);
3739    }
3740
3741    /**
3742     * Returns a unique key to be used while handling change animations.
3743     * It might be child's position or stable id depending on the adapter type.
3744     */
3745    long getChangedHolderKey(ViewHolder holder) {
3746        return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
3747    }
3748
3749    void animateAppearance(@NonNull ViewHolder itemHolder,
3750            @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
3751        itemHolder.setIsRecyclable(false);
3752        if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
3753            postAnimationRunner();
3754        }
3755    }
3756
3757    void animateDisappearance(@NonNull ViewHolder holder,
3758            @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
3759        addAnimatingView(holder);
3760        holder.setIsRecyclable(false);
3761        if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
3762            postAnimationRunner();
3763        }
3764    }
3765
3766    private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
3767            @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
3768            boolean oldHolderDisappearing, boolean newHolderDisappearing) {
3769        oldHolder.setIsRecyclable(false);
3770        if (oldHolderDisappearing) {
3771            addAnimatingView(oldHolder);
3772        }
3773        if (oldHolder != newHolder) {
3774            if (newHolderDisappearing) {
3775                addAnimatingView(newHolder);
3776            }
3777            oldHolder.mShadowedHolder = newHolder;
3778            // old holder should disappear after animation ends
3779            addAnimatingView(oldHolder);
3780            mRecycler.unscrapView(oldHolder);
3781            newHolder.setIsRecyclable(false);
3782            newHolder.mShadowingHolder = oldHolder;
3783        }
3784        if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
3785            postAnimationRunner();
3786        }
3787    }
3788
3789    @Override
3790    protected void onLayout(boolean changed, int l, int t, int r, int b) {
3791        Trace.beginSection(TRACE_ON_LAYOUT_TAG);
3792        dispatchLayout();
3793        Trace.endSection();
3794        mFirstLayoutComplete = true;
3795    }
3796
3797    @Override
3798    public void requestLayout() {
3799        if (mEatRequestLayout == 0 && !mLayoutFrozen) {
3800            super.requestLayout();
3801        } else {
3802            mLayoutRequestEaten = true;
3803        }
3804    }
3805
3806    void markItemDecorInsetsDirty() {
3807        final int childCount = mChildHelper.getUnfilteredChildCount();
3808        for (int i = 0; i < childCount; i++) {
3809            final View child = mChildHelper.getUnfilteredChildAt(i);
3810            ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
3811        }
3812        mRecycler.markItemDecorInsetsDirty();
3813    }
3814
3815    @Override
3816    public void draw(Canvas c) {
3817        super.draw(c);
3818
3819        final int count = mItemDecorations.size();
3820        for (int i = 0; i < count; i++) {
3821            mItemDecorations.get(i).onDrawOver(c, this, mState);
3822        }
3823        // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
3824        // need find children closest to edges. Not sure if it is worth the effort.
3825        boolean needsInvalidate = false;
3826        if (mLeftGlow != null && !mLeftGlow.isFinished()) {
3827            final int restore = c.save();
3828            final int padding = mClipToPadding ? getPaddingBottom() : 0;
3829            c.rotate(270);
3830            c.translate(-getHeight() + padding, 0);
3831            needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
3832            c.restoreToCount(restore);
3833        }
3834        if (mTopGlow != null && !mTopGlow.isFinished()) {
3835            final int restore = c.save();
3836            if (mClipToPadding) {
3837                c.translate(getPaddingLeft(), getPaddingTop());
3838            }
3839            needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
3840            c.restoreToCount(restore);
3841        }
3842        if (mRightGlow != null && !mRightGlow.isFinished()) {
3843            final int restore = c.save();
3844            final int width = getWidth();
3845            final int padding = mClipToPadding ? getPaddingTop() : 0;
3846            c.rotate(90);
3847            c.translate(-padding, -width);
3848            needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
3849            c.restoreToCount(restore);
3850        }
3851        if (mBottomGlow != null && !mBottomGlow.isFinished()) {
3852            final int restore = c.save();
3853            c.rotate(180);
3854            if (mClipToPadding) {
3855                c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
3856            } else {
3857                c.translate(-getWidth(), -getHeight());
3858            }
3859            needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
3860            c.restoreToCount(restore);
3861        }
3862
3863        // If some views are animating, ItemDecorators are likely to move/change with them.
3864        // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
3865        // display lists are not invalidated.
3866        if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
3867                && mItemAnimator.isRunning()) {
3868            needsInvalidate = true;
3869        }
3870
3871        if (needsInvalidate) {
3872            postInvalidateOnAnimation();
3873        }
3874    }
3875
3876    @Override
3877    public void onDraw(Canvas c) {
3878        super.onDraw(c);
3879
3880        final int count = mItemDecorations.size();
3881        for (int i = 0; i < count; i++) {
3882            mItemDecorations.get(i).onDraw(c, this, mState);
3883        }
3884    }
3885
3886    @Override
3887    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
3888        return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
3889    }
3890
3891    @Override
3892    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
3893        if (mLayout == null) {
3894            throw new IllegalStateException("RecyclerView has no LayoutManager");
3895        }
3896        return mLayout.generateDefaultLayoutParams();
3897    }
3898
3899    @Override
3900    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
3901        if (mLayout == null) {
3902            throw new IllegalStateException("RecyclerView has no LayoutManager");
3903        }
3904        return mLayout.generateLayoutParams(getContext(), attrs);
3905    }
3906
3907    @Override
3908    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
3909        if (mLayout == null) {
3910            throw new IllegalStateException("RecyclerView has no LayoutManager");
3911        }
3912        return mLayout.generateLayoutParams(p);
3913    }
3914
3915    /**
3916     * Returns true if RecyclerView is currently running some animations.
3917     * <p>
3918     * If you want to be notified when animations are finished, use
3919     * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
3920     *
3921     * @return True if there are some item animations currently running or waiting to be started.
3922     */
3923    public boolean isAnimating() {
3924        return mItemAnimator != null && mItemAnimator.isRunning();
3925    }
3926
3927    void saveOldPositions() {
3928        final int childCount = mChildHelper.getUnfilteredChildCount();
3929        for (int i = 0; i < childCount; i++) {
3930            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3931            if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
3932                throw new IllegalStateException("view holder cannot have position -1 unless it"
3933                        + " is removed");
3934            }
3935            if (!holder.shouldIgnore()) {
3936                holder.saveOldPosition();
3937            }
3938        }
3939    }
3940
3941    void clearOldPositions() {
3942        final int childCount = mChildHelper.getUnfilteredChildCount();
3943        for (int i = 0; i < childCount; i++) {
3944            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3945            if (!holder.shouldIgnore()) {
3946                holder.clearOldPosition();
3947            }
3948        }
3949        mRecycler.clearOldPositions();
3950    }
3951
3952    void offsetPositionRecordsForMove(int from, int to) {
3953        final int childCount = mChildHelper.getUnfilteredChildCount();
3954        final int start, end, inBetweenOffset;
3955        if (from < to) {
3956            start = from;
3957            end = to;
3958            inBetweenOffset = -1;
3959        } else {
3960            start = to;
3961            end = from;
3962            inBetweenOffset = 1;
3963        }
3964
3965        for (int i = 0; i < childCount; i++) {
3966            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3967            if (holder == null || holder.mPosition < start || holder.mPosition > end) {
3968                continue;
3969            }
3970            if (DEBUG) {
3971                Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
3972                        + holder);
3973            }
3974            if (holder.mPosition == from) {
3975                holder.offsetPosition(to - from, false);
3976            } else {
3977                holder.offsetPosition(inBetweenOffset, false);
3978            }
3979
3980            mState.mStructureChanged = true;
3981        }
3982        mRecycler.offsetPositionRecordsForMove(from, to);
3983        requestLayout();
3984    }
3985
3986    void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
3987        final int childCount = mChildHelper.getUnfilteredChildCount();
3988        for (int i = 0; i < childCount; i++) {
3989            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3990            if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
3991                if (DEBUG) {
3992                    Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
3993                            + holder + " now at position " + (holder.mPosition + itemCount));
3994                }
3995                holder.offsetPosition(itemCount, false);
3996                mState.mStructureChanged = true;
3997            }
3998        }
3999        mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
4000        requestLayout();
4001    }
4002
4003    void offsetPositionRecordsForRemove(int positionStart, int itemCount,
4004            boolean applyToPreLayout) {
4005        final int positionEnd = positionStart + itemCount;
4006        final int childCount = mChildHelper.getUnfilteredChildCount();
4007        for (int i = 0; i < childCount; i++) {
4008            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4009            if (holder != null && !holder.shouldIgnore()) {
4010                if (holder.mPosition >= positionEnd) {
4011                    if (DEBUG) {
4012                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4013                                + " holder " + holder + " now at position "
4014                                + (holder.mPosition - itemCount));
4015                    }
4016                    holder.offsetPosition(-itemCount, applyToPreLayout);
4017                    mState.mStructureChanged = true;
4018                } else if (holder.mPosition >= positionStart) {
4019                    if (DEBUG) {
4020                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4021                                + " holder " + holder + " now REMOVED");
4022                    }
4023                    holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
4024                            applyToPreLayout);
4025                    mState.mStructureChanged = true;
4026                }
4027            }
4028        }
4029        mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
4030        requestLayout();
4031    }
4032
4033    /**
4034     * Rebind existing views for the given range, or create as needed.
4035     *
4036     * @param positionStart Adapter position to start at
4037     * @param itemCount Number of views that must explicitly be rebound
4038     */
4039    void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
4040        final int childCount = mChildHelper.getUnfilteredChildCount();
4041        final int positionEnd = positionStart + itemCount;
4042
4043        for (int i = 0; i < childCount; i++) {
4044            final View child = mChildHelper.getUnfilteredChildAt(i);
4045            final ViewHolder holder = getChildViewHolderInt(child);
4046            if (holder == null || holder.shouldIgnore()) {
4047                continue;
4048            }
4049            if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
4050                // We re-bind these view holders after pre-processing is complete so that
4051                // ViewHolders have their final positions assigned.
4052                holder.addFlags(ViewHolder.FLAG_UPDATE);
4053                holder.addChangePayload(payload);
4054                // lp cannot be null since we get ViewHolder from it.
4055                ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
4056            }
4057        }
4058        mRecycler.viewRangeUpdate(positionStart, itemCount);
4059    }
4060
4061    boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
4062        return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
4063                viewHolder.getUnmodifiedPayloads());
4064    }
4065
4066
4067    /**
4068     * Call this method to signal that *all* adapter content has changed (generally, because of
4069     * swapAdapter, or notifyDataSetChanged), and that once layout occurs, all attached items should
4070     * be discarded or animated. Note that this work is deferred because RecyclerView requires a
4071     * layout to resolve non-incremental changes to the data set.
4072     *
4073     * Attached items are labeled as position unknown, and may no longer be cached.
4074     *
4075     * It is still possible for items to be prefetched while mDataSetHasChangedAfterLayout == true,
4076     * so calling this method *must* be associated with marking the cache invalid, so that the
4077     * only valid items that remain in the cache, once layout occurs, are prefetched items.
4078     */
4079    void setDataSetChangedAfterLayout() {
4080        if (mDataSetHasChangedAfterLayout) {
4081            return;
4082        }
4083        mDataSetHasChangedAfterLayout = true;
4084        final int childCount = mChildHelper.getUnfilteredChildCount();
4085        for (int i = 0; i < childCount; i++) {
4086            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4087            if (holder != null && !holder.shouldIgnore()) {
4088                holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
4089            }
4090        }
4091        mRecycler.setAdapterPositionsAsUnknown();
4092
4093        // immediately mark all views as invalid, so prefetched views can be
4094        // differentiated from views bound to previous data set - both in children, and cache
4095        markKnownViewsInvalid();
4096    }
4097
4098    /**
4099     * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
4100     * data change event.
4101     */
4102    void markKnownViewsInvalid() {
4103        final int childCount = mChildHelper.getUnfilteredChildCount();
4104        for (int i = 0; i < childCount; i++) {
4105            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4106            if (holder != null && !holder.shouldIgnore()) {
4107                holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
4108            }
4109        }
4110        markItemDecorInsetsDirty();
4111        mRecycler.markKnownViewsInvalid();
4112    }
4113
4114    /**
4115     * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
4116     * will trigger a {@link #requestLayout()} call.
4117     */
4118    public void invalidateItemDecorations() {
4119        if (mItemDecorations.size() == 0) {
4120            return;
4121        }
4122        if (mLayout != null) {
4123            mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
4124                    + " or layout");
4125        }
4126        markItemDecorInsetsDirty();
4127        requestLayout();
4128    }
4129
4130    /**
4131     * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
4132     * focus even if the View representing the Item is replaced during a layout calculation.
4133     * <p>
4134     * By default, this value is {@code true}.
4135     *
4136     * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
4137     * focus.
4138     *
4139     * @see #setPreserveFocusAfterLayout(boolean)
4140     */
4141    public boolean getPreserveFocusAfterLayout() {
4142        return mPreserveFocusAfterLayout;
4143    }
4144
4145    /**
4146     * Set whether the RecyclerView should try to keep the same Item focused after a layout
4147     * calculation or not.
4148     * <p>
4149     * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
4150     * views may lose focus during a layout calculation as their state changes or they are replaced
4151     * with another view due to type change or animation. In these cases, RecyclerView can request
4152     * focus on the new view automatically.
4153     *
4154     * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
4155     *                                 layout calculations. Defaults to true.
4156     *
4157     * @see #getPreserveFocusAfterLayout()
4158     */
4159    public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
4160        mPreserveFocusAfterLayout = preserveFocusAfterLayout;
4161    }
4162
4163    /**
4164     * Retrieve the {@link ViewHolder} for the given child view.
4165     *
4166     * @param child Child of this RecyclerView to query for its ViewHolder
4167     * @return The child view's ViewHolder
4168     */
4169    public ViewHolder getChildViewHolder(View child) {
4170        final ViewParent parent = child.getParent();
4171        if (parent != null && parent != this) {
4172            throw new IllegalArgumentException("View " + child + " is not a direct child of "
4173                    + this);
4174        }
4175        return getChildViewHolderInt(child);
4176    }
4177
4178    /**
4179     * Traverses the ancestors of the given view and returns the item view that contains it and
4180     * also a direct child of the RecyclerView. This returned view can be used to get the
4181     * ViewHolder by calling {@link #getChildViewHolder(View)}.
4182     *
4183     * @param view The view that is a descendant of the RecyclerView.
4184     *
4185     * @return The direct child of the RecyclerView which contains the given view or null if the
4186     * provided view is not a descendant of this RecyclerView.
4187     *
4188     * @see #getChildViewHolder(View)
4189     * @see #findContainingViewHolder(View)
4190     */
4191    @Nullable
4192    public View findContainingItemView(View view) {
4193        ViewParent parent = view.getParent();
4194        while (parent != null && parent != this && parent instanceof View) {
4195            view = (View) parent;
4196            parent = view.getParent();
4197        }
4198        return parent == this ? view : null;
4199    }
4200
4201    /**
4202     * Returns the ViewHolder that contains the given view.
4203     *
4204     * @param view The view that is a descendant of the RecyclerView.
4205     *
4206     * @return The ViewHolder that contains the given view or null if the provided view is not a
4207     * descendant of this RecyclerView.
4208     */
4209    @Nullable
4210    public ViewHolder findContainingViewHolder(View view) {
4211        View itemView = findContainingItemView(view);
4212        return itemView == null ? null : getChildViewHolder(itemView);
4213    }
4214
4215
4216    static ViewHolder getChildViewHolderInt(View child) {
4217        if (child == null) {
4218            return null;
4219        }
4220        return ((LayoutParams) child.getLayoutParams()).mViewHolder;
4221    }
4222
4223    /**
4224     * @deprecated use {@link #getChildAdapterPosition(View)} or
4225     * {@link #getChildLayoutPosition(View)}.
4226     */
4227    @Deprecated
4228    public int getChildPosition(View child) {
4229        return getChildAdapterPosition(child);
4230    }
4231
4232    /**
4233     * Return the adapter position that the given child view corresponds to.
4234     *
4235     * @param child Child View to query
4236     * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
4237     */
4238    public int getChildAdapterPosition(View child) {
4239        final ViewHolder holder = getChildViewHolderInt(child);
4240        return holder != null ? holder.getAdapterPosition() : NO_POSITION;
4241    }
4242
4243    /**
4244     * Return the adapter position of the given child view as of the latest completed layout pass.
4245     * <p>
4246     * This position may not be equal to Item's adapter position if there are pending changes
4247     * in the adapter which have not been reflected to the layout yet.
4248     *
4249     * @param child Child View to query
4250     * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
4251     * the View is representing a removed item.
4252     */
4253    public int getChildLayoutPosition(View child) {
4254        final ViewHolder holder = getChildViewHolderInt(child);
4255        return holder != null ? holder.getLayoutPosition() : NO_POSITION;
4256    }
4257
4258    /**
4259     * Return the stable item id that the given child view corresponds to.
4260     *
4261     * @param child Child View to query
4262     * @return Item id corresponding to the given view or {@link #NO_ID}
4263     */
4264    public long getChildItemId(View child) {
4265        if (mAdapter == null || !mAdapter.hasStableIds()) {
4266            return NO_ID;
4267        }
4268        final ViewHolder holder = getChildViewHolderInt(child);
4269        return holder != null ? holder.getItemId() : NO_ID;
4270    }
4271
4272    /**
4273     * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
4274     * {@link #findViewHolderForAdapterPosition(int)}
4275     */
4276    @Deprecated
4277    public ViewHolder findViewHolderForPosition(int position) {
4278        return findViewHolderForPosition(position, false);
4279    }
4280
4281    /**
4282     * Return the ViewHolder for the item in the given position of the data set as of the latest
4283     * layout pass.
4284     * <p>
4285     * This method checks only the children of RecyclerView. If the item at the given
4286     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4287     * <p>
4288     * Note that when Adapter contents change, ViewHolder positions are not updated until the
4289     * next layout calculation. If there are pending adapter updates, the return value of this
4290     * method may not match your adapter contents. You can use
4291     * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
4292     * <p>
4293     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4294     * with the same layout position representing the same Item. In this case, the updated
4295     * ViewHolder will be returned.
4296     *
4297     * @param position The position of the item in the data set of the adapter
4298     * @return The ViewHolder at <code>position</code> or null if there is no such item
4299     */
4300    public ViewHolder findViewHolderForLayoutPosition(int position) {
4301        return findViewHolderForPosition(position, false);
4302    }
4303
4304    /**
4305     * Return the ViewHolder for the item in the given position of the data set. Unlike
4306     * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
4307     * adapter changes that may not be reflected to the layout yet. On the other hand, if
4308     * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
4309     * calculated yet, this method will return <code>null</code> since the new positions of views
4310     * are unknown until the layout is calculated.
4311     * <p>
4312     * This method checks only the children of RecyclerView. If the item at the given
4313     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4314     * <p>
4315     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4316     * representing the same Item. In this case, the updated ViewHolder will be returned.
4317     *
4318     * @param position The position of the item in the data set of the adapter
4319     * @return The ViewHolder at <code>position</code> or null if there is no such item
4320     */
4321    public ViewHolder findViewHolderForAdapterPosition(int position) {
4322        if (mDataSetHasChangedAfterLayout) {
4323            return null;
4324        }
4325        final int childCount = mChildHelper.getUnfilteredChildCount();
4326        // hidden VHs are not preferred but if that is the only one we find, we rather return it
4327        ViewHolder hidden = null;
4328        for (int i = 0; i < childCount; i++) {
4329            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4330            if (holder != null && !holder.isRemoved()
4331                    && getAdapterPositionFor(holder) == position) {
4332                if (mChildHelper.isHidden(holder.itemView)) {
4333                    hidden = holder;
4334                } else {
4335                    return holder;
4336                }
4337            }
4338        }
4339        return hidden;
4340    }
4341
4342    ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
4343        final int childCount = mChildHelper.getUnfilteredChildCount();
4344        ViewHolder hidden = null;
4345        for (int i = 0; i < childCount; i++) {
4346            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4347            if (holder != null && !holder.isRemoved()) {
4348                if (checkNewPosition) {
4349                    if (holder.mPosition != position) {
4350                        continue;
4351                    }
4352                } else if (holder.getLayoutPosition() != position) {
4353                    continue;
4354                }
4355                if (mChildHelper.isHidden(holder.itemView)) {
4356                    hidden = holder;
4357                } else {
4358                    return holder;
4359                }
4360            }
4361        }
4362        // This method should not query cached views. It creates a problem during adapter updates
4363        // when we are dealing with already laid out views. Also, for the public method, it is more
4364        // reasonable to return null if position is not laid out.
4365        return hidden;
4366    }
4367
4368    /**
4369     * Return the ViewHolder for the item with the given id. The RecyclerView must
4370     * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
4371     * return a non-null value.
4372     * <p>
4373     * This method checks only the children of RecyclerView. If the item with the given
4374     * <code>id</code> is not laid out, it <em>will not</em> create a new one.
4375     *
4376     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
4377     * same id. In this case, the updated ViewHolder will be returned.
4378     *
4379     * @param id The id for the requested item
4380     * @return The ViewHolder with the given <code>id</code> or null if there is no such item
4381     */
4382    public ViewHolder findViewHolderForItemId(long id) {
4383        if (mAdapter == null || !mAdapter.hasStableIds()) {
4384            return null;
4385        }
4386        final int childCount = mChildHelper.getUnfilteredChildCount();
4387        ViewHolder hidden = null;
4388        for (int i = 0; i < childCount; i++) {
4389            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4390            if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
4391                if (mChildHelper.isHidden(holder.itemView)) {
4392                    hidden = holder;
4393                } else {
4394                    return holder;
4395                }
4396            }
4397        }
4398        return hidden;
4399    }
4400
4401    /**
4402     * Find the topmost view under the given point.
4403     *
4404     * @param x Horizontal position in pixels to search
4405     * @param y Vertical position in pixels to search
4406     * @return The child view under (x, y) or null if no matching child is found
4407     */
4408    public View findChildViewUnder(float x, float y) {
4409        final int count = mChildHelper.getChildCount();
4410        for (int i = count - 1; i >= 0; i--) {
4411            final View child = mChildHelper.getChildAt(i);
4412            final float translationX = child.getTranslationX();
4413            final float translationY = child.getTranslationY();
4414            if (x >= child.getLeft() + translationX
4415                    && x <= child.getRight() + translationX
4416                    && y >= child.getTop() + translationY
4417                    && y <= child.getBottom() + translationY) {
4418                return child;
4419            }
4420        }
4421        return null;
4422    }
4423
4424    @Override
4425    public boolean drawChild(Canvas canvas, View child, long drawingTime) {
4426        return super.drawChild(canvas, child, drawingTime);
4427    }
4428
4429    /**
4430     * Offset the bounds of all child views by <code>dy</code> pixels.
4431     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4432     *
4433     * @param dy Vertical pixel offset to apply to the bounds of all child views
4434     */
4435    public void offsetChildrenVertical(int dy) {
4436        final int childCount = mChildHelper.getChildCount();
4437        for (int i = 0; i < childCount; i++) {
4438            mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
4439        }
4440    }
4441
4442    /**
4443     * Called when an item view is attached to this RecyclerView.
4444     *
4445     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4446     * of child views as they become attached. This will be called before a
4447     * {@link LayoutManager} measures or lays out the view and is a good time to perform these
4448     * changes.</p>
4449     *
4450     * @param child Child view that is now attached to this RecyclerView and its associated window
4451     */
4452    public void onChildAttachedToWindow(View child) {
4453    }
4454
4455    /**
4456     * Called when an item view is detached from this RecyclerView.
4457     *
4458     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4459     * of child views as they become detached. This will be called as a
4460     * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
4461     *
4462     * @param child Child view that is now detached from this RecyclerView and its associated window
4463     */
4464    public void onChildDetachedFromWindow(View child) {
4465    }
4466
4467    /**
4468     * Offset the bounds of all child views by <code>dx</code> pixels.
4469     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4470     *
4471     * @param dx Horizontal pixel offset to apply to the bounds of all child views
4472     */
4473    public void offsetChildrenHorizontal(int dx) {
4474        final int childCount = mChildHelper.getChildCount();
4475        for (int i = 0; i < childCount; i++) {
4476            mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
4477        }
4478    }
4479
4480    /**
4481     * Returns the bounds of the view including its decoration and margins.
4482     *
4483     * @param view The view element to check
4484     * @param outBounds A rect that will receive the bounds of the element including its
4485     *                  decoration and margins.
4486     */
4487    public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
4488        getDecoratedBoundsWithMarginsInt(view, outBounds);
4489    }
4490
4491    static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
4492        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
4493        final Rect insets = lp.mDecorInsets;
4494        outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
4495                view.getTop() - insets.top - lp.topMargin,
4496                view.getRight() + insets.right + lp.rightMargin,
4497                view.getBottom() + insets.bottom + lp.bottomMargin);
4498    }
4499
4500    Rect getItemDecorInsetsForChild(View child) {
4501        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
4502        if (!lp.mInsetsDirty) {
4503            return lp.mDecorInsets;
4504        }
4505
4506        if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
4507            // changed/invalid items should not be updated until they are rebound.
4508            return lp.mDecorInsets;
4509        }
4510        final Rect insets = lp.mDecorInsets;
4511        insets.set(0, 0, 0, 0);
4512        final int decorCount = mItemDecorations.size();
4513        for (int i = 0; i < decorCount; i++) {
4514            mTempRect.set(0, 0, 0, 0);
4515            mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
4516            insets.left += mTempRect.left;
4517            insets.top += mTempRect.top;
4518            insets.right += mTempRect.right;
4519            insets.bottom += mTempRect.bottom;
4520        }
4521        lp.mInsetsDirty = false;
4522        return insets;
4523    }
4524
4525    /**
4526     * Called when the scroll position of this RecyclerView changes. Subclasses should use
4527     * this method to respond to scrolling within the adapter's data set instead of an explicit
4528     * listener.
4529     *
4530     * <p>This method will always be invoked before listeners. If a subclass needs to perform
4531     * any additional upkeep or bookkeeping after scrolling but before listeners run,
4532     * this is a good place to do so.</p>
4533     *
4534     * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
4535     * the distance scrolled in either direction within the adapter's data set instead of absolute
4536     * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
4537     * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
4538     * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
4539     * do not correspond to the data set scroll position. However, some subclasses may choose
4540     * to use these fields as special offsets.</p>
4541     *
4542     * @param dx horizontal distance scrolled in pixels
4543     * @param dy vertical distance scrolled in pixels
4544     */
4545    public void onScrolled(int dx, int dy) {
4546        // Do nothing
4547    }
4548
4549    void dispatchOnScrolled(int hresult, int vresult) {
4550        mDispatchScrollCounter++;
4551        // Pass the current scrollX/scrollY values; no actual change in these properties occurred
4552        // but some general-purpose code may choose to respond to changes this way.
4553        final int scrollX = getScrollX();
4554        final int scrollY = getScrollY();
4555        onScrollChanged(scrollX, scrollY, scrollX, scrollY);
4556
4557        // Pass the real deltas to onScrolled, the RecyclerView-specific method.
4558        onScrolled(hresult, vresult);
4559
4560        // Invoke listeners last. Subclassed view methods always handle the event first.
4561        // All internal state is consistent by the time listeners are invoked.
4562        if (mScrollListener != null) {
4563            mScrollListener.onScrolled(this, hresult, vresult);
4564        }
4565        if (mScrollListeners != null) {
4566            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4567                mScrollListeners.get(i).onScrolled(this, hresult, vresult);
4568            }
4569        }
4570        mDispatchScrollCounter--;
4571    }
4572
4573    /**
4574     * Called when the scroll state of this RecyclerView changes. Subclasses should use this
4575     * method to respond to state changes instead of an explicit listener.
4576     *
4577     * <p>This method will always be invoked before listeners, but after the LayoutManager
4578     * responds to the scroll state change.</p>
4579     *
4580     * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
4581     *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
4582     */
4583    public void onScrollStateChanged(int state) {
4584        // Do nothing
4585    }
4586
4587    void dispatchOnScrollStateChanged(int state) {
4588        // Let the LayoutManager go first; this allows it to bring any properties into
4589        // a consistent state before the RecyclerView subclass responds.
4590        if (mLayout != null) {
4591            mLayout.onScrollStateChanged(state);
4592        }
4593
4594        // Let the RecyclerView subclass handle this event next; any LayoutManager property
4595        // changes will be reflected by this time.
4596        onScrollStateChanged(state);
4597
4598        // Listeners go last. All other internal state is consistent by this point.
4599        if (mScrollListener != null) {
4600            mScrollListener.onScrollStateChanged(this, state);
4601        }
4602        if (mScrollListeners != null) {
4603            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4604                mScrollListeners.get(i).onScrollStateChanged(this, state);
4605            }
4606        }
4607    }
4608
4609    /**
4610     * Returns whether there are pending adapter updates which are not yet applied to the layout.
4611     * <p>
4612     * If this method returns <code>true</code>, it means that what user is currently seeing may not
4613     * reflect them adapter contents (depending on what has changed).
4614     * You may use this information to defer or cancel some operations.
4615     * <p>
4616     * This method returns true if RecyclerView has not yet calculated the first layout after it is
4617     * attached to the Window or the Adapter has been replaced.
4618     *
4619     * @return True if there are some adapter updates which are not yet reflected to layout or false
4620     * if layout is up to date.
4621     */
4622    public boolean hasPendingAdapterUpdates() {
4623        return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
4624                || mAdapterHelper.hasPendingUpdates();
4625    }
4626
4627    class ViewFlinger implements Runnable {
4628        private int mLastFlingX;
4629        private int mLastFlingY;
4630        private OverScroller mScroller;
4631        Interpolator mInterpolator = sQuinticInterpolator;
4632
4633
4634        // When set to true, postOnAnimation callbacks are delayed until the run method completes
4635        private boolean mEatRunOnAnimationRequest = false;
4636
4637        // Tracks if postAnimationCallback should be re-attached when it is done
4638        private boolean mReSchedulePostAnimationCallback = false;
4639
4640        ViewFlinger() {
4641            mScroller = new OverScroller(getContext(), sQuinticInterpolator);
4642        }
4643
4644        @Override
4645        public void run() {
4646            if (mLayout == null) {
4647                stop();
4648                return; // no layout, cannot scroll.
4649            }
4650            disableRunOnAnimationRequests();
4651            consumePendingUpdateOperations();
4652            // keep a local reference so that if it is changed during onAnimation method, it won't
4653            // cause unexpected behaviors
4654            final OverScroller scroller = mScroller;
4655            final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
4656            if (scroller.computeScrollOffset()) {
4657                final int x = scroller.getCurrX();
4658                final int y = scroller.getCurrY();
4659                final int dx = x - mLastFlingX;
4660                final int dy = y - mLastFlingY;
4661                int hresult = 0;
4662                int vresult = 0;
4663                mLastFlingX = x;
4664                mLastFlingY = y;
4665                int overscrollX = 0, overscrollY = 0;
4666                if (mAdapter != null) {
4667                    eatRequestLayout();
4668                    onEnterLayoutOrScroll();
4669                    Trace.beginSection(TRACE_SCROLL_TAG);
4670                    if (dx != 0) {
4671                        hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
4672                        overscrollX = dx - hresult;
4673                    }
4674                    if (dy != 0) {
4675                        vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
4676                        overscrollY = dy - vresult;
4677                    }
4678                    Trace.endSection();
4679                    repositionShadowingViews();
4680
4681                    onExitLayoutOrScroll();
4682                    resumeRequestLayout(false);
4683
4684                    if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
4685                            && smoothScroller.isRunning()) {
4686                        final int adapterSize = mState.getItemCount();
4687                        if (adapterSize == 0) {
4688                            smoothScroller.stop();
4689                        } else if (smoothScroller.getTargetPosition() >= adapterSize) {
4690                            smoothScroller.setTargetPosition(adapterSize - 1);
4691                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4692                        } else {
4693                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4694                        }
4695                    }
4696                }
4697                if (!mItemDecorations.isEmpty()) {
4698                    invalidate();
4699                }
4700                if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
4701                    considerReleasingGlowsOnScroll(dx, dy);
4702                }
4703                if (overscrollX != 0 || overscrollY != 0) {
4704                    final int vel = (int) scroller.getCurrVelocity();
4705
4706                    int velX = 0;
4707                    if (overscrollX != x) {
4708                        velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
4709                    }
4710
4711                    int velY = 0;
4712                    if (overscrollY != y) {
4713                        velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
4714                    }
4715
4716                    if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
4717                        absorbGlows(velX, velY);
4718                    }
4719                    if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
4720                            && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
4721                        scroller.abortAnimation();
4722                    }
4723                }
4724                if (hresult != 0 || vresult != 0) {
4725                    dispatchOnScrolled(hresult, vresult);
4726                }
4727
4728                if (!awakenScrollBars()) {
4729                    invalidate();
4730                }
4731
4732                final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
4733                        && vresult == dy;
4734                final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
4735                        && hresult == dx;
4736                final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
4737                        || fullyConsumedVertical;
4738
4739                if (scroller.isFinished() || !fullyConsumedAny) {
4740                    setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this.
4741                    if (ALLOW_THREAD_GAP_WORK) {
4742                        mPrefetchRegistry.clearPrefetchPositions();
4743                    }
4744                } else {
4745                    postOnAnimation();
4746                    if (mGapWorker != null) {
4747                        mGapWorker.postFromTraversal(RecyclerView.this, dx, dy);
4748                    }
4749                }
4750            }
4751            // call this after the onAnimation is complete not to have inconsistent callbacks etc.
4752            if (smoothScroller != null) {
4753                if (smoothScroller.isPendingInitialRun()) {
4754                    smoothScroller.onAnimation(0, 0);
4755                }
4756                if (!mReSchedulePostAnimationCallback) {
4757                    smoothScroller.stop(); //stop if it does not trigger any scroll
4758                }
4759            }
4760            enableRunOnAnimationRequests();
4761        }
4762
4763        private void disableRunOnAnimationRequests() {
4764            mReSchedulePostAnimationCallback = false;
4765            mEatRunOnAnimationRequest = true;
4766        }
4767
4768        private void enableRunOnAnimationRequests() {
4769            mEatRunOnAnimationRequest = false;
4770            if (mReSchedulePostAnimationCallback) {
4771                postOnAnimation();
4772            }
4773        }
4774
4775        void postOnAnimation() {
4776            if (mEatRunOnAnimationRequest) {
4777                mReSchedulePostAnimationCallback = true;
4778            } else {
4779                removeCallbacks(this);
4780                RecyclerView.this.postOnAnimation(this);
4781            }
4782        }
4783
4784        public void fling(int velocityX, int velocityY) {
4785            setScrollState(SCROLL_STATE_SETTLING);
4786            mLastFlingX = mLastFlingY = 0;
4787            mScroller.fling(0, 0, velocityX, velocityY,
4788                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
4789            postOnAnimation();
4790        }
4791
4792        public void smoothScrollBy(int dx, int dy) {
4793            smoothScrollBy(dx, dy, 0, 0);
4794        }
4795
4796        public void smoothScrollBy(int dx, int dy, int vx, int vy) {
4797            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
4798        }
4799
4800        private float distanceInfluenceForSnapDuration(float f) {
4801            f -= 0.5f; // center the values about 0.
4802            f *= 0.3f * Math.PI / 2.0f;
4803            return (float) Math.sin(f);
4804        }
4805
4806        private int computeScrollDuration(int dx, int dy, int vx, int vy) {
4807            final int absDx = Math.abs(dx);
4808            final int absDy = Math.abs(dy);
4809            final boolean horizontal = absDx > absDy;
4810            final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
4811            final int delta = (int) Math.sqrt(dx * dx + dy * dy);
4812            final int containerSize = horizontal ? getWidth() : getHeight();
4813            final int halfContainerSize = containerSize / 2;
4814            final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
4815            final float distance = halfContainerSize + halfContainerSize
4816                    * distanceInfluenceForSnapDuration(distanceRatio);
4817
4818            final int duration;
4819            if (velocity > 0) {
4820                duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
4821            } else {
4822                float absDelta = (float) (horizontal ? absDx : absDy);
4823                duration = (int) (((absDelta / containerSize) + 1) * 300);
4824            }
4825            return Math.min(duration, MAX_SCROLL_DURATION);
4826        }
4827
4828        public void smoothScrollBy(int dx, int dy, int duration) {
4829            smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
4830        }
4831
4832        public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
4833            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, 0, 0),
4834                    interpolator == null ? sQuinticInterpolator : interpolator);
4835        }
4836
4837        public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
4838            if (mInterpolator != interpolator) {
4839                mInterpolator = interpolator;
4840                mScroller = new OverScroller(getContext(), interpolator);
4841            }
4842            setScrollState(SCROLL_STATE_SETTLING);
4843            mLastFlingX = mLastFlingY = 0;
4844            mScroller.startScroll(0, 0, dx, dy, duration);
4845            postOnAnimation();
4846        }
4847
4848        public void stop() {
4849            removeCallbacks(this);
4850            mScroller.abortAnimation();
4851        }
4852
4853    }
4854
4855    void repositionShadowingViews() {
4856        // Fix up shadow views used by change animations
4857        int count = mChildHelper.getChildCount();
4858        for (int i = 0; i < count; i++) {
4859            View view = mChildHelper.getChildAt(i);
4860            ViewHolder holder = getChildViewHolder(view);
4861            if (holder != null && holder.mShadowingHolder != null) {
4862                View shadowingView = holder.mShadowingHolder.itemView;
4863                int left = view.getLeft();
4864                int top = view.getTop();
4865                if (left != shadowingView.getLeft() ||  top != shadowingView.getTop()) {
4866                    shadowingView.layout(left, top,
4867                            left + shadowingView.getWidth(),
4868                            top + shadowingView.getHeight());
4869                }
4870            }
4871        }
4872    }
4873
4874    private class RecyclerViewDataObserver extends AdapterDataObserver {
4875        RecyclerViewDataObserver() {
4876        }
4877
4878        @Override
4879        public void onChanged() {
4880            assertNotInLayoutOrScroll(null);
4881            mState.mStructureChanged = true;
4882
4883            setDataSetChangedAfterLayout();
4884            if (!mAdapterHelper.hasPendingUpdates()) {
4885                requestLayout();
4886            }
4887        }
4888
4889        @Override
4890        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
4891            assertNotInLayoutOrScroll(null);
4892            if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
4893                triggerUpdateProcessor();
4894            }
4895        }
4896
4897        @Override
4898        public void onItemRangeInserted(int positionStart, int itemCount) {
4899            assertNotInLayoutOrScroll(null);
4900            if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
4901                triggerUpdateProcessor();
4902            }
4903        }
4904
4905        @Override
4906        public void onItemRangeRemoved(int positionStart, int itemCount) {
4907            assertNotInLayoutOrScroll(null);
4908            if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
4909                triggerUpdateProcessor();
4910            }
4911        }
4912
4913        @Override
4914        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
4915            assertNotInLayoutOrScroll(null);
4916            if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
4917                triggerUpdateProcessor();
4918            }
4919        }
4920
4921        void triggerUpdateProcessor() {
4922            if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
4923                RecyclerView.this.postOnAnimation(mUpdateChildViewsRunnable);
4924            } else {
4925                mAdapterUpdateDuringMeasure = true;
4926                requestLayout();
4927            }
4928        }
4929    }
4930
4931    /**
4932     * RecycledViewPool lets you share Views between multiple RecyclerViews.
4933     * <p>
4934     * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
4935     * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
4936     * <p>
4937     * RecyclerView automatically creates a pool for itself if you don't provide one.
4938     *
4939     */
4940    public static class RecycledViewPool {
4941        private static final int DEFAULT_MAX_SCRAP = 5;
4942
4943        /**
4944         * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
4945         *
4946         * Note that this tracks running averages of create/bind time across all RecyclerViews
4947         * (and, indirectly, Adapters) that use this pool.
4948         *
4949         * 1) This enables us to track average create and bind times across multiple adapters. Even
4950         * though create (and especially bind) may behave differently for different Adapter
4951         * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
4952         *
4953         * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
4954         * false for all other views of its type for the same deadline. This prevents items
4955         * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
4956         */
4957        static class ScrapData {
4958            ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
4959            int mMaxScrap = DEFAULT_MAX_SCRAP;
4960            long mCreateRunningAverageNs = 0;
4961            long mBindRunningAverageNs = 0;
4962        }
4963        SparseArray<ScrapData> mScrap = new SparseArray<>();
4964
4965        private int mAttachCount = 0;
4966
4967        public void clear() {
4968            for (int i = 0; i < mScrap.size(); i++) {
4969                ScrapData data = mScrap.valueAt(i);
4970                data.mScrapHeap.clear();
4971            }
4972        }
4973
4974        public void setMaxRecycledViews(int viewType, int max) {
4975            ScrapData scrapData = getScrapDataForType(viewType);
4976            scrapData.mMaxScrap = max;
4977            final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
4978            if (scrapHeap != null) {
4979                while (scrapHeap.size() > max) {
4980                    scrapHeap.remove(scrapHeap.size() - 1);
4981                }
4982            }
4983        }
4984
4985        /**
4986         * Returns the current number of Views held by the RecycledViewPool of the given view type.
4987         */
4988        public int getRecycledViewCount(int viewType) {
4989            return getScrapDataForType(viewType).mScrapHeap.size();
4990        }
4991
4992        public ViewHolder getRecycledView(int viewType) {
4993            final ScrapData scrapData = mScrap.get(viewType);
4994            if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
4995                final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
4996                return scrapHeap.remove(scrapHeap.size() - 1);
4997            }
4998            return null;
4999        }
5000
5001        int size() {
5002            int count = 0;
5003            for (int i = 0; i < mScrap.size(); i++) {
5004                ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
5005                if (viewHolders != null) {
5006                    count += viewHolders.size();
5007                }
5008            }
5009            return count;
5010        }
5011
5012        public void putRecycledView(ViewHolder scrap) {
5013            final int viewType = scrap.getItemViewType();
5014            final ArrayList scrapHeap = getScrapDataForType(viewType).mScrapHeap;
5015            if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
5016                return;
5017            }
5018            if (DEBUG && scrapHeap.contains(scrap)) {
5019                throw new IllegalArgumentException("this scrap item already exists");
5020            }
5021            scrap.resetInternal();
5022            scrapHeap.add(scrap);
5023        }
5024
5025        long runningAverage(long oldAverage, long newValue) {
5026            if (oldAverage == 0) {
5027                return newValue;
5028            }
5029            return (oldAverage / 4 * 3) + (newValue / 4);
5030        }
5031
5032        void factorInCreateTime(int viewType, long createTimeNs) {
5033            ScrapData scrapData = getScrapDataForType(viewType);
5034            scrapData.mCreateRunningAverageNs = runningAverage(
5035                    scrapData.mCreateRunningAverageNs, createTimeNs);
5036        }
5037
5038        void factorInBindTime(int viewType, long bindTimeNs) {
5039            ScrapData scrapData = getScrapDataForType(viewType);
5040            scrapData.mBindRunningAverageNs = runningAverage(
5041                    scrapData.mBindRunningAverageNs, bindTimeNs);
5042        }
5043
5044        boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5045            long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
5046            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5047        }
5048
5049        boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5050            long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
5051            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5052        }
5053
5054        void attach(Adapter adapter) {
5055            mAttachCount++;
5056        }
5057
5058        void detach() {
5059            mAttachCount--;
5060        }
5061
5062
5063        /**
5064         * Detaches the old adapter and attaches the new one.
5065         * <p>
5066         * RecycledViewPool will clear its cache if it has only one adapter attached and the new
5067         * adapter uses a different ViewHolder than the oldAdapter.
5068         *
5069         * @param oldAdapter The previous adapter instance. Will be detached.
5070         * @param newAdapter The new adapter instance. Will be attached.
5071         * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
5072         *                               ViewHolder and view types.
5073         */
5074        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
5075                boolean compatibleWithPrevious) {
5076            if (oldAdapter != null) {
5077                detach();
5078            }
5079            if (!compatibleWithPrevious && mAttachCount == 0) {
5080                clear();
5081            }
5082            if (newAdapter != null) {
5083                attach(newAdapter);
5084            }
5085        }
5086
5087        private ScrapData getScrapDataForType(int viewType) {
5088            ScrapData scrapData = mScrap.get(viewType);
5089            if (scrapData == null) {
5090                scrapData = new ScrapData();
5091                mScrap.put(viewType, scrapData);
5092            }
5093            return scrapData;
5094        }
5095    }
5096
5097    /**
5098     * Utility method for finding an internal RecyclerView, if present
5099     */
5100    @Nullable
5101    static RecyclerView findNestedRecyclerView(@NonNull View view) {
5102        if (!(view instanceof ViewGroup)) {
5103            return null;
5104        }
5105        if (view instanceof RecyclerView) {
5106            return (RecyclerView) view;
5107        }
5108        final ViewGroup parent = (ViewGroup) view;
5109        final int count = parent.getChildCount();
5110        for (int i = 0; i < count; i++) {
5111            final View child = parent.getChildAt(i);
5112            final RecyclerView descendant = findNestedRecyclerView(child);
5113            if (descendant != null) {
5114                return descendant;
5115            }
5116        }
5117        return null;
5118    }
5119
5120    /**
5121     * Utility method for clearing holder's internal RecyclerView, if present
5122     */
5123    static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
5124        if (holder.mNestedRecyclerView != null) {
5125            View item = holder.mNestedRecyclerView.get();
5126            while (item != null) {
5127                if (item == holder.itemView) {
5128                    return; // match found, don't need to clear
5129                }
5130
5131                ViewParent parent = item.getParent();
5132                if (parent instanceof View) {
5133                    item = (View) parent;
5134                } else {
5135                    item = null;
5136                }
5137            }
5138            holder.mNestedRecyclerView = null; // not nested
5139        }
5140    }
5141
5142    /**
5143     * Time base for deadline-aware work scheduling. Overridable for testing.
5144     *
5145     * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
5146     * isn't relevant.
5147     */
5148    long getNanoTime() {
5149        if (ALLOW_THREAD_GAP_WORK) {
5150            return System.nanoTime();
5151        } else {
5152            return 0;
5153        }
5154    }
5155
5156    /**
5157     * A Recycler is responsible for managing scrapped or detached item views for reuse.
5158     *
5159     * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
5160     * that has been marked for removal or reuse.</p>
5161     *
5162     * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
5163     * an adapter's data set representing the data at a given position or item ID.
5164     * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
5165     * If not, the view can be quickly reused by the LayoutManager with no further work.
5166     * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
5167     * may be repositioned by a LayoutManager without remeasurement.</p>
5168     */
5169    public final class Recycler {
5170        final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
5171        ArrayList<ViewHolder> mChangedScrap = null;
5172
5173        final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
5174
5175        private final List<ViewHolder>
5176                mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
5177
5178        private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
5179        int mViewCacheMax = DEFAULT_CACHE_SIZE;
5180
5181        RecycledViewPool mRecyclerPool;
5182
5183        private ViewCacheExtension mViewCacheExtension;
5184
5185        static final int DEFAULT_CACHE_SIZE = 2;
5186
5187        /**
5188         * Clear scrap views out of this recycler. Detached views contained within a
5189         * recycled view pool will remain.
5190         */
5191        public void clear() {
5192            mAttachedScrap.clear();
5193            recycleAndClearCachedViews();
5194        }
5195
5196        /**
5197         * Set the maximum number of detached, valid views we should retain for later use.
5198         *
5199         * @param viewCount Number of views to keep before sending views to the shared pool
5200         */
5201        public void setViewCacheSize(int viewCount) {
5202            mRequestedCacheMax = viewCount;
5203            updateViewCacheSize();
5204        }
5205
5206        void updateViewCacheSize() {
5207            int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
5208            mViewCacheMax = mRequestedCacheMax + extraCache;
5209
5210            // first, try the views that can be recycled
5211            for (int i = mCachedViews.size() - 1;
5212                    i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
5213                recycleCachedViewAt(i);
5214            }
5215        }
5216
5217        /**
5218         * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
5219         *
5220         * @return List of ViewHolders in the scrap list.
5221         */
5222        public List<ViewHolder> getScrapList() {
5223            return mUnmodifiableAttachedScrap;
5224        }
5225
5226        /**
5227         * Helper method for getViewForPosition.
5228         * <p>
5229         * Checks whether a given view holder can be used for the provided position.
5230         *
5231         * @param holder ViewHolder
5232         * @return true if ViewHolder matches the provided position, false otherwise
5233         */
5234        boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
5235            // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
5236            // if it is not removed, verify the type and id.
5237            if (holder.isRemoved()) {
5238                if (DEBUG && !mState.isPreLayout()) {
5239                    throw new IllegalStateException("should not receive a removed view unless it"
5240                            + " is pre layout");
5241                }
5242                return mState.isPreLayout();
5243            }
5244            if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
5245                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
5246                        + "adapter position" + holder);
5247            }
5248            if (!mState.isPreLayout()) {
5249                // don't check type if it is pre-layout.
5250                final int type = mAdapter.getItemViewType(holder.mPosition);
5251                if (type != holder.getItemViewType()) {
5252                    return false;
5253                }
5254            }
5255            if (mAdapter.hasStableIds()) {
5256                return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
5257            }
5258            return true;
5259        }
5260
5261        /**
5262         * Attempts to bind view, and account for relevant timing information. If
5263         * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
5264         *
5265         * @param holder Holder to be bound.
5266         * @param offsetPosition Position of item to be bound.
5267         * @param position Pre-layout position of item to be bound.
5268         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5269         *                   complete. If FOREVER_NS is passed, this method will not fail to
5270         *                   bind the holder.
5271         * @return
5272         */
5273        private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,
5274                int position, long deadlineNs) {
5275            holder.mOwnerRecyclerView = RecyclerView.this;
5276            final int viewType = holder.getItemViewType();
5277            long startBindNs = getNanoTime();
5278            if (deadlineNs != FOREVER_NS
5279                    && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
5280                // abort - we have a deadline we can't meet
5281                return false;
5282            }
5283            mAdapter.bindViewHolder(holder, offsetPosition);
5284            long endBindNs = getNanoTime();
5285            mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
5286            attachAccessibilityDelegate(holder.itemView);
5287            if (mState.isPreLayout()) {
5288                holder.mPreLayoutPosition = position;
5289            }
5290            return true;
5291        }
5292
5293        /**
5294         * Binds the given View to the position. The View can be a View previously retrieved via
5295         * {@link #getViewForPosition(int)} or created by
5296         * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
5297         * <p>
5298         * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
5299         * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
5300         * wants to handle its own recycling logic.
5301         * <p>
5302         * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
5303         * you don't need to call this method unless you want to bind this View to another position.
5304         *
5305         * @param view The view to update.
5306         * @param position The position of the item to bind to this View.
5307         */
5308        public void bindViewToPosition(View view, int position) {
5309            ViewHolder holder = getChildViewHolderInt(view);
5310            if (holder == null) {
5311                throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
5312                        + " pass arbitrary views to this method, they should be created by the "
5313                        + "Adapter");
5314            }
5315            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5316            if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5317                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5318                        + "position " + position + "(offset:" + offsetPosition + ")."
5319                        + "state:" + mState.getItemCount());
5320            }
5321            tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
5322
5323            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5324            final LayoutParams rvLayoutParams;
5325            if (lp == null) {
5326                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5327                holder.itemView.setLayoutParams(rvLayoutParams);
5328            } else if (!checkLayoutParams(lp)) {
5329                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5330                holder.itemView.setLayoutParams(rvLayoutParams);
5331            } else {
5332                rvLayoutParams = (LayoutParams) lp;
5333            }
5334
5335            rvLayoutParams.mInsetsDirty = true;
5336            rvLayoutParams.mViewHolder = holder;
5337            rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
5338        }
5339
5340        /**
5341         * RecyclerView provides artificial position range (item count) in pre-layout state and
5342         * automatically maps these positions to {@link Adapter} positions when
5343         * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
5344         * <p>
5345         * Usually, LayoutManager does not need to worry about this. However, in some cases, your
5346         * LayoutManager may need to call some custom component with item positions in which
5347         * case you need the actual adapter position instead of the pre layout position. You
5348         * can use this method to convert a pre-layout position to adapter (post layout) position.
5349         * <p>
5350         * Note that if the provided position belongs to a deleted ViewHolder, this method will
5351         * return -1.
5352         * <p>
5353         * Calling this method in post-layout state returns the same value back.
5354         *
5355         * @param position The pre-layout position to convert. Must be greater or equal to 0 and
5356         *                 less than {@link State#getItemCount()}.
5357         */
5358        public int convertPreLayoutPositionToPostLayout(int position) {
5359            if (position < 0 || position >= mState.getItemCount()) {
5360                throw new IndexOutOfBoundsException("invalid position " + position + ". State "
5361                        + "item count is " + mState.getItemCount());
5362            }
5363            if (!mState.isPreLayout()) {
5364                return position;
5365            }
5366            return mAdapterHelper.findPositionOffset(position);
5367        }
5368
5369        /**
5370         * Obtain a view initialized for the given position.
5371         *
5372         * This method should be used by {@link LayoutManager} implementations to obtain
5373         * views to represent data from an {@link Adapter}.
5374         * <p>
5375         * The Recycler may reuse a scrap or detached view from a shared pool if one is
5376         * available for the correct view type. If the adapter has not indicated that the
5377         * data at the given position has changed, the Recycler will attempt to hand back
5378         * a scrap view that was previously initialized for that data without rebinding.
5379         *
5380         * @param position Position to obtain a view for
5381         * @return A view representing the data at <code>position</code> from <code>adapter</code>
5382         */
5383        public View getViewForPosition(int position) {
5384            return getViewForPosition(position, false);
5385        }
5386
5387        View getViewForPosition(int position, boolean dryRun) {
5388            return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
5389        }
5390
5391        /**
5392         * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
5393         * cache, the RecycledViewPool, or creating it directly.
5394         * <p>
5395         * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
5396         * rather than constructing or binding a ViewHolder if it doesn't think it has time.
5397         * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
5398         * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
5399         * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
5400         *
5401         * @param position Position of ViewHolder to be returned.
5402         * @param dryRun True if the ViewHolder should not be removed from scrap/cache/
5403         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5404         *                   complete. If FOREVER_NS is passed, this method will not fail to
5405         *                   create/bind the holder if needed.
5406         *
5407         * @return ViewHolder for requested position
5408         */
5409        @Nullable
5410        ViewHolder tryGetViewHolderForPositionByDeadline(int position,
5411                boolean dryRun, long deadlineNs) {
5412            if (position < 0 || position >= mState.getItemCount()) {
5413                throw new IndexOutOfBoundsException("Invalid item position " + position
5414                        + "(" + position + "). Item count:" + mState.getItemCount());
5415            }
5416            boolean fromScrapOrHiddenOrCache = false;
5417            ViewHolder holder = null;
5418            // 0) If there is a changed scrap, try to find from there
5419            if (mState.isPreLayout()) {
5420                holder = getChangedScrapViewForPosition(position);
5421                fromScrapOrHiddenOrCache = holder != null;
5422            }
5423            // 1) Find by position from scrap/hidden list/cache
5424            if (holder == null) {
5425                holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
5426                if (holder != null) {
5427                    if (!validateViewHolderForOffsetPosition(holder)) {
5428                        // recycle holder (and unscrap if relevant) since it can't be used
5429                        if (!dryRun) {
5430                            // we would like to recycle this but need to make sure it is not used by
5431                            // animation logic etc.
5432                            holder.addFlags(ViewHolder.FLAG_INVALID);
5433                            if (holder.isScrap()) {
5434                                removeDetachedView(holder.itemView, false);
5435                                holder.unScrap();
5436                            } else if (holder.wasReturnedFromScrap()) {
5437                                holder.clearReturnedFromScrapFlag();
5438                            }
5439                            recycleViewHolderInternal(holder);
5440                        }
5441                        holder = null;
5442                    } else {
5443                        fromScrapOrHiddenOrCache = true;
5444                    }
5445                }
5446            }
5447            if (holder == null) {
5448                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5449                if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5450                    throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5451                            + "position " + position + "(offset:" + offsetPosition + ")."
5452                            + "state:" + mState.getItemCount());
5453                }
5454
5455                final int type = mAdapter.getItemViewType(offsetPosition);
5456                // 2) Find from scrap/cache via stable ids, if exists
5457                if (mAdapter.hasStableIds()) {
5458                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
5459                            type, dryRun);
5460                    if (holder != null) {
5461                        // update position
5462                        holder.mPosition = offsetPosition;
5463                        fromScrapOrHiddenOrCache = true;
5464                    }
5465                }
5466                if (holder == null && mViewCacheExtension != null) {
5467                    // We are NOT sending the offsetPosition because LayoutManager does not
5468                    // know it.
5469                    final View view = mViewCacheExtension
5470                            .getViewForPositionAndType(this, position, type);
5471                    if (view != null) {
5472                        holder = getChildViewHolder(view);
5473                        if (holder == null) {
5474                            throw new IllegalArgumentException("getViewForPositionAndType returned"
5475                                    + " a view which does not have a ViewHolder");
5476                        } else if (holder.shouldIgnore()) {
5477                            throw new IllegalArgumentException("getViewForPositionAndType returned"
5478                                    + " a view that is ignored. You must call stopIgnoring before"
5479                                    + " returning this view.");
5480                        }
5481                    }
5482                }
5483                if (holder == null) { // fallback to pool
5484                    if (DEBUG) {
5485                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
5486                                + position + ") fetching from shared pool");
5487                    }
5488                    holder = getRecycledViewPool().getRecycledView(type);
5489                    if (holder != null) {
5490                        holder.resetInternal();
5491                        if (FORCE_INVALIDATE_DISPLAY_LIST) {
5492                            invalidateDisplayListInt(holder);
5493                        }
5494                    }
5495                }
5496                if (holder == null) {
5497                    long start = getNanoTime();
5498                    if (deadlineNs != FOREVER_NS
5499                            && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
5500                        // abort - we have a deadline we can't meet
5501                        return null;
5502                    }
5503                    holder = mAdapter.createViewHolder(RecyclerView.this, type);
5504                    if (ALLOW_THREAD_GAP_WORK) {
5505                        // only bother finding nested RV if prefetching
5506                        RecyclerView innerView = findNestedRecyclerView(holder.itemView);
5507                        if (innerView != null) {
5508                            holder.mNestedRecyclerView = new WeakReference<>(innerView);
5509                        }
5510                    }
5511
5512                    long end = getNanoTime();
5513                    mRecyclerPool.factorInCreateTime(type, end - start);
5514                    if (DEBUG) {
5515                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
5516                    }
5517                }
5518            }
5519
5520            // This is very ugly but the only place we can grab this information
5521            // before the View is rebound and returned to the LayoutManager for post layout ops.
5522            // We don't need this in pre-layout since the VH is not updated by the LM.
5523            if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
5524                    .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
5525                holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5526                if (mState.mRunSimpleAnimations) {
5527                    int changeFlags = ItemAnimator
5528                            .buildAdapterChangeFlagsForAnimations(holder);
5529                    changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
5530                    final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
5531                            holder, changeFlags, holder.getUnmodifiedPayloads());
5532                    recordAnimationInfoIfBouncedHiddenView(holder, info);
5533                }
5534            }
5535
5536            boolean bound = false;
5537            if (mState.isPreLayout() && holder.isBound()) {
5538                // do not update unless we absolutely have to.
5539                holder.mPreLayoutPosition = position;
5540            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
5541                if (DEBUG && holder.isRemoved()) {
5542                    throw new IllegalStateException("Removed holder should be bound and it should"
5543                            + " come here only in pre-layout. Holder: " + holder);
5544                }
5545                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5546                bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
5547            }
5548
5549            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5550            final LayoutParams rvLayoutParams;
5551            if (lp == null) {
5552                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5553                holder.itemView.setLayoutParams(rvLayoutParams);
5554            } else if (!checkLayoutParams(lp)) {
5555                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5556                holder.itemView.setLayoutParams(rvLayoutParams);
5557            } else {
5558                rvLayoutParams = (LayoutParams) lp;
5559            }
5560            rvLayoutParams.mViewHolder = holder;
5561            rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
5562            return holder;
5563        }
5564
5565        private void attachAccessibilityDelegate(View itemView) {
5566            if (isAccessibilityEnabled()) {
5567                if (itemView.getImportantForAccessibility()
5568                        == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
5569                    itemView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
5570                }
5571
5572                if (itemView.getAccessibilityDelegate() == null) {
5573                    itemView.setAccessibilityDelegate(mAccessibilityDelegate.getItemDelegate());
5574                }
5575            }
5576        }
5577
5578        private void invalidateDisplayListInt(ViewHolder holder) {
5579            if (holder.itemView instanceof ViewGroup) {
5580                invalidateDisplayListInt((ViewGroup) holder.itemView, false);
5581            }
5582        }
5583
5584        private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
5585            for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
5586                final View view = viewGroup.getChildAt(i);
5587                if (view instanceof ViewGroup) {
5588                    invalidateDisplayListInt((ViewGroup) view, true);
5589                }
5590            }
5591            if (!invalidateThis) {
5592                return;
5593            }
5594            // we need to force it to become invisible
5595            if (viewGroup.getVisibility() == View.INVISIBLE) {
5596                viewGroup.setVisibility(View.VISIBLE);
5597                viewGroup.setVisibility(View.INVISIBLE);
5598            } else {
5599                final int visibility = viewGroup.getVisibility();
5600                viewGroup.setVisibility(View.INVISIBLE);
5601                viewGroup.setVisibility(visibility);
5602            }
5603        }
5604
5605        /**
5606         * Recycle a detached view. The specified view will be added to a pool of views
5607         * for later rebinding and reuse.
5608         *
5609         * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
5610         * View is scrapped, it will be removed from scrap list.</p>
5611         *
5612         * @param view Removed view for recycling
5613         * @see LayoutManager#removeAndRecycleView(View, Recycler)
5614         */
5615        public void recycleView(View view) {
5616            // This public recycle method tries to make view recycle-able since layout manager
5617            // intended to recycle this view (e.g. even if it is in scrap or change cache)
5618            ViewHolder holder = getChildViewHolderInt(view);
5619            if (holder.isTmpDetached()) {
5620                removeDetachedView(view, false);
5621            }
5622            if (holder.isScrap()) {
5623                holder.unScrap();
5624            } else if (holder.wasReturnedFromScrap()) {
5625                holder.clearReturnedFromScrapFlag();
5626            }
5627            recycleViewHolderInternal(holder);
5628        }
5629
5630        /**
5631         * Internally, use this method instead of {@link #recycleView(android.view.View)} to
5632         * catch potential bugs.
5633         * @param view
5634         */
5635        void recycleViewInternal(View view) {
5636            recycleViewHolderInternal(getChildViewHolderInt(view));
5637        }
5638
5639        void recycleAndClearCachedViews() {
5640            final int count = mCachedViews.size();
5641            for (int i = count - 1; i >= 0; i--) {
5642                recycleCachedViewAt(i);
5643            }
5644            mCachedViews.clear();
5645            if (ALLOW_THREAD_GAP_WORK) {
5646                mPrefetchRegistry.clearPrefetchPositions();
5647            }
5648        }
5649
5650        /**
5651         * Recycles a cached view and removes the view from the list. Views are added to cache
5652         * if and only if they are recyclable, so this method does not check it again.
5653         * <p>
5654         * A small exception to this rule is when the view does not have an animator reference
5655         * but transient state is true (due to animations created outside ItemAnimator). In that
5656         * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
5657         * still recyclable since Adapter wants to do so.
5658         *
5659         * @param cachedViewIndex The index of the view in cached views list
5660         */
5661        void recycleCachedViewAt(int cachedViewIndex) {
5662            if (DEBUG) {
5663                Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
5664            }
5665            ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
5666            if (DEBUG) {
5667                Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
5668            }
5669            addViewHolderToRecycledViewPool(viewHolder, true);
5670            mCachedViews.remove(cachedViewIndex);
5671        }
5672
5673        /**
5674         * internal implementation checks if view is scrapped or attached and throws an exception
5675         * if so.
5676         * Public version un-scraps before calling recycle.
5677         */
5678        void recycleViewHolderInternal(ViewHolder holder) {
5679            if (holder.isScrap() || holder.itemView.getParent() != null) {
5680                throw new IllegalArgumentException(
5681                        "Scrapped or attached views may not be recycled. isScrap:"
5682                                + holder.isScrap() + " isAttached:"
5683                                + (holder.itemView.getParent() != null));
5684            }
5685
5686            if (holder.isTmpDetached()) {
5687                throw new IllegalArgumentException("Tmp detached view should be removed "
5688                        + "from RecyclerView before it can be recycled: " + holder);
5689            }
5690
5691            if (holder.shouldIgnore()) {
5692                throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
5693                        + " should first call stopIgnoringView(view) before calling recycle.");
5694            }
5695            //noinspection unchecked
5696            final boolean transientStatePreventsRecycling = holder
5697                    .doesTransientStatePreventRecycling();
5698            final boolean forceRecycle = mAdapter != null
5699                    && transientStatePreventsRecycling
5700                    && mAdapter.onFailedToRecycleView(holder);
5701            boolean cached = false;
5702            boolean recycled = false;
5703            if (DEBUG && mCachedViews.contains(holder)) {
5704                throw new IllegalArgumentException("cached view received recycle internal? "
5705                        + holder);
5706            }
5707            if (forceRecycle || holder.isRecyclable()) {
5708                if (mViewCacheMax > 0
5709                        && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
5710                                | ViewHolder.FLAG_REMOVED
5711                                | ViewHolder.FLAG_UPDATE
5712                                | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
5713                    // Retire oldest cached view
5714                    int cachedViewSize = mCachedViews.size();
5715                    if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
5716                        recycleCachedViewAt(0);
5717                        cachedViewSize--;
5718                    }
5719
5720                    int targetCacheIndex = cachedViewSize;
5721                    if (ALLOW_THREAD_GAP_WORK
5722                            && cachedViewSize > 0
5723                            && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
5724                        // when adding the view, skip past most recently prefetched views
5725                        int cacheIndex = cachedViewSize - 1;
5726                        while (cacheIndex >= 0) {
5727                            int cachedPos = mCachedViews.get(cacheIndex).mPosition;
5728                            if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
5729                                break;
5730                            }
5731                            cacheIndex--;
5732                        }
5733                        targetCacheIndex = cacheIndex + 1;
5734                    }
5735                    mCachedViews.add(targetCacheIndex, holder);
5736                    cached = true;
5737                }
5738                if (!cached) {
5739                    addViewHolderToRecycledViewPool(holder, true);
5740                    recycled = true;
5741                }
5742            } else {
5743                // NOTE: A view can fail to be recycled when it is scrolled off while an animation
5744                // runs. In this case, the item is eventually recycled by
5745                // ItemAnimatorRestoreListener#onAnimationFinished.
5746
5747                // TODO: consider cancelling an animation when an item is removed scrollBy,
5748                // to return it to the pool faster
5749                if (DEBUG) {
5750                    Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
5751                            + "re-visit here. We are still removing it from animation lists");
5752                }
5753            }
5754            // even if the holder is not removed, we still call this method so that it is removed
5755            // from view holder lists.
5756            mViewInfoStore.removeViewHolder(holder);
5757            if (!cached && !recycled && transientStatePreventsRecycling) {
5758                holder.mOwnerRecyclerView = null;
5759            }
5760        }
5761
5762        /**
5763         * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
5764         *
5765         * Pass false to dispatchRecycled for views that have not been bound.
5766         *
5767         * @param holder Holder to be added to the pool.
5768         * @param dispatchRecycled True to dispatch View recycled callbacks.
5769         */
5770        void addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) {
5771            clearNestedRecyclerViewIfNotNested(holder);
5772            holder.itemView.setAccessibilityDelegate(null);
5773            if (dispatchRecycled) {
5774                dispatchViewRecycled(holder);
5775            }
5776            holder.mOwnerRecyclerView = null;
5777            getRecycledViewPool().putRecycledView(holder);
5778        }
5779
5780        /**
5781         * Used as a fast path for unscrapping and recycling a view during a bulk operation.
5782         * The caller must call {@link #clearScrap()} when it's done to update the recycler's
5783         * internal bookkeeping.
5784         */
5785        void quickRecycleScrapView(View view) {
5786            final ViewHolder holder = getChildViewHolderInt(view);
5787            holder.mScrapContainer = null;
5788            holder.mInChangeScrap = false;
5789            holder.clearReturnedFromScrapFlag();
5790            recycleViewHolderInternal(holder);
5791        }
5792
5793        /**
5794         * Mark an attached view as scrap.
5795         *
5796         * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
5797         * for rebinding and reuse. Requests for a view for a given position may return a
5798         * reused or rebound scrap view instance.</p>
5799         *
5800         * @param view View to scrap
5801         */
5802        void scrapView(View view) {
5803            final ViewHolder holder = getChildViewHolderInt(view);
5804            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
5805                    || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
5806                if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
5807                    throw new IllegalArgumentException("Called scrap view with an invalid view."
5808                            + " Invalid views cannot be reused from scrap, they should rebound from"
5809                            + " recycler pool.");
5810                }
5811                holder.setScrapContainer(this, false);
5812                mAttachedScrap.add(holder);
5813            } else {
5814                if (mChangedScrap == null) {
5815                    mChangedScrap = new ArrayList<ViewHolder>();
5816                }
5817                holder.setScrapContainer(this, true);
5818                mChangedScrap.add(holder);
5819            }
5820        }
5821
5822        /**
5823         * Remove a previously scrapped view from the pool of eligible scrap.
5824         *
5825         * <p>This view will no longer be eligible for reuse until re-scrapped or
5826         * until it is explicitly removed and recycled.</p>
5827         */
5828        void unscrapView(ViewHolder holder) {
5829            if (holder.mInChangeScrap) {
5830                mChangedScrap.remove(holder);
5831            } else {
5832                mAttachedScrap.remove(holder);
5833            }
5834            holder.mScrapContainer = null;
5835            holder.mInChangeScrap = false;
5836            holder.clearReturnedFromScrapFlag();
5837        }
5838
5839        int getScrapCount() {
5840            return mAttachedScrap.size();
5841        }
5842
5843        View getScrapViewAt(int index) {
5844            return mAttachedScrap.get(index).itemView;
5845        }
5846
5847        void clearScrap() {
5848            mAttachedScrap.clear();
5849            if (mChangedScrap != null) {
5850                mChangedScrap.clear();
5851            }
5852        }
5853
5854        ViewHolder getChangedScrapViewForPosition(int position) {
5855            // If pre-layout, check the changed scrap for an exact match.
5856            final int changedScrapSize;
5857            if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
5858                return null;
5859            }
5860            // find by position
5861            for (int i = 0; i < changedScrapSize; i++) {
5862                final ViewHolder holder = mChangedScrap.get(i);
5863                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
5864                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5865                    return holder;
5866                }
5867            }
5868            // find by id
5869            if (mAdapter.hasStableIds()) {
5870                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5871                if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
5872                    final long id = mAdapter.getItemId(offsetPosition);
5873                    for (int i = 0; i < changedScrapSize; i++) {
5874                        final ViewHolder holder = mChangedScrap.get(i);
5875                        if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
5876                            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5877                            return holder;
5878                        }
5879                    }
5880                }
5881            }
5882            return null;
5883        }
5884
5885        /**
5886         * Returns a view for the position either from attach scrap, hidden children, or cache.
5887         *
5888         * @param position Item position
5889         * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
5890         * @return a ViewHolder that can be re-used for this position.
5891         */
5892        ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
5893            final int scrapCount = mAttachedScrap.size();
5894
5895            // Try first for an exact, non-invalid match from scrap.
5896            for (int i = 0; i < scrapCount; i++) {
5897                final ViewHolder holder = mAttachedScrap.get(i);
5898                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
5899                        && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
5900                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5901                    return holder;
5902                }
5903            }
5904
5905            if (!dryRun) {
5906                View view = mChildHelper.findHiddenNonRemovedView(position);
5907                if (view != null) {
5908                    // This View is good to be used. We just need to unhide, detach and move to the
5909                    // scrap list.
5910                    final ViewHolder vh = getChildViewHolderInt(view);
5911                    mChildHelper.unhide(view);
5912                    int layoutIndex = mChildHelper.indexOfChild(view);
5913                    if (layoutIndex == RecyclerView.NO_POSITION) {
5914                        throw new IllegalStateException("layout index should not be -1 after "
5915                                + "unhiding a view:" + vh);
5916                    }
5917                    mChildHelper.detachViewFromParent(layoutIndex);
5918                    scrapView(view);
5919                    vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
5920                            | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5921                    return vh;
5922                }
5923            }
5924
5925            // Search in our first-level recycled view cache.
5926            final int cacheSize = mCachedViews.size();
5927            for (int i = 0; i < cacheSize; i++) {
5928                final ViewHolder holder = mCachedViews.get(i);
5929                // invalid view holders may be in cache if adapter has stable ids as they can be
5930                // retrieved via getScrapOrCachedViewForId
5931                if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
5932                    if (!dryRun) {
5933                        mCachedViews.remove(i);
5934                    }
5935                    if (DEBUG) {
5936                        Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
5937                                + ") found match in cache: " + holder);
5938                    }
5939                    return holder;
5940                }
5941            }
5942            return null;
5943        }
5944
5945        ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
5946            // Look in our attached views first
5947            final int count = mAttachedScrap.size();
5948            for (int i = count - 1; i >= 0; i--) {
5949                final ViewHolder holder = mAttachedScrap.get(i);
5950                if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
5951                    if (type == holder.getItemViewType()) {
5952                        holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5953                        if (holder.isRemoved()) {
5954                            // this might be valid in two cases:
5955                            // > item is removed but we are in pre-layout pass
5956                            // >> do nothing. return as is. make sure we don't rebind
5957                            // > item is removed then added to another position and we are in
5958                            // post layout.
5959                            // >> remove removed and invalid flags, add update flag to rebind
5960                            // because item was invisible to us and we don't know what happened in
5961                            // between.
5962                            if (!mState.isPreLayout()) {
5963                                holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
5964                                        | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
5965                            }
5966                        }
5967                        return holder;
5968                    } else if (!dryRun) {
5969                        // if we are running animations, it is actually better to keep it in scrap
5970                        // but this would force layout manager to lay it out which would be bad.
5971                        // Recycle this scrap. Type mismatch.
5972                        mAttachedScrap.remove(i);
5973                        removeDetachedView(holder.itemView, false);
5974                        quickRecycleScrapView(holder.itemView);
5975                    }
5976                }
5977            }
5978
5979            // Search the first-level cache
5980            final int cacheSize = mCachedViews.size();
5981            for (int i = cacheSize - 1; i >= 0; i--) {
5982                final ViewHolder holder = mCachedViews.get(i);
5983                if (holder.getItemId() == id) {
5984                    if (type == holder.getItemViewType()) {
5985                        if (!dryRun) {
5986                            mCachedViews.remove(i);
5987                        }
5988                        return holder;
5989                    } else if (!dryRun) {
5990                        recycleCachedViewAt(i);
5991                        return null;
5992                    }
5993                }
5994            }
5995            return null;
5996        }
5997
5998        void dispatchViewRecycled(ViewHolder holder) {
5999            if (mRecyclerListener != null) {
6000                mRecyclerListener.onViewRecycled(holder);
6001            }
6002            if (mAdapter != null) {
6003                mAdapter.onViewRecycled(holder);
6004            }
6005            if (mState != null) {
6006                mViewInfoStore.removeViewHolder(holder);
6007            }
6008            if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
6009        }
6010
6011        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
6012                boolean compatibleWithPrevious) {
6013            clear();
6014            getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
6015        }
6016
6017        void offsetPositionRecordsForMove(int from, int to) {
6018            final int start, end, inBetweenOffset;
6019            if (from < to) {
6020                start = from;
6021                end = to;
6022                inBetweenOffset = -1;
6023            } else {
6024                start = to;
6025                end = from;
6026                inBetweenOffset = 1;
6027            }
6028            final int cachedCount = mCachedViews.size();
6029            for (int i = 0; i < cachedCount; i++) {
6030                final ViewHolder holder = mCachedViews.get(i);
6031                if (holder == null || holder.mPosition < start || holder.mPosition > end) {
6032                    continue;
6033                }
6034                if (holder.mPosition == from) {
6035                    holder.offsetPosition(to - from, false);
6036                } else {
6037                    holder.offsetPosition(inBetweenOffset, false);
6038                }
6039                if (DEBUG) {
6040                    Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
6041                            + holder);
6042                }
6043            }
6044        }
6045
6046        void offsetPositionRecordsForInsert(int insertedAt, int count) {
6047            final int cachedCount = mCachedViews.size();
6048            for (int i = 0; i < cachedCount; i++) {
6049                final ViewHolder holder = mCachedViews.get(i);
6050                if (holder != null && holder.mPosition >= insertedAt) {
6051                    if (DEBUG) {
6052                        Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder "
6053                                + holder + " now at position " + (holder.mPosition + count));
6054                    }
6055                    holder.offsetPosition(count, true);
6056                }
6057            }
6058        }
6059
6060        /**
6061         * @param removedFrom Remove start index
6062         * @param count Remove count
6063         * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
6064         *                         false, they'll be applied before the second layout pass
6065         */
6066        void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
6067            final int removedEnd = removedFrom + count;
6068            final int cachedCount = mCachedViews.size();
6069            for (int i = cachedCount - 1; i >= 0; i--) {
6070                final ViewHolder holder = mCachedViews.get(i);
6071                if (holder != null) {
6072                    if (holder.mPosition >= removedEnd) {
6073                        if (DEBUG) {
6074                            Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
6075                                    + " holder " + holder + " now at position "
6076                                    + (holder.mPosition - count));
6077                        }
6078                        holder.offsetPosition(-count, applyToPreLayout);
6079                    } else if (holder.mPosition >= removedFrom) {
6080                        // Item for this view was removed. Dump it from the cache.
6081                        holder.addFlags(ViewHolder.FLAG_REMOVED);
6082                        recycleCachedViewAt(i);
6083                    }
6084                }
6085            }
6086        }
6087
6088        void setViewCacheExtension(ViewCacheExtension extension) {
6089            mViewCacheExtension = extension;
6090        }
6091
6092        void setRecycledViewPool(RecycledViewPool pool) {
6093            if (mRecyclerPool != null) {
6094                mRecyclerPool.detach();
6095            }
6096            mRecyclerPool = pool;
6097            if (pool != null) {
6098                mRecyclerPool.attach(getAdapter());
6099            }
6100        }
6101
6102        RecycledViewPool getRecycledViewPool() {
6103            if (mRecyclerPool == null) {
6104                mRecyclerPool = new RecycledViewPool();
6105            }
6106            return mRecyclerPool;
6107        }
6108
6109        void viewRangeUpdate(int positionStart, int itemCount) {
6110            final int positionEnd = positionStart + itemCount;
6111            final int cachedCount = mCachedViews.size();
6112            for (int i = cachedCount - 1; i >= 0; i--) {
6113                final ViewHolder holder = mCachedViews.get(i);
6114                if (holder == null) {
6115                    continue;
6116                }
6117
6118                final int pos = holder.getLayoutPosition();
6119                if (pos >= positionStart && pos < positionEnd) {
6120                    holder.addFlags(ViewHolder.FLAG_UPDATE);
6121                    recycleCachedViewAt(i);
6122                    // cached views should not be flagged as changed because this will cause them
6123                    // to animate when they are returned from cache.
6124                }
6125            }
6126        }
6127
6128        void setAdapterPositionsAsUnknown() {
6129            final int cachedCount = mCachedViews.size();
6130            for (int i = 0; i < cachedCount; i++) {
6131                final ViewHolder holder = mCachedViews.get(i);
6132                if (holder != null) {
6133                    holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
6134                }
6135            }
6136        }
6137
6138        void markKnownViewsInvalid() {
6139            if (mAdapter != null && mAdapter.hasStableIds()) {
6140                final int cachedCount = mCachedViews.size();
6141                for (int i = 0; i < cachedCount; i++) {
6142                    final ViewHolder holder = mCachedViews.get(i);
6143                    if (holder != null) {
6144                        holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
6145                        holder.addChangePayload(null);
6146                    }
6147                }
6148            } else {
6149                // we cannot re-use cached views in this case. Recycle them all
6150                recycleAndClearCachedViews();
6151            }
6152        }
6153
6154        void clearOldPositions() {
6155            final int cachedCount = mCachedViews.size();
6156            for (int i = 0; i < cachedCount; i++) {
6157                final ViewHolder holder = mCachedViews.get(i);
6158                holder.clearOldPosition();
6159            }
6160            final int scrapCount = mAttachedScrap.size();
6161            for (int i = 0; i < scrapCount; i++) {
6162                mAttachedScrap.get(i).clearOldPosition();
6163            }
6164            if (mChangedScrap != null) {
6165                final int changedScrapCount = mChangedScrap.size();
6166                for (int i = 0; i < changedScrapCount; i++) {
6167                    mChangedScrap.get(i).clearOldPosition();
6168                }
6169            }
6170        }
6171
6172        void markItemDecorInsetsDirty() {
6173            final int cachedCount = mCachedViews.size();
6174            for (int i = 0; i < cachedCount; i++) {
6175                final ViewHolder holder = mCachedViews.get(i);
6176                LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
6177                if (layoutParams != null) {
6178                    layoutParams.mInsetsDirty = true;
6179                }
6180            }
6181        }
6182    }
6183
6184    /**
6185     * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
6186     * be controlled by the developer.
6187     * <p>
6188     * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
6189     * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
6190     * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
6191     * {@link RecycledViewPool}.
6192     * <p>
6193     * Note that, Recycler never sends Views to this method to be cached. It is developers
6194     * responsibility to decide whether they want to keep their Views in this custom cache or let
6195     * the default recycling policy handle it.
6196     */
6197    public abstract static class ViewCacheExtension {
6198
6199        /**
6200         * Returns a View that can be binded to the given Adapter position.
6201         * <p>
6202         * This method should <b>not</b> create a new View. Instead, it is expected to return
6203         * an already created View that can be re-used for the given type and position.
6204         * If the View is marked as ignored, it should first call
6205         * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
6206         * <p>
6207         * RecyclerView will re-bind the returned View to the position if necessary.
6208         *
6209         * @param recycler The Recycler that can be used to bind the View
6210         * @param position The adapter position
6211         * @param type     The type of the View, defined by adapter
6212         * @return A View that is bound to the given position or NULL if there is no View to re-use
6213         * @see LayoutManager#ignoreView(View)
6214         */
6215        public abstract View getViewForPositionAndType(Recycler recycler, int position, int type);
6216    }
6217
6218    /**
6219     * Base class for an Adapter
6220     *
6221     * <p>Adapters provide a binding from an app-specific data set to views that are displayed
6222     * within a {@link RecyclerView}.</p>
6223     *
6224     * @param  A class that extends ViewHolder that will be used by the adapter.
6225     */
6226    public abstract static class Adapter<VH extends ViewHolder> {
6227        private final AdapterDataObservable mObservable = new AdapterDataObservable();
6228        private boolean mHasStableIds = false;
6229
6230        /**
6231         * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
6232         * an item.
6233         * <p>
6234         * This new ViewHolder should be constructed with a new View that can represent the items
6235         * of the given type. You can either create a new View manually or inflate it from an XML
6236         * layout file.
6237         * <p>
6238         * The new ViewHolder will be used to display items of the adapter using
6239         * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
6240         * different items in the data set, it is a good idea to cache references to sub views of
6241         * the View to avoid unnecessary {@link View#findViewById(int)} calls.
6242         *
6243         * @param parent The ViewGroup into which the new View will be added after it is bound to
6244         *               an adapter position.
6245         * @param viewType The view type of the new View.
6246         *
6247         * @return A new ViewHolder that holds a View of the given view type.
6248         * @see #getItemViewType(int)
6249         * @see #onBindViewHolder(ViewHolder, int)
6250         */
6251        public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
6252
6253        /**
6254         * Called by RecyclerView to display the data at the specified position. This method should
6255         * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
6256         * position.
6257         * <p>
6258         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6259         * again if the position of the item changes in the data set unless the item itself is
6260         * invalidated or the new position cannot be determined. For this reason, you should only
6261         * use the <code>position</code> parameter while acquiring the related data item inside
6262         * this method and should not keep a copy of it. If you need the position of an item later
6263         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6264         * have the updated adapter position.
6265         *
6266         * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
6267         * handle efficient partial bind.
6268         *
6269         * @param holder The ViewHolder which should be updated to represent the contents of the
6270         *        item at the given position in the data set.
6271         * @param position The position of the item within the adapter's data set.
6272         */
6273        public abstract void onBindViewHolder(VH holder, int position);
6274
6275        /**
6276         * Called by RecyclerView to display the data at the specified position. This method
6277         * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
6278         * the given position.
6279         * <p>
6280         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6281         * again if the position of the item changes in the data set unless the item itself is
6282         * invalidated or the new position cannot be determined. For this reason, you should only
6283         * use the <code>position</code> parameter while acquiring the related data item inside
6284         * this method and should not keep a copy of it. If you need the position of an item later
6285         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6286         * have the updated adapter position.
6287         * <p>
6288         * Partial bind vs full bind:
6289         * <p>
6290         * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
6291         * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
6292         * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
6293         * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
6294         * Adapter should not assume that the payload passed in notify methods will be received by
6295         * onBindViewHolder().  For example when the view is not attached to the screen, the
6296         * payload in notifyItemChange() will be simply dropped.
6297         *
6298         * @param holder The ViewHolder which should be updated to represent the contents of the
6299         *               item at the given position in the data set.
6300         * @param position The position of the item within the adapter's data set.
6301         * @param payloads A non-null list of merged payloads. Can be empty list if requires full
6302         *                 update.
6303         */
6304        public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
6305            onBindViewHolder(holder, position);
6306        }
6307
6308        /**
6309         * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
6310         * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
6311         *
6312         * @see #onCreateViewHolder(ViewGroup, int)
6313         */
6314        public final VH createViewHolder(ViewGroup parent, int viewType) {
6315            Trace.beginSection(TRACE_CREATE_VIEW_TAG);
6316            final VH holder = onCreateViewHolder(parent, viewType);
6317            holder.mItemViewType = viewType;
6318            Trace.endSection();
6319            return holder;
6320        }
6321
6322        /**
6323         * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
6324         * {@link ViewHolder} contents with the item at the given position and also sets up some
6325         * private fields to be used by RecyclerView.
6326         *
6327         * @see #onBindViewHolder(ViewHolder, int)
6328         */
6329        public final void bindViewHolder(VH holder, int position) {
6330            holder.mPosition = position;
6331            if (hasStableIds()) {
6332                holder.mItemId = getItemId(position);
6333            }
6334            holder.setFlags(ViewHolder.FLAG_BOUND,
6335                    ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
6336                            | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
6337            Trace.beginSection(TRACE_BIND_VIEW_TAG);
6338            onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
6339            holder.clearPayload();
6340            final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
6341            if (layoutParams instanceof RecyclerView.LayoutParams) {
6342                ((LayoutParams) layoutParams).mInsetsDirty = true;
6343            }
6344            Trace.endSection();
6345        }
6346
6347        /**
6348         * Return the view type of the item at <code>position</code> for the purposes
6349         * of view recycling.
6350         *
6351         * <p>The default implementation of this method returns 0, making the assumption of
6352         * a single view type for the adapter. Unlike ListView adapters, types need not
6353         * be contiguous. Consider using id resources to uniquely identify item view types.
6354         *
6355         * @param position position to query
6356         * @return integer value identifying the type of the view needed to represent the item at
6357         *                 <code>position</code>. Type codes need not be contiguous.
6358         */
6359        public int getItemViewType(int position) {
6360            return 0;
6361        }
6362
6363        /**
6364         * Indicates whether each item in the data set can be represented with a unique identifier
6365         * of type {@link java.lang.Long}.
6366         *
6367         * @param hasStableIds Whether items in data set have unique identifiers or not.
6368         * @see #hasStableIds()
6369         * @see #getItemId(int)
6370         */
6371        public void setHasStableIds(boolean hasStableIds) {
6372            if (hasObservers()) {
6373                throw new IllegalStateException("Cannot change whether this adapter has "
6374                        + "stable IDs while the adapter has registered observers.");
6375            }
6376            mHasStableIds = hasStableIds;
6377        }
6378
6379        /**
6380         * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
6381         * would return false this method should return {@link #NO_ID}. The default implementation
6382         * of this method returns {@link #NO_ID}.
6383         *
6384         * @param position Adapter position to query
6385         * @return the stable ID of the item at position
6386         */
6387        public long getItemId(int position) {
6388            return NO_ID;
6389        }
6390
6391        /**
6392         * Returns the total number of items in the data set held by the adapter.
6393         *
6394         * @return The total number of items in this adapter.
6395         */
6396        public abstract int getItemCount();
6397
6398        /**
6399         * Returns true if this adapter publishes a unique <code>long</code> value that can
6400         * act as a key for the item at a given position in the data set. If that item is relocated
6401         * in the data set, the ID returned for that item should be the same.
6402         *
6403         * @return true if this adapter's items have stable IDs
6404         */
6405        public final boolean hasStableIds() {
6406            return mHasStableIds;
6407        }
6408
6409        /**
6410         * Called when a view created by this adapter has been recycled.
6411         *
6412         * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
6413         * needs to be attached to its parent {@link RecyclerView}. This can be because it has
6414         * fallen out of visibility or a set of cached views represented by views still
6415         * attached to the parent RecyclerView. If an item view has large or expensive data
6416         * bound to it such as large bitmaps, this may be a good place to release those
6417         * resources.</p>
6418         * <p>
6419         * RecyclerView calls this method right before clearing ViewHolder's internal data and
6420         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
6421         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
6422         * its adapter position.
6423         *
6424         * @param holder The ViewHolder for the view being recycled
6425         */
6426        public void onViewRecycled(VH holder) {
6427        }
6428
6429        /**
6430         * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
6431         * due to its transient state. Upon receiving this callback, Adapter can clear the
6432         * animation(s) that effect the View's transient state and return <code>true</code> so that
6433         * the View can be recycled. Keep in mind that the View in question is already removed from
6434         * the RecyclerView.
6435         * <p>
6436         * In some cases, it is acceptable to recycle a View although it has transient state. Most
6437         * of the time, this is a case where the transient state will be cleared in
6438         * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
6439         * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
6440         * value of this method to decide whether the View should be recycled or not.
6441         * <p>
6442         * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
6443         * should never receive this callback because RecyclerView keeps those Views as children
6444         * until their animations are complete. This callback is useful when children of the item
6445         * views create animations which may not be easy to implement using an {@link ItemAnimator}.
6446         * <p>
6447         * You should <em>never</em> fix this issue by calling
6448         * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
6449         * <code>holder.itemView.setHasTransientState(true);</code>. Each
6450         * <code>View.setHasTransientState(true)</code> call must be matched by a
6451         * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
6452         * may become inconsistent. You should always prefer to end or cancel animations that are
6453         * triggering the transient state instead of handling it manually.
6454         *
6455         * @param holder The ViewHolder containing the View that could not be recycled due to its
6456         *               transient state.
6457         * @return True if the View should be recycled, false otherwise. Note that if this method
6458         * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
6459         * the View and recycle it regardless. If this method returns <code>false</code>,
6460         * RecyclerView will check the View's transient state again before giving a final decision.
6461         * Default implementation returns false.
6462         */
6463        public boolean onFailedToRecycleView(VH holder) {
6464            return false;
6465        }
6466
6467        /**
6468         * Called when a view created by this adapter has been attached to a window.
6469         *
6470         * <p>This can be used as a reasonable signal that the view is about to be seen
6471         * by the user. If the adapter previously freed any resources in
6472         * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
6473         * those resources should be restored here.</p>
6474         *
6475         * @param holder Holder of the view being attached
6476         */
6477        public void onViewAttachedToWindow(VH holder) {
6478        }
6479
6480        /**
6481         * Called when a view created by this adapter has been detached from its window.
6482         *
6483         * <p>Becoming detached from the window is not necessarily a permanent condition;
6484         * the consumer of an Adapter's views may choose to cache views offscreen while they
6485         * are not visible, attaching and detaching them as appropriate.</p>
6486         *
6487         * @param holder Holder of the view being detached
6488         */
6489        public void onViewDetachedFromWindow(VH holder) {
6490        }
6491
6492        /**
6493         * Returns true if one or more observers are attached to this adapter.
6494         *
6495         * @return true if this adapter has observers
6496         */
6497        public final boolean hasObservers() {
6498            return mObservable.hasObservers();
6499        }
6500
6501        /**
6502         * Register a new observer to listen for data changes.
6503         *
6504         * <p>The adapter may publish a variety of events describing specific changes.
6505         * Not all adapters may support all change types and some may fall back to a generic
6506         * {@link com.android.internal.widget.RecyclerView.AdapterDataObserver#onChanged()
6507         * "something changed"} event if more specific data is not available.</p>
6508         *
6509         * <p>Components registering observers with an adapter are responsible for
6510         * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6511         * unregistering} those observers when finished.</p>
6512         *
6513         * @param observer Observer to register
6514         *
6515         * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6516         */
6517        public void registerAdapterDataObserver(AdapterDataObserver observer) {
6518            mObservable.registerObserver(observer);
6519        }
6520
6521        /**
6522         * Unregister an observer currently listening for data changes.
6523         *
6524         * <p>The unregistered observer will no longer receive events about changes
6525         * to the adapter.</p>
6526         *
6527         * @param observer Observer to unregister
6528         *
6529         * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
6530         */
6531        public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
6532            mObservable.unregisterObserver(observer);
6533        }
6534
6535        /**
6536         * Called by RecyclerView when it starts observing this Adapter.
6537         * <p>
6538         * Keep in mind that same adapter may be observed by multiple RecyclerViews.
6539         *
6540         * @param recyclerView The RecyclerView instance which started observing this adapter.
6541         * @see #onDetachedFromRecyclerView(RecyclerView)
6542         */
6543        public void onAttachedToRecyclerView(RecyclerView recyclerView) {
6544        }
6545
6546        /**
6547         * Called by RecyclerView when it stops observing this Adapter.
6548         *
6549         * @param recyclerView The RecyclerView instance which stopped observing this adapter.
6550         * @see #onAttachedToRecyclerView(RecyclerView)
6551         */
6552        public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
6553        }
6554
6555        /**
6556         * Notify any registered observers that the data set has changed.
6557         *
6558         * <p>There are two different classes of data change events, item changes and structural
6559         * changes. Item changes are when a single item has its data updated but no positional
6560         * changes have occurred. Structural changes are when items are inserted, removed or moved
6561         * within the data set.</p>
6562         *
6563         * <p>This event does not specify what about the data set has changed, forcing
6564         * any observers to assume that all existing items and structure may no longer be valid.
6565         * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
6566         *
6567         * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
6568         * for adapters that report that they have {@link #hasStableIds() stable IDs} when
6569         * this method is used. This can help for the purposes of animation and visual
6570         * object persistence but individual item views will still need to be rebound
6571         * and relaid out.</p>
6572         *
6573         * <p>If you are writing an adapter it will always be more efficient to use the more
6574         * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
6575         * as a last resort.</p>
6576         *
6577         * @see #notifyItemChanged(int)
6578         * @see #notifyItemInserted(int)
6579         * @see #notifyItemRemoved(int)
6580         * @see #notifyItemRangeChanged(int, int)
6581         * @see #notifyItemRangeInserted(int, int)
6582         * @see #notifyItemRangeRemoved(int, int)
6583         */
6584        public final void notifyDataSetChanged() {
6585            mObservable.notifyChanged();
6586        }
6587
6588        /**
6589         * Notify any registered observers that the item at <code>position</code> has changed.
6590         * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
6591         *
6592         * <p>This is an item change event, not a structural change event. It indicates that any
6593         * reflection of the data at <code>position</code> is out of date and should be updated.
6594         * The item at <code>position</code> retains the same identity.</p>
6595         *
6596         * @param position Position of the item that has changed
6597         *
6598         * @see #notifyItemRangeChanged(int, int)
6599         */
6600        public final void notifyItemChanged(int position) {
6601            mObservable.notifyItemRangeChanged(position, 1);
6602        }
6603
6604        /**
6605         * Notify any registered observers that the item at <code>position</code> has changed with
6606         * an optional payload object.
6607         *
6608         * <p>This is an item change event, not a structural change event. It indicates that any
6609         * reflection of the data at <code>position</code> is out of date and should be updated.
6610         * The item at <code>position</code> retains the same identity.
6611         * </p>
6612         *
6613         * <p>
6614         * Client can optionally pass a payload for partial change. These payloads will be merged
6615         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6616         * item is already represented by a ViewHolder and it will be rebound to the same
6617         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6618         * payloads on that item and prevent future payload until
6619         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6620         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6621         * attached, the payload will be simply dropped.
6622         *
6623         * @param position Position of the item that has changed
6624         * @param payload Optional parameter, use null to identify a "full" update
6625         *
6626         * @see #notifyItemRangeChanged(int, int)
6627         */
6628        public final void notifyItemChanged(int position, Object payload) {
6629            mObservable.notifyItemRangeChanged(position, 1, payload);
6630        }
6631
6632        /**
6633         * Notify any registered observers that the <code>itemCount</code> items starting at
6634         * position <code>positionStart</code> have changed.
6635         * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
6636         *
6637         * <p>This is an item change event, not a structural change event. It indicates that
6638         * any reflection of the data in the given position range is out of date and should
6639         * be updated. The items in the given range retain the same identity.</p>
6640         *
6641         * @param positionStart Position of the first item that has changed
6642         * @param itemCount Number of items that have changed
6643         *
6644         * @see #notifyItemChanged(int)
6645         */
6646        public final void notifyItemRangeChanged(int positionStart, int itemCount) {
6647            mObservable.notifyItemRangeChanged(positionStart, itemCount);
6648        }
6649
6650        /**
6651         * Notify any registered observers that the <code>itemCount</code> items starting at
6652         * position <code>positionStart</code> have changed. An optional payload can be
6653         * passed to each changed item.
6654         *
6655         * <p>This is an item change event, not a structural change event. It indicates that any
6656         * reflection of the data in the given position range is out of date and should be updated.
6657         * The items in the given range retain the same identity.
6658         * </p>
6659         *
6660         * <p>
6661         * Client can optionally pass a payload for partial change. These payloads will be merged
6662         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6663         * item is already represented by a ViewHolder and it will be rebound to the same
6664         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6665         * payloads on that item and prevent future payload until
6666         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6667         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6668         * attached, the payload will be simply dropped.
6669         *
6670         * @param positionStart Position of the first item that has changed
6671         * @param itemCount Number of items that have changed
6672         * @param payload  Optional parameter, use null to identify a "full" update
6673         *
6674         * @see #notifyItemChanged(int)
6675         */
6676        public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
6677            mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
6678        }
6679
6680        /**
6681         * Notify any registered observers that the item reflected at <code>position</code>
6682         * has been newly inserted. The item previously at <code>position</code> is now at
6683         * position <code>position + 1</code>.
6684         *
6685         * <p>This is a structural change event. Representations of other existing items in the
6686         * data set are still considered up to date and will not be rebound, though their
6687         * positions may be altered.</p>
6688         *
6689         * @param position Position of the newly inserted item in the data set
6690         *
6691         * @see #notifyItemRangeInserted(int, int)
6692         */
6693        public final void notifyItemInserted(int position) {
6694            mObservable.notifyItemRangeInserted(position, 1);
6695        }
6696
6697        /**
6698         * Notify any registered observers that the item reflected at <code>fromPosition</code>
6699         * has been moved to <code>toPosition</code>.
6700         *
6701         * <p>This is a structural change event. Representations of other existing items in the
6702         * data set are still considered up to date and will not be rebound, though their
6703         * positions may be altered.</p>
6704         *
6705         * @param fromPosition Previous position of the item.
6706         * @param toPosition New position of the item.
6707         */
6708        public final void notifyItemMoved(int fromPosition, int toPosition) {
6709            mObservable.notifyItemMoved(fromPosition, toPosition);
6710        }
6711
6712        /**
6713         * Notify any registered observers that the currently reflected <code>itemCount</code>
6714         * items starting at <code>positionStart</code> have been newly inserted. The items
6715         * previously located at <code>positionStart</code> and beyond can now be found starting
6716         * at position <code>positionStart + itemCount</code>.
6717         *
6718         * <p>This is a structural change event. Representations of other existing items in the
6719         * data set are still considered up to date and will not be rebound, though their positions
6720         * may be altered.</p>
6721         *
6722         * @param positionStart Position of the first item that was inserted
6723         * @param itemCount Number of items inserted
6724         *
6725         * @see #notifyItemInserted(int)
6726         */
6727        public final void notifyItemRangeInserted(int positionStart, int itemCount) {
6728            mObservable.notifyItemRangeInserted(positionStart, itemCount);
6729        }
6730
6731        /**
6732         * Notify any registered observers that the item previously located at <code>position</code>
6733         * has been removed from the data set. The items previously located at and after
6734         * <code>position</code> may now be found at <code>oldPosition - 1</code>.
6735         *
6736         * <p>This is a structural change event. Representations of other existing items in the
6737         * data set are still considered up to date and will not be rebound, though their positions
6738         * may be altered.</p>
6739         *
6740         * @param position Position of the item that has now been removed
6741         *
6742         * @see #notifyItemRangeRemoved(int, int)
6743         */
6744        public final void notifyItemRemoved(int position) {
6745            mObservable.notifyItemRangeRemoved(position, 1);
6746        }
6747
6748        /**
6749         * Notify any registered observers that the <code>itemCount</code> items previously
6750         * located at <code>positionStart</code> have been removed from the data set. The items
6751         * previously located at and after <code>positionStart + itemCount</code> may now be found
6752         * at <code>oldPosition - itemCount</code>.
6753         *
6754         * <p>This is a structural change event. Representations of other existing items in the data
6755         * set are still considered up to date and will not be rebound, though their positions
6756         * may be altered.</p>
6757         *
6758         * @param positionStart Previous position of the first item that was removed
6759         * @param itemCount Number of items removed from the data set
6760         */
6761        public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
6762            mObservable.notifyItemRangeRemoved(positionStart, itemCount);
6763        }
6764    }
6765
6766    void dispatchChildDetached(View child) {
6767        final ViewHolder viewHolder = getChildViewHolderInt(child);
6768        onChildDetachedFromWindow(child);
6769        if (mAdapter != null && viewHolder != null) {
6770            mAdapter.onViewDetachedFromWindow(viewHolder);
6771        }
6772        if (mOnChildAttachStateListeners != null) {
6773            final int cnt = mOnChildAttachStateListeners.size();
6774            for (int i = cnt - 1; i >= 0; i--) {
6775                mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
6776            }
6777        }
6778    }
6779
6780    void dispatchChildAttached(View child) {
6781        final ViewHolder viewHolder = getChildViewHolderInt(child);
6782        onChildAttachedToWindow(child);
6783        if (mAdapter != null && viewHolder != null) {
6784            mAdapter.onViewAttachedToWindow(viewHolder);
6785        }
6786        if (mOnChildAttachStateListeners != null) {
6787            final int cnt = mOnChildAttachStateListeners.size();
6788            for (int i = cnt - 1; i >= 0; i--) {
6789                mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
6790            }
6791        }
6792    }
6793
6794    /**
6795     * A <code>LayoutManager</code> is responsible for measuring and positioning item views
6796     * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
6797     * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
6798     * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
6799     * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
6800     * layout managers are provided for general use.
6801     * <p/>
6802     * If the LayoutManager specifies a default constructor or one with the signature
6803     * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
6804     * instantiate and set the LayoutManager when being inflated. Most used properties can
6805     * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
6806     * a LayoutManager specifies both constructors, the non-default constructor will take
6807     * precedence.
6808     *
6809     */
6810    public abstract static class LayoutManager {
6811        ChildHelper mChildHelper;
6812        RecyclerView mRecyclerView;
6813
6814        @Nullable
6815        SmoothScroller mSmoothScroller;
6816
6817        boolean mRequestedSimpleAnimations = false;
6818
6819        boolean mIsAttachedToWindow = false;
6820
6821        boolean mAutoMeasure = false;
6822
6823        /**
6824         * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
6825         * if the space that will be given to it is already larger than what it has measured before.
6826         */
6827        private boolean mMeasurementCacheEnabled = true;
6828
6829        private boolean mItemPrefetchEnabled = true;
6830
6831        /**
6832         * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
6833         * requested by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)} or
6834         * {@link #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)} call.
6835         *
6836         * If expanded by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)},
6837         * will be reset upon layout to prevent initial prefetches (often large, since they're
6838         * proportional to expected child count) from expanding cache permanently.
6839         */
6840        int mPrefetchMaxCountObserved;
6841
6842        /**
6843         * If true, mPrefetchMaxCountObserved is only valid until next layout, and should be reset.
6844         */
6845        boolean mPrefetchMaxObservedInInitialPrefetch;
6846
6847        /**
6848         * These measure specs might be the measure specs that were passed into RecyclerView's
6849         * onMeasure method OR fake measure specs created by the RecyclerView.
6850         * For example, when a layout is run, RecyclerView always sets these specs to be
6851         * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
6852         * <p>
6853         * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
6854         * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
6855         * corrupt values. Older platforms have no responsibility to provide a size if they set
6856         * mode to unspecified.
6857         */
6858        private int mWidthMode, mHeightMode;
6859        private int mWidth, mHeight;
6860
6861
6862        /**
6863         * Interface for LayoutManagers to request items to be prefetched, based on position, with
6864         * specified distance from viewport, which indicates priority.
6865         *
6866         * @see LayoutManager#collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
6867         * @see LayoutManager#collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
6868         */
6869        public interface LayoutPrefetchRegistry {
6870            /**
6871             * Requests an an item to be prefetched, based on position, with a specified distance,
6872             * indicating priority.
6873             *
6874             * @param layoutPosition Position of the item to prefetch.
6875             * @param pixelDistance Distance from the current viewport to the bounds of the item,
6876             *                      must be non-negative.
6877             */
6878            void addPosition(int layoutPosition, int pixelDistance);
6879        }
6880
6881        void setRecyclerView(RecyclerView recyclerView) {
6882            if (recyclerView == null) {
6883                mRecyclerView = null;
6884                mChildHelper = null;
6885                mWidth = 0;
6886                mHeight = 0;
6887            } else {
6888                mRecyclerView = recyclerView;
6889                mChildHelper = recyclerView.mChildHelper;
6890                mWidth = recyclerView.getWidth();
6891                mHeight = recyclerView.getHeight();
6892            }
6893            mWidthMode = MeasureSpec.EXACTLY;
6894            mHeightMode = MeasureSpec.EXACTLY;
6895        }
6896
6897        void setMeasureSpecs(int wSpec, int hSpec) {
6898            mWidth = MeasureSpec.getSize(wSpec);
6899            mWidthMode = MeasureSpec.getMode(wSpec);
6900            if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
6901                mWidth = 0;
6902            }
6903
6904            mHeight = MeasureSpec.getSize(hSpec);
6905            mHeightMode = MeasureSpec.getMode(hSpec);
6906            if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
6907                mHeight = 0;
6908            }
6909        }
6910
6911        /**
6912         * Called after a layout is calculated during a measure pass when using auto-measure.
6913         * <p>
6914         * It simply traverses all children to calculate a bounding box then calls
6915         * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
6916         * if they need to handle the bounding box differently.
6917         * <p>
6918         * For example, GridLayoutManager override that method to ensure that even if a column is
6919         * empty, the GridLayoutManager still measures wide enough to include it.
6920         *
6921         * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
6922         * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
6923         */
6924        void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
6925            final int count = getChildCount();
6926            if (count == 0) {
6927                mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
6928                return;
6929            }
6930            int minX = Integer.MAX_VALUE;
6931            int minY = Integer.MAX_VALUE;
6932            int maxX = Integer.MIN_VALUE;
6933            int maxY = Integer.MIN_VALUE;
6934
6935            for (int i = 0; i < count; i++) {
6936                View child = getChildAt(i);
6937                final Rect bounds = mRecyclerView.mTempRect;
6938                getDecoratedBoundsWithMargins(child, bounds);
6939                if (bounds.left < minX) {
6940                    minX = bounds.left;
6941                }
6942                if (bounds.right > maxX) {
6943                    maxX = bounds.right;
6944                }
6945                if (bounds.top < minY) {
6946                    minY = bounds.top;
6947                }
6948                if (bounds.bottom > maxY) {
6949                    maxY = bounds.bottom;
6950                }
6951            }
6952            mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
6953            setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
6954        }
6955
6956        /**
6957         * Sets the measured dimensions from the given bounding box of the children and the
6958         * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
6959         * called after the RecyclerView calls
6960         * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a measurement pass.
6961         * <p>
6962         * This method should call {@link #setMeasuredDimension(int, int)}.
6963         * <p>
6964         * The default implementation adds the RecyclerView's padding to the given bounding box
6965         * then caps the value to be within the given measurement specs.
6966         * <p>
6967         * This method is only called if the LayoutManager opted into the auto measurement API.
6968         *
6969         * @param childrenBounds The bounding box of all children
6970         * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
6971         * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
6972         *
6973         * @see #setAutoMeasureEnabled(boolean)
6974         */
6975        public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
6976            int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
6977            int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
6978            int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
6979            int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
6980            setMeasuredDimension(width, height);
6981        }
6982
6983        /**
6984         * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
6985         */
6986        public void requestLayout() {
6987            if (mRecyclerView != null) {
6988                mRecyclerView.requestLayout();
6989            }
6990        }
6991
6992        /**
6993         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
6994         * {@link IllegalStateException} if it <b>is not</b>.
6995         *
6996         * @param message The message for the exception. Can be null.
6997         * @see #assertNotInLayoutOrScroll(String)
6998         */
6999        public void assertInLayoutOrScroll(String message) {
7000            if (mRecyclerView != null) {
7001                mRecyclerView.assertInLayoutOrScroll(message);
7002            }
7003        }
7004
7005        /**
7006         * Chooses a size from the given specs and parameters that is closest to the desired size
7007         * and also complies with the spec.
7008         *
7009         * @param spec The measureSpec
7010         * @param desired The preferred measurement
7011         * @param min The minimum value
7012         *
7013         * @return A size that fits to the given specs
7014         */
7015        public static int chooseSize(int spec, int desired, int min) {
7016            final int mode = View.MeasureSpec.getMode(spec);
7017            final int size = View.MeasureSpec.getSize(spec);
7018            switch (mode) {
7019                case View.MeasureSpec.EXACTLY:
7020                    return size;
7021                case View.MeasureSpec.AT_MOST:
7022                    return Math.min(size, Math.max(desired, min));
7023                case View.MeasureSpec.UNSPECIFIED:
7024                default:
7025                    return Math.max(desired, min);
7026            }
7027        }
7028
7029        /**
7030         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7031         * {@link IllegalStateException} if it <b>is</b>.
7032         *
7033         * @param message The message for the exception. Can be null.
7034         * @see #assertInLayoutOrScroll(String)
7035         */
7036        public void assertNotInLayoutOrScroll(String message) {
7037            if (mRecyclerView != null) {
7038                mRecyclerView.assertNotInLayoutOrScroll(message);
7039            }
7040        }
7041
7042        /**
7043         * Defines whether the layout should be measured by the RecyclerView or the LayoutManager
7044         * wants to handle the layout measurements itself.
7045         * <p>
7046         * This method is usually called by the LayoutManager with value {@code true} if it wants
7047         * to support WRAP_CONTENT. If you are using a public LayoutManager but want to customize
7048         * the measurement logic, you can call this method with {@code false} and override
7049         * {@link LayoutManager#onMeasure(int, int)} to implement your custom measurement logic.
7050         * <p>
7051         * AutoMeasure is a convenience mechanism for LayoutManagers to easily wrap their content or
7052         * handle various specs provided by the RecyclerView's parent.
7053         * It works by calling {@link LayoutManager#onLayoutChildren(Recycler, State)} during an
7054         * {@link RecyclerView#onMeasure(int, int)} call, then calculating desired dimensions based
7055         * on children's positions. It does this while supporting all existing animation
7056         * capabilities of the RecyclerView.
7057         * <p>
7058         * AutoMeasure works as follows:
7059         * <ol>
7060         * <li>LayoutManager should call {@code setAutoMeasureEnabled(true)} to enable it. All of
7061         * the framework LayoutManagers use {@code auto-measure}.</li>
7062         * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided specs are
7063         * exact, RecyclerView will only call LayoutManager's {@code onMeasure} and return without
7064         * doing any layout calculation.</li>
7065         * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
7066         * layout process in {@code onMeasure} call. It will process all pending Adapter updates and
7067         * decide whether to run a predictive layout or not. If it decides to do so, it will first
7068         * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
7069         * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
7070         * return the width and height of the RecyclerView as of the last layout calculation.
7071         * <p>
7072         * After handling the predictive case, RecyclerView will call
7073         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7074         * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
7075         * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
7076         * {@link #getWidth()} and {@link #getWidthMode()}.</li>
7077         * <li>After the layout calculation, RecyclerView sets the measured width & height by
7078         * calculating the bounding box for the children (+ RecyclerView's padding). The
7079         * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
7080         * different values. For instance, GridLayoutManager overrides this value to handle the case
7081         * where if it is vertical and has 3 columns but only 2 items, it should still measure its
7082         * width to fit 3 items, not 2.</li>
7083         * <li>Any following on measure call to the RecyclerView will run
7084         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7085         * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
7086         * take care of which views are actually added / removed / moved / changed for animations so
7087         * that the LayoutManager should not worry about them and handle each
7088         * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.
7089         * </li>
7090         * <li>When measure is complete and RecyclerView's
7091         * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
7092         * whether it already did layout calculations during the measure pass and if so, it re-uses
7093         * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
7094         * if the last measure spec was different from the final dimensions or adapter contents
7095         * have changed between the measure call and the layout call.</li>
7096         * <li>Finally, animations are calculated and run as usual.</li>
7097         * </ol>
7098         *
7099         * @param enabled <code>True</code> if the Layout should be measured by the
7100         *                             RecyclerView, <code>false</code> if the LayoutManager wants
7101         *                             to measure itself.
7102         *
7103         * @see #setMeasuredDimension(Rect, int, int)
7104         * @see #isAutoMeasureEnabled()
7105         */
7106        public void setAutoMeasureEnabled(boolean enabled) {
7107            mAutoMeasure = enabled;
7108        }
7109
7110        /**
7111         * Returns whether the LayoutManager uses the automatic measurement API or not.
7112         *
7113         * @return <code>True</code> if the LayoutManager is measured by the RecyclerView or
7114         * <code>false</code> if it measures itself.
7115         *
7116         * @see #setAutoMeasureEnabled(boolean)
7117         */
7118        public boolean isAutoMeasureEnabled() {
7119            return mAutoMeasure;
7120        }
7121
7122        /**
7123         * Returns whether this LayoutManager supports automatic item animations.
7124         * A LayoutManager wishing to support item animations should obey certain
7125         * rules as outlined in {@link #onLayoutChildren(Recycler, State)}.
7126         * The default return value is <code>false</code>, so subclasses of LayoutManager
7127         * will not get predictive item animations by default.
7128         *
7129         * <p>Whether item animations are enabled in a RecyclerView is determined both
7130         * by the return value from this method and the
7131         * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
7132         * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
7133         * method returns false, then simple item animations will be enabled, in which
7134         * views that are moving onto or off of the screen are simply faded in/out. If
7135         * the RecyclerView has a non-null ItemAnimator and this method returns true,
7136         * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to
7137         * setup up the information needed to more intelligently predict where appearing
7138         * and disappearing views should be animated from/to.</p>
7139         *
7140         * @return true if predictive item animations should be enabled, false otherwise
7141         */
7142        public boolean supportsPredictiveItemAnimations() {
7143            return false;
7144        }
7145
7146        /**
7147         * Sets whether the LayoutManager should be queried for views outside of
7148         * its viewport while the UI thread is idle between frames.
7149         *
7150         * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
7151         * view system traversals on devices running API 21 or greater. Default value is true.</p>
7152         *
7153         * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
7154         * to RenderThread and the starting up its next frame at the next VSync pulse. By
7155         * prefetching out of window views in this time period, delays from inflation and view
7156         * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
7157         *
7158         * <p>While prefetch is enabled, it will have the side effect of expanding the effective
7159         * size of the View cache to hold prefetched views.</p>
7160         *
7161         * @param enabled <code>True</code> if items should be prefetched in between traversals.
7162         *
7163         * @see #isItemPrefetchEnabled()
7164         */
7165        public final void setItemPrefetchEnabled(boolean enabled) {
7166            if (enabled != mItemPrefetchEnabled) {
7167                mItemPrefetchEnabled = enabled;
7168                mPrefetchMaxCountObserved = 0;
7169                if (mRecyclerView != null) {
7170                    mRecyclerView.mRecycler.updateViewCacheSize();
7171                }
7172            }
7173        }
7174
7175        /**
7176         * Sets whether the LayoutManager should be queried for views outside of
7177         * its viewport while the UI thread is idle between frames.
7178         *
7179         * @see #setItemPrefetchEnabled(boolean)
7180         *
7181         * @return true if item prefetch is enabled, false otherwise
7182         */
7183        public final boolean isItemPrefetchEnabled() {
7184            return mItemPrefetchEnabled;
7185        }
7186
7187        /**
7188         * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
7189         *
7190         * <p>If item prefetch is enabled, this method is called in between traversals to gather
7191         * which positions the LayoutManager will soon need, given upcoming movement in subsequent
7192         * traversals.</p>
7193         *
7194         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7195         * each item to be prepared, and these positions will have their ViewHolders created and
7196         * bound, if there is sufficient time available, in advance of being needed by a
7197         * scroll or layout.</p>
7198         *
7199         * @param dx X movement component.
7200         * @param dy Y movement component.
7201         * @param state State of RecyclerView
7202         * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7203         *
7204         * @see #isItemPrefetchEnabled()
7205         * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
7206         */
7207        public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
7208                LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7209
7210        /**
7211         * Gather all positions from the LayoutManager to be prefetched in preperation for its
7212         * RecyclerView to come on screen, due to the movement of another, containing RecyclerView.
7213         *
7214         * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
7215         *
7216         * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
7217         * LayoutManager, this method is called in between draw traversals to gather
7218         * which positions this LayoutManager will first need, once it appears on the screen.</p>
7219         *
7220         * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
7221         * vertically scrolling LayoutManager, this method would be called when the horizontal list
7222         * is about to come onscreen.</p>
7223         *
7224         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7225         * each item to be prepared, and these positions will have their ViewHolders created and
7226         * bound, if there is sufficient time available, in advance of being needed by a
7227         * scroll or layout.</p>
7228         *
7229         * @param adapterItemCount number of items in the associated adapter.
7230         * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7231         *
7232         * @see #isItemPrefetchEnabled()
7233         * @see #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
7234         */
7235        public void collectInitialPrefetchPositions(int adapterItemCount,
7236                LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7237
7238        void dispatchAttachedToWindow(RecyclerView view) {
7239            mIsAttachedToWindow = true;
7240            onAttachedToWindow(view);
7241        }
7242
7243        void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
7244            mIsAttachedToWindow = false;
7245            onDetachedFromWindow(view, recycler);
7246        }
7247
7248        /**
7249         * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
7250         * to a window.
7251         *
7252         * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
7253         * is attached to window.
7254         */
7255        public boolean isAttachedToWindow() {
7256            return mIsAttachedToWindow;
7257        }
7258
7259        /**
7260         * Causes the Runnable to execute on the next animation time step.
7261         * The runnable will be run on the user interface thread.
7262         * <p>
7263         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7264         *
7265         * @param action The Runnable that will be executed.
7266         *
7267         * @see #removeCallbacks
7268         */
7269        public void postOnAnimation(Runnable action) {
7270            if (mRecyclerView != null) {
7271                mRecyclerView.postOnAnimation(action);
7272            }
7273        }
7274
7275        /**
7276         * Removes the specified Runnable from the message queue.
7277         * <p>
7278         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7279         *
7280         * @param action The Runnable to remove from the message handling queue
7281         *
7282         * @return true if RecyclerView could ask the Handler to remove the Runnable,
7283         *         false otherwise. When the returned value is true, the Runnable
7284         *         may or may not have been actually removed from the message queue
7285         *         (for instance, if the Runnable was not in the queue already.)
7286         *
7287         * @see #postOnAnimation
7288         */
7289        public boolean removeCallbacks(Runnable action) {
7290            if (mRecyclerView != null) {
7291                return mRecyclerView.removeCallbacks(action);
7292            }
7293            return false;
7294        }
7295        /**
7296         * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
7297         * is attached to a window.
7298         * <p>
7299         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7300         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7301         * not requested on the RecyclerView while it was detached.
7302         * <p>
7303         * Subclass implementations should always call through to the superclass implementation.
7304         *
7305         * @param view The RecyclerView this LayoutManager is bound to
7306         *
7307         * @see #onDetachedFromWindow(RecyclerView, Recycler)
7308         */
7309        @CallSuper
7310        public void onAttachedToWindow(RecyclerView view) {
7311        }
7312
7313        /**
7314         * @deprecated
7315         * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
7316         */
7317        @Deprecated
7318        public void onDetachedFromWindow(RecyclerView view) {
7319
7320        }
7321
7322        /**
7323         * Called when this LayoutManager is detached from its parent RecyclerView or when
7324         * its parent RecyclerView is detached from its window.
7325         * <p>
7326         * LayoutManager should clear all of its View references as another LayoutManager might be
7327         * assigned to the RecyclerView.
7328         * <p>
7329         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7330         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7331         * not requested on the RecyclerView while it was detached.
7332         * <p>
7333         * If your LayoutManager has View references that it cleans in on-detach, it should also
7334         * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
7335         * RecyclerView is re-attached.
7336         * <p>
7337         * Subclass implementations should always call through to the superclass implementation.
7338         *
7339         * @param view The RecyclerView this LayoutManager is bound to
7340         * @param recycler The recycler to use if you prefer to recycle your children instead of
7341         *                 keeping them around.
7342         *
7343         * @see #onAttachedToWindow(RecyclerView)
7344         */
7345        @CallSuper
7346        public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
7347            onDetachedFromWindow(view);
7348        }
7349
7350        /**
7351         * Check if the RecyclerView is configured to clip child views to its padding.
7352         *
7353         * @return true if this RecyclerView clips children to its padding, false otherwise
7354         */
7355        public boolean getClipToPadding() {
7356            return mRecyclerView != null && mRecyclerView.mClipToPadding;
7357        }
7358
7359        /**
7360         * Lay out all relevant child views from the given adapter.
7361         *
7362         * The LayoutManager is in charge of the behavior of item animations. By default,
7363         * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
7364         * item animations are enabled. This means that add/remove operations on the
7365         * adapter will result in animations to add new or appearing items, removed or
7366         * disappearing items, and moved items. If a LayoutManager returns false from
7367         * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
7368         * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
7369         * RecyclerView will have enough information to run those animations in a simple
7370         * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
7371         * simply fade views in and out, whether they are actually added/removed or whether
7372         * they are moved on or off the screen due to other add/remove operations.
7373         *
7374         * <p>A LayoutManager wanting a better item animation experience, where items can be
7375         * animated onto and off of the screen according to where the items exist when they
7376         * are not on screen, then the LayoutManager should return true from
7377         * {@link #supportsPredictiveItemAnimations()} and add additional logic to
7378         * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
7379         * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
7380         * once as a "pre" layout step to determine where items would have been prior to
7381         * a real layout, and again to do the "real" layout. In the pre-layout phase,
7382         * items will remember their pre-layout positions to allow them to be laid out
7383         * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
7384         * be returned from the scrap to help determine correct placement of other items.
7385         * These removed items should not be added to the child list, but should be used
7386         * to help calculate correct positioning of other views, including views that
7387         * were not previously onscreen (referred to as APPEARING views), but whose
7388         * pre-layout offscreen position can be determined given the extra
7389         * information about the pre-layout removed views.</p>
7390         *
7391         * <p>The second layout pass is the real layout in which only non-removed views
7392         * will be used. The only additional requirement during this pass is, if
7393         * {@link #supportsPredictiveItemAnimations()} returns true, to note which
7394         * views exist in the child list prior to layout and which are not there after
7395         * layout (referred to as DISAPPEARING views), and to position/layout those views
7396         * appropriately, without regard to the actual bounds of the RecyclerView. This allows
7397         * the animation system to know the location to which to animate these disappearing
7398         * views.</p>
7399         *
7400         * <p>The default LayoutManager implementations for RecyclerView handle all of these
7401         * requirements for animations already. Clients of RecyclerView can either use one
7402         * of these layout managers directly or look at their implementations of
7403         * onLayoutChildren() to see how they account for the APPEARING and
7404         * DISAPPEARING views.</p>
7405         *
7406         * @param recycler         Recycler to use for fetching potentially cached views for a
7407         *                         position
7408         * @param state            Transient state of RecyclerView
7409         */
7410        public void onLayoutChildren(Recycler recycler, State state) {
7411            Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
7412        }
7413
7414        /**
7415         * Called after a full layout calculation is finished. The layout calculation may include
7416         * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
7417         * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
7418         * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
7419         * <p>
7420         * This is a good place for the LayoutManager to do some cleanup like pending scroll
7421         * position, saved state etc.
7422         *
7423         * @param state Transient state of RecyclerView
7424         */
7425        public void onLayoutCompleted(State state) {
7426        }
7427
7428        /**
7429         * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
7430         *
7431         * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
7432         * to store extra information specific to the layout. Client code should subclass
7433         * {@link RecyclerView.LayoutParams} for this purpose.</p>
7434         *
7435         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7436         * you must also override
7437         * {@link #checkLayoutParams(LayoutParams)},
7438         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7439         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7440         *
7441         * @return A new LayoutParams for a child view
7442         */
7443        public abstract LayoutParams generateDefaultLayoutParams();
7444
7445        /**
7446         * Determines the validity of the supplied LayoutParams object.
7447         *
7448         * <p>This should check to make sure that the object is of the correct type
7449         * and all values are within acceptable ranges. The default implementation
7450         * returns <code>true</code> for non-null params.</p>
7451         *
7452         * @param lp LayoutParams object to check
7453         * @return true if this LayoutParams object is valid, false otherwise
7454         */
7455        public boolean checkLayoutParams(LayoutParams lp) {
7456            return lp != null;
7457        }
7458
7459        /**
7460         * Create a LayoutParams object suitable for this LayoutManager, copying relevant
7461         * values from the supplied LayoutParams object if possible.
7462         *
7463         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7464         * you must also override
7465         * {@link #checkLayoutParams(LayoutParams)},
7466         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7467         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7468         *
7469         * @param lp Source LayoutParams object to copy values from
7470         * @return a new LayoutParams object
7471         */
7472        public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
7473            if (lp instanceof LayoutParams) {
7474                return new LayoutParams((LayoutParams) lp);
7475            } else if (lp instanceof MarginLayoutParams) {
7476                return new LayoutParams((MarginLayoutParams) lp);
7477            } else {
7478                return new LayoutParams(lp);
7479            }
7480        }
7481
7482        /**
7483         * Create a LayoutParams object suitable for this LayoutManager from
7484         * an inflated layout resource.
7485         *
7486         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7487         * you must also override
7488         * {@link #checkLayoutParams(LayoutParams)},
7489         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7490         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7491         *
7492         * @param c Context for obtaining styled attributes
7493         * @param attrs AttributeSet describing the supplied arguments
7494         * @return a new LayoutParams object
7495         */
7496        public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
7497            return new LayoutParams(c, attrs);
7498        }
7499
7500        /**
7501         * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
7502         * The default implementation does nothing and returns 0.
7503         *
7504         * @param dx            distance to scroll by in pixels. X increases as scroll position
7505         *                      approaches the right.
7506         * @param recycler      Recycler to use for fetching potentially cached views for a
7507         *                      position
7508         * @param state         Transient state of RecyclerView
7509         * @return The actual distance scrolled. The return value will be negative if dx was
7510         * negative and scrolling proceeeded in that direction.
7511         * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
7512         */
7513        public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
7514            return 0;
7515        }
7516
7517        /**
7518         * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
7519         * The default implementation does nothing and returns 0.
7520         *
7521         * @param dy            distance to scroll in pixels. Y increases as scroll position
7522         *                      approaches the bottom.
7523         * @param recycler      Recycler to use for fetching potentially cached views for a
7524         *                      position
7525         * @param state         Transient state of RecyclerView
7526         * @return The actual distance scrolled. The return value will be negative if dy was
7527         * negative and scrolling proceeeded in that direction.
7528         * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
7529         */
7530        public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
7531            return 0;
7532        }
7533
7534        /**
7535         * Query if horizontal scrolling is currently supported. The default implementation
7536         * returns false.
7537         *
7538         * @return True if this LayoutManager can scroll the current contents horizontally
7539         */
7540        public boolean canScrollHorizontally() {
7541            return false;
7542        }
7543
7544        /**
7545         * Query if vertical scrolling is currently supported. The default implementation
7546         * returns false.
7547         *
7548         * @return True if this LayoutManager can scroll the current contents vertically
7549         */
7550        public boolean canScrollVertically() {
7551            return false;
7552        }
7553
7554        /**
7555         * Scroll to the specified adapter position.
7556         *
7557         * Actual position of the item on the screen depends on the LayoutManager implementation.
7558         * @param position Scroll to this adapter position.
7559         */
7560        public void scrollToPosition(int position) {
7561            if (DEBUG) {
7562                Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
7563            }
7564        }
7565
7566        /**
7567         * <p>Smooth scroll to the specified adapter position.</p>
7568         * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
7569         * instance and call {@link #startSmoothScroll(SmoothScroller)}.
7570         * </p>
7571         * @param recyclerView The RecyclerView to which this layout manager is attached
7572         * @param state    Current State of RecyclerView
7573         * @param position Scroll to this adapter position.
7574         */
7575        public void smoothScrollToPosition(RecyclerView recyclerView, State state,
7576                int position) {
7577            Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
7578        }
7579
7580        /**
7581         * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
7582         * <p>Calling this method will cancel any previous smooth scroll request.</p>
7583         * @param smoothScroller Instance which defines how smooth scroll should be animated
7584         */
7585        public void startSmoothScroll(SmoothScroller smoothScroller) {
7586            if (mSmoothScroller != null && smoothScroller != mSmoothScroller
7587                    && mSmoothScroller.isRunning()) {
7588                mSmoothScroller.stop();
7589            }
7590            mSmoothScroller = smoothScroller;
7591            mSmoothScroller.start(mRecyclerView, this);
7592        }
7593
7594        /**
7595         * @return true if RecycylerView is currently in the state of smooth scrolling.
7596         */
7597        public boolean isSmoothScrolling() {
7598            return mSmoothScroller != null && mSmoothScroller.isRunning();
7599        }
7600
7601
7602        /**
7603         * Returns the resolved layout direction for this RecyclerView.
7604         *
7605         * @return {@link android.view.View#LAYOUT_DIRECTION_RTL} if the layout
7606         * direction is RTL or returns
7607         * {@link android.view.View#LAYOUT_DIRECTION_LTR} if the layout direction
7608         * is not RTL.
7609         */
7610        public int getLayoutDirection() {
7611            return mRecyclerView.getLayoutDirection();
7612        }
7613
7614        /**
7615         * Ends all animations on the view created by the {@link ItemAnimator}.
7616         *
7617         * @param view The View for which the animations should be ended.
7618         * @see RecyclerView.ItemAnimator#endAnimations()
7619         */
7620        public void endAnimation(View view) {
7621            if (mRecyclerView.mItemAnimator != null) {
7622                mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
7623            }
7624        }
7625
7626        /**
7627         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
7628         * to the layout that is known to be going away, either because it has been
7629         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
7630         * visible portion of the container but is being laid out in order to inform RecyclerView
7631         * in how to animate the item out of view.
7632         * <p>
7633         * Views added via this method are going to be invisible to LayoutManager after the
7634         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
7635         * or won't be included in {@link #getChildCount()} method.
7636         *
7637         * @param child View to add and then remove with animation.
7638         */
7639        public void addDisappearingView(View child) {
7640            addDisappearingView(child, -1);
7641        }
7642
7643        /**
7644         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
7645         * to the layout that is known to be going away, either because it has been
7646         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
7647         * visible portion of the container but is being laid out in order to inform RecyclerView
7648         * in how to animate the item out of view.
7649         * <p>
7650         * Views added via this method are going to be invisible to LayoutManager after the
7651         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
7652         * or won't be included in {@link #getChildCount()} method.
7653         *
7654         * @param child View to add and then remove with animation.
7655         * @param index Index of the view.
7656         */
7657        public void addDisappearingView(View child, int index) {
7658            addViewInt(child, index, true);
7659        }
7660
7661        /**
7662         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
7663         * use this method to add views obtained from a {@link Recycler} using
7664         * {@link Recycler#getViewForPosition(int)}.
7665         *
7666         * @param child View to add
7667         */
7668        public void addView(View child) {
7669            addView(child, -1);
7670        }
7671
7672        /**
7673         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
7674         * use this method to add views obtained from a {@link Recycler} using
7675         * {@link Recycler#getViewForPosition(int)}.
7676         *
7677         * @param child View to add
7678         * @param index Index to add child at
7679         */
7680        public void addView(View child, int index) {
7681            addViewInt(child, index, false);
7682        }
7683
7684        private void addViewInt(View child, int index, boolean disappearing) {
7685            final ViewHolder holder = getChildViewHolderInt(child);
7686            if (disappearing || holder.isRemoved()) {
7687                // these views will be hidden at the end of the layout pass.
7688                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
7689            } else {
7690                // This may look like unnecessary but may happen if layout manager supports
7691                // predictive layouts and adapter removed then re-added the same item.
7692                // In this case, added version will be visible in the post layout (because add is
7693                // deferred) but RV will still bind it to the same View.
7694                // So if a View re-appears in post layout pass, remove it from disappearing list.
7695                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
7696            }
7697            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
7698            if (holder.wasReturnedFromScrap() || holder.isScrap()) {
7699                if (holder.isScrap()) {
7700                    holder.unScrap();
7701                } else {
7702                    holder.clearReturnedFromScrapFlag();
7703                }
7704                mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
7705                if (DISPATCH_TEMP_DETACH) {
7706                    child.dispatchFinishTemporaryDetach();
7707                }
7708            } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
7709                // ensure in correct position
7710                int currentIndex = mChildHelper.indexOfChild(child);
7711                if (index == -1) {
7712                    index = mChildHelper.getChildCount();
7713                }
7714                if (currentIndex == -1) {
7715                    throw new IllegalStateException("Added View has RecyclerView as parent but"
7716                            + " view is not a real child. Unfiltered index:"
7717                            + mRecyclerView.indexOfChild(child));
7718                }
7719                if (currentIndex != index) {
7720                    mRecyclerView.mLayout.moveView(currentIndex, index);
7721                }
7722            } else {
7723                mChildHelper.addView(child, index, false);
7724                lp.mInsetsDirty = true;
7725                if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
7726                    mSmoothScroller.onChildAttachedToWindow(child);
7727                }
7728            }
7729            if (lp.mPendingInvalidate) {
7730                if (DEBUG) {
7731                    Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
7732                }
7733                holder.itemView.invalidate();
7734                lp.mPendingInvalidate = false;
7735            }
7736        }
7737
7738        /**
7739         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
7740         * use this method to completely remove a child view that is no longer needed.
7741         * LayoutManagers should strongly consider recycling removed views using
7742         * {@link Recycler#recycleView(android.view.View)}.
7743         *
7744         * @param child View to remove
7745         */
7746        public void removeView(View child) {
7747            mChildHelper.removeView(child);
7748        }
7749
7750        /**
7751         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
7752         * use this method to completely remove a child view that is no longer needed.
7753         * LayoutManagers should strongly consider recycling removed views using
7754         * {@link Recycler#recycleView(android.view.View)}.
7755         *
7756         * @param index Index of the child view to remove
7757         */
7758        public void removeViewAt(int index) {
7759            final View child = getChildAt(index);
7760            if (child != null) {
7761                mChildHelper.removeViewAt(index);
7762            }
7763        }
7764
7765        /**
7766         * Remove all views from the currently attached RecyclerView. This will not recycle
7767         * any of the affected views; the LayoutManager is responsible for doing so if desired.
7768         */
7769        public void removeAllViews() {
7770            // Only remove non-animating views
7771            final int childCount = getChildCount();
7772            for (int i = childCount - 1; i >= 0; i--) {
7773                mChildHelper.removeViewAt(i);
7774            }
7775        }
7776
7777        /**
7778         * Returns offset of the RecyclerView's text baseline from the its top boundary.
7779         *
7780         * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
7781         * there is no baseline.
7782         */
7783        public int getBaseline() {
7784            return -1;
7785        }
7786
7787        /**
7788         * Returns the adapter position of the item represented by the given View. This does not
7789         * contain any adapter changes that might have happened after the last layout.
7790         *
7791         * @param view The view to query
7792         * @return The adapter position of the item which is rendered by this View.
7793         */
7794        public int getPosition(View view) {
7795            return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
7796        }
7797
7798        /**
7799         * Returns the View type defined by the adapter.
7800         *
7801         * @param view The view to query
7802         * @return The type of the view assigned by the adapter.
7803         */
7804        public int getItemViewType(View view) {
7805            return getChildViewHolderInt(view).getItemViewType();
7806        }
7807
7808        /**
7809         * Traverses the ancestors of the given view and returns the item view that contains it
7810         * and also a direct child of the LayoutManager.
7811         * <p>
7812         * Note that this method may return null if the view is a child of the RecyclerView but
7813         * not a child of the LayoutManager (e.g. running a disappear animation).
7814         *
7815         * @param view The view that is a descendant of the LayoutManager.
7816         *
7817         * @return The direct child of the LayoutManager which contains the given view or null if
7818         * the provided view is not a descendant of this LayoutManager.
7819         *
7820         * @see RecyclerView#getChildViewHolder(View)
7821         * @see RecyclerView#findContainingViewHolder(View)
7822         */
7823        @Nullable
7824        public View findContainingItemView(View view) {
7825            if (mRecyclerView == null) {
7826                return null;
7827            }
7828            View found = mRecyclerView.findContainingItemView(view);
7829            if (found == null) {
7830                return null;
7831            }
7832            if (mChildHelper.isHidden(found)) {
7833                return null;
7834            }
7835            return found;
7836        }
7837
7838        /**
7839         * Finds the view which represents the given adapter position.
7840         * <p>
7841         * This method traverses each child since it has no information about child order.
7842         * Override this method to improve performance if your LayoutManager keeps data about
7843         * child views.
7844         * <p>
7845         * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
7846         *
7847         * @param position Position of the item in adapter
7848         * @return The child view that represents the given position or null if the position is not
7849         * laid out
7850         */
7851        public View findViewByPosition(int position) {
7852            final int childCount = getChildCount();
7853            for (int i = 0; i < childCount; i++) {
7854                View child = getChildAt(i);
7855                ViewHolder vh = getChildViewHolderInt(child);
7856                if (vh == null) {
7857                    continue;
7858                }
7859                if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
7860                        && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
7861                    return child;
7862                }
7863            }
7864            return null;
7865        }
7866
7867        /**
7868         * Temporarily detach a child view.
7869         *
7870         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
7871         * views currently attached to the RecyclerView. Generally LayoutManager implementations
7872         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
7873         * so that the detached view may be rebound and reused.</p>
7874         *
7875         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
7876         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
7877         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
7878         * before the LayoutManager entry point method called by RecyclerView returns.</p>
7879         *
7880         * @param child Child to detach
7881         */
7882        public void detachView(View child) {
7883            final int ind = mChildHelper.indexOfChild(child);
7884            if (ind >= 0) {
7885                detachViewInternal(ind, child);
7886            }
7887        }
7888
7889        /**
7890         * Temporarily detach a child view.
7891         *
7892         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
7893         * views currently attached to the RecyclerView. Generally LayoutManager implementations
7894         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
7895         * so that the detached view may be rebound and reused.</p>
7896         *
7897         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
7898         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
7899         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
7900         * before the LayoutManager entry point method called by RecyclerView returns.</p>
7901         *
7902         * @param index Index of the child to detach
7903         */
7904        public void detachViewAt(int index) {
7905            detachViewInternal(index, getChildAt(index));
7906        }
7907
7908        private void detachViewInternal(int index, View view) {
7909            if (DISPATCH_TEMP_DETACH) {
7910                view.dispatchStartTemporaryDetach();
7911            }
7912            mChildHelper.detachViewFromParent(index);
7913        }
7914
7915        /**
7916         * Reattach a previously {@link #detachView(android.view.View) detached} view.
7917         * This method should not be used to reattach views that were previously
7918         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
7919         *
7920         * @param child Child to reattach
7921         * @param index Intended child index for child
7922         * @param lp LayoutParams for child
7923         */
7924        public void attachView(View child, int index, LayoutParams lp) {
7925            ViewHolder vh = getChildViewHolderInt(child);
7926            if (vh.isRemoved()) {
7927                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
7928            } else {
7929                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
7930            }
7931            mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
7932            if (DISPATCH_TEMP_DETACH)  {
7933                child.dispatchFinishTemporaryDetach();
7934            }
7935        }
7936
7937        /**
7938         * Reattach a previously {@link #detachView(android.view.View) detached} view.
7939         * This method should not be used to reattach views that were previously
7940         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
7941         *
7942         * @param child Child to reattach
7943         * @param index Intended child index for child
7944         */
7945        public void attachView(View child, int index) {
7946            attachView(child, index, (LayoutParams) child.getLayoutParams());
7947        }
7948
7949        /**
7950         * Reattach a previously {@link #detachView(android.view.View) detached} view.
7951         * This method should not be used to reattach views that were previously
7952         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
7953         *
7954         * @param child Child to reattach
7955         */
7956        public void attachView(View child) {
7957            attachView(child, -1);
7958        }
7959
7960        /**
7961         * Finish removing a view that was previously temporarily
7962         * {@link #detachView(android.view.View) detached}.
7963         *
7964         * @param child Detached child to remove
7965         */
7966        public void removeDetachedView(View child) {
7967            mRecyclerView.removeDetachedView(child, false);
7968        }
7969
7970        /**
7971         * Moves a View from one position to another.
7972         *
7973         * @param fromIndex The View's initial index
7974         * @param toIndex The View's target index
7975         */
7976        public void moveView(int fromIndex, int toIndex) {
7977            View view = getChildAt(fromIndex);
7978            if (view == null) {
7979                throw new IllegalArgumentException("Cannot move a child from non-existing index:"
7980                        + fromIndex);
7981            }
7982            detachViewAt(fromIndex);
7983            attachView(view, toIndex);
7984        }
7985
7986        /**
7987         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
7988         *
7989         * <p>Scrapping a view allows it to be rebound and reused to show updated or
7990         * different data.</p>
7991         *
7992         * @param child Child to detach and scrap
7993         * @param recycler Recycler to deposit the new scrap view into
7994         */
7995        public void detachAndScrapView(View child, Recycler recycler) {
7996            int index = mChildHelper.indexOfChild(child);
7997            scrapOrRecycleView(recycler, index, child);
7998        }
7999
8000        /**
8001         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8002         *
8003         * <p>Scrapping a view allows it to be rebound and reused to show updated or
8004         * different data.</p>
8005         *
8006         * @param index Index of child to detach and scrap
8007         * @param recycler Recycler to deposit the new scrap view into
8008         */
8009        public void detachAndScrapViewAt(int index, Recycler recycler) {
8010            final View child = getChildAt(index);
8011            scrapOrRecycleView(recycler, index, child);
8012        }
8013
8014        /**
8015         * Remove a child view and recycle it using the given Recycler.
8016         *
8017         * @param child Child to remove and recycle
8018         * @param recycler Recycler to use to recycle child
8019         */
8020        public void removeAndRecycleView(View child, Recycler recycler) {
8021            removeView(child);
8022            recycler.recycleView(child);
8023        }
8024
8025        /**
8026         * Remove a child view and recycle it using the given Recycler.
8027         *
8028         * @param index Index of child to remove and recycle
8029         * @param recycler Recycler to use to recycle child
8030         */
8031        public void removeAndRecycleViewAt(int index, Recycler recycler) {
8032            final View view = getChildAt(index);
8033            removeViewAt(index);
8034            recycler.recycleView(view);
8035        }
8036
8037        /**
8038         * Return the current number of child views attached to the parent RecyclerView.
8039         * This does not include child views that were temporarily detached and/or scrapped.
8040         *
8041         * @return Number of attached children
8042         */
8043        public int getChildCount() {
8044            return mChildHelper != null ? mChildHelper.getChildCount() : 0;
8045        }
8046
8047        /**
8048         * Return the child view at the given index
8049         * @param index Index of child to return
8050         * @return Child view at index
8051         */
8052        public View getChildAt(int index) {
8053            return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
8054        }
8055
8056        /**
8057         * Return the width measurement spec mode of the RecyclerView.
8058         * <p>
8059         * This value is set only if the LayoutManager opts into the auto measure api via
8060         * {@link #setAutoMeasureEnabled(boolean)}.
8061         * <p>
8062         * When RecyclerView is running a layout, this value is always set to
8063         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8064         *
8065         * @return Width measure spec mode.
8066         *
8067         * @see View.MeasureSpec#getMode(int)
8068         * @see View#onMeasure(int, int)
8069         */
8070        public int getWidthMode() {
8071            return mWidthMode;
8072        }
8073
8074        /**
8075         * Return the height measurement spec mode of the RecyclerView.
8076         * <p>
8077         * This value is set only if the LayoutManager opts into the auto measure api via
8078         * {@link #setAutoMeasureEnabled(boolean)}.
8079         * <p>
8080         * When RecyclerView is running a layout, this value is always set to
8081         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8082         *
8083         * @return Height measure spec mode.
8084         *
8085         * @see View.MeasureSpec#getMode(int)
8086         * @see View#onMeasure(int, int)
8087         */
8088        public int getHeightMode() {
8089            return mHeightMode;
8090        }
8091
8092        /**
8093         * Return the width of the parent RecyclerView
8094         *
8095         * @return Width in pixels
8096         */
8097        public int getWidth() {
8098            return mWidth;
8099        }
8100
8101        /**
8102         * Return the height of the parent RecyclerView
8103         *
8104         * @return Height in pixels
8105         */
8106        public int getHeight() {
8107            return mHeight;
8108        }
8109
8110        /**
8111         * Return the left padding of the parent RecyclerView
8112         *
8113         * @return Padding in pixels
8114         */
8115        public int getPaddingLeft() {
8116            return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
8117        }
8118
8119        /**
8120         * Return the top padding of the parent RecyclerView
8121         *
8122         * @return Padding in pixels
8123         */
8124        public int getPaddingTop() {
8125            return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
8126        }
8127
8128        /**
8129         * Return the right padding of the parent RecyclerView
8130         *
8131         * @return Padding in pixels
8132         */
8133        public int getPaddingRight() {
8134            return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
8135        }
8136
8137        /**
8138         * Return the bottom padding of the parent RecyclerView
8139         *
8140         * @return Padding in pixels
8141         */
8142        public int getPaddingBottom() {
8143            return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
8144        }
8145
8146        /**
8147         * Return the start padding of the parent RecyclerView
8148         *
8149         * @return Padding in pixels
8150         */
8151        public int getPaddingStart() {
8152            return mRecyclerView != null ? mRecyclerView.getPaddingStart() : 0;
8153        }
8154
8155        /**
8156         * Return the end padding of the parent RecyclerView
8157         *
8158         * @return Padding in pixels
8159         */
8160        public int getPaddingEnd() {
8161            return mRecyclerView != null ? mRecyclerView.getPaddingEnd() : 0;
8162        }
8163
8164        /**
8165         * Returns true if the RecyclerView this LayoutManager is bound to has focus.
8166         *
8167         * @return True if the RecyclerView has focus, false otherwise.
8168         * @see View#isFocused()
8169         */
8170        public boolean isFocused() {
8171            return mRecyclerView != null && mRecyclerView.isFocused();
8172        }
8173
8174        /**
8175         * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
8176         *
8177         * @return true if the RecyclerView has or contains focus
8178         * @see View#hasFocus()
8179         */
8180        public boolean hasFocus() {
8181            return mRecyclerView != null && mRecyclerView.hasFocus();
8182        }
8183
8184        /**
8185         * Returns the item View which has or contains focus.
8186         *
8187         * @return A direct child of RecyclerView which has focus or contains the focused child.
8188         */
8189        public View getFocusedChild() {
8190            if (mRecyclerView == null) {
8191                return null;
8192            }
8193            final View focused = mRecyclerView.getFocusedChild();
8194            if (focused == null || mChildHelper.isHidden(focused)) {
8195                return null;
8196            }
8197            return focused;
8198        }
8199
8200        /**
8201         * Returns the number of items in the adapter bound to the parent RecyclerView.
8202         * <p>
8203         * Note that this number is not necessarily equal to
8204         * {@link State#getItemCount() State#getItemCount()}. In methods where {@link State} is
8205         * available, you should use {@link State#getItemCount() State#getItemCount()} instead.
8206         * For more details, check the documentation for
8207         * {@link State#getItemCount() State#getItemCount()}.
8208         *
8209         * @return The number of items in the bound adapter
8210         * @see State#getItemCount()
8211         */
8212        public int getItemCount() {
8213            final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
8214            return a != null ? a.getItemCount() : 0;
8215        }
8216
8217        /**
8218         * Offset all child views attached to the parent RecyclerView by dx pixels along
8219         * the horizontal axis.
8220         *
8221         * @param dx Pixels to offset by
8222         */
8223        public void offsetChildrenHorizontal(int dx) {
8224            if (mRecyclerView != null) {
8225                mRecyclerView.offsetChildrenHorizontal(dx);
8226            }
8227        }
8228
8229        /**
8230         * Offset all child views attached to the parent RecyclerView by dy pixels along
8231         * the vertical axis.
8232         *
8233         * @param dy Pixels to offset by
8234         */
8235        public void offsetChildrenVertical(int dy) {
8236            if (mRecyclerView != null) {
8237                mRecyclerView.offsetChildrenVertical(dy);
8238            }
8239        }
8240
8241        /**
8242         * Flags a view so that it will not be scrapped or recycled.
8243         * <p>
8244         * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
8245         * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
8246         * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
8247         * ignore the child.
8248         * <p>
8249         * Before this child can be recycled again, you have to call
8250         * {@link #stopIgnoringView(View)}.
8251         * <p>
8252         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8253         *
8254         * @param view View to ignore.
8255         * @see #stopIgnoringView(View)
8256         */
8257        public void ignoreView(View view) {
8258            if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
8259                // checking this because calling this method on a recycled or detached view may
8260                // cause loss of state.
8261                throw new IllegalArgumentException("View should be fully attached to be ignored");
8262            }
8263            final ViewHolder vh = getChildViewHolderInt(view);
8264            vh.addFlags(ViewHolder.FLAG_IGNORE);
8265            mRecyclerView.mViewInfoStore.removeViewHolder(vh);
8266        }
8267
8268        /**
8269         * View can be scrapped and recycled again.
8270         * <p>
8271         * Note that calling this method removes all information in the view holder.
8272         * <p>
8273         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8274         *
8275         * @param view View to ignore.
8276         */
8277        public void stopIgnoringView(View view) {
8278            final ViewHolder vh = getChildViewHolderInt(view);
8279            vh.stopIgnoring();
8280            vh.resetInternal();
8281            vh.addFlags(ViewHolder.FLAG_INVALID);
8282        }
8283
8284        /**
8285         * Temporarily detach and scrap all currently attached child views. Views will be scrapped
8286         * into the given Recycler. The Recycler may prefer to reuse scrap views before
8287         * other views that were previously recycled.
8288         *
8289         * @param recycler Recycler to scrap views into
8290         */
8291        public void detachAndScrapAttachedViews(Recycler recycler) {
8292            final int childCount = getChildCount();
8293            for (int i = childCount - 1; i >= 0; i--) {
8294                final View v = getChildAt(i);
8295                scrapOrRecycleView(recycler, i, v);
8296            }
8297        }
8298
8299        private void scrapOrRecycleView(Recycler recycler, int index, View view) {
8300            final ViewHolder viewHolder = getChildViewHolderInt(view);
8301            if (viewHolder.shouldIgnore()) {
8302                if (DEBUG) {
8303                    Log.d(TAG, "ignoring view " + viewHolder);
8304                }
8305                return;
8306            }
8307            if (viewHolder.isInvalid() && !viewHolder.isRemoved()
8308                    && !mRecyclerView.mAdapter.hasStableIds()) {
8309                removeViewAt(index);
8310                recycler.recycleViewHolderInternal(viewHolder);
8311            } else {
8312                detachViewAt(index);
8313                recycler.scrapView(view);
8314                mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
8315            }
8316        }
8317
8318        /**
8319         * Recycles the scrapped views.
8320         * <p>
8321         * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
8322         * the expected behavior if scrapped views are used for animations. Otherwise, we need to
8323         * call remove and invalidate RecyclerView to ensure UI update.
8324         *
8325         * @param recycler Recycler
8326         */
8327        void removeAndRecycleScrapInt(Recycler recycler) {
8328            final int scrapCount = recycler.getScrapCount();
8329            // Loop backward, recycler might be changed by removeDetachedView()
8330            for (int i = scrapCount - 1; i >= 0; i--) {
8331                final View scrap = recycler.getScrapViewAt(i);
8332                final ViewHolder vh = getChildViewHolderInt(scrap);
8333                if (vh.shouldIgnore()) {
8334                    continue;
8335                }
8336                // If the scrap view is animating, we need to cancel them first. If we cancel it
8337                // here, ItemAnimator callback may recycle it which will cause double recycling.
8338                // To avoid this, we mark it as not recycleable before calling the item animator.
8339                // Since removeDetachedView calls a user API, a common mistake (ending animations on
8340                // the view) may recycle it too, so we guard it before we call user APIs.
8341                vh.setIsRecyclable(false);
8342                if (vh.isTmpDetached()) {
8343                    mRecyclerView.removeDetachedView(scrap, false);
8344                }
8345                if (mRecyclerView.mItemAnimator != null) {
8346                    mRecyclerView.mItemAnimator.endAnimation(vh);
8347                }
8348                vh.setIsRecyclable(true);
8349                recycler.quickRecycleScrapView(scrap);
8350            }
8351            recycler.clearScrap();
8352            if (scrapCount > 0) {
8353                mRecyclerView.invalidate();
8354            }
8355        }
8356
8357
8358        /**
8359         * Measure a child view using standard measurement policy, taking the padding
8360         * of the parent RecyclerView and any added item decorations into account.
8361         *
8362         * <p>If the RecyclerView can be scrolled in either dimension the caller may
8363         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8364         *
8365         * @param child Child view to measure
8366         * @param widthUsed Width in pixels currently consumed by other views, if relevant
8367         * @param heightUsed Height in pixels currently consumed by other views, if relevant
8368         */
8369        public void measureChild(View child, int widthUsed, int heightUsed) {
8370            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8371
8372            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8373            widthUsed += insets.left + insets.right;
8374            heightUsed += insets.top + insets.bottom;
8375            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8376                    getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
8377                    canScrollHorizontally());
8378            final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8379                    getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
8380                    canScrollVertically());
8381            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8382                child.measure(widthSpec, heightSpec);
8383            }
8384        }
8385
8386        /**
8387         * RecyclerView internally does its own View measurement caching which should help with
8388         * WRAP_CONTENT.
8389         * <p>
8390         * Use this method if the View is already measured once in this layout pass.
8391         */
8392        boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8393            return !mMeasurementCacheEnabled
8394                    || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
8395                    || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
8396        }
8397
8398        // we may consider making this public
8399        /**
8400         * RecyclerView internally does its own View measurement caching which should help with
8401         * WRAP_CONTENT.
8402         * <p>
8403         * Use this method if the View is not yet measured and you need to decide whether to
8404         * measure this View or not.
8405         */
8406        boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8407            return child.isLayoutRequested()
8408                    || !mMeasurementCacheEnabled
8409                    || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
8410                    || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
8411        }
8412
8413        /**
8414         * In addition to the View Framework's measurement cache, RecyclerView uses its own
8415         * additional measurement cache for its children to avoid re-measuring them when not
8416         * necessary. It is on by default but it can be turned off via
8417         * {@link #setMeasurementCacheEnabled(boolean)}.
8418         *
8419         * @return True if measurement cache is enabled, false otherwise.
8420         *
8421         * @see #setMeasurementCacheEnabled(boolean)
8422         */
8423        public boolean isMeasurementCacheEnabled() {
8424            return mMeasurementCacheEnabled;
8425        }
8426
8427        /**
8428         * Sets whether RecyclerView should use its own measurement cache for the children. This is
8429         * a more aggressive cache than the framework uses.
8430         *
8431         * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
8432         *
8433         * @see #isMeasurementCacheEnabled()
8434         */
8435        public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
8436            mMeasurementCacheEnabled = measurementCacheEnabled;
8437        }
8438
8439        private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
8440            final int specMode = MeasureSpec.getMode(spec);
8441            final int specSize = MeasureSpec.getSize(spec);
8442            if (dimension > 0 && childSize != dimension) {
8443                return false;
8444            }
8445            switch (specMode) {
8446                case MeasureSpec.UNSPECIFIED:
8447                    return true;
8448                case MeasureSpec.AT_MOST:
8449                    return specSize >= childSize;
8450                case MeasureSpec.EXACTLY:
8451                    return  specSize == childSize;
8452            }
8453            return false;
8454        }
8455
8456        /**
8457         * Measure a child view using standard measurement policy, taking the padding
8458         * of the parent RecyclerView, any added item decorations and the child margins
8459         * into account.
8460         *
8461         * <p>If the RecyclerView can be scrolled in either dimension the caller may
8462         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8463         *
8464         * @param child Child view to measure
8465         * @param widthUsed Width in pixels currently consumed by other views, if relevant
8466         * @param heightUsed Height in pixels currently consumed by other views, if relevant
8467         */
8468        public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
8469            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8470
8471            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8472            widthUsed += insets.left + insets.right;
8473            heightUsed += insets.top + insets.bottom;
8474
8475            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8476                    getPaddingLeft() + getPaddingRight()
8477                            + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
8478                    canScrollHorizontally());
8479            final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8480                    getPaddingTop() + getPaddingBottom()
8481                            + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
8482                    canScrollVertically());
8483            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8484                child.measure(widthSpec, heightSpec);
8485            }
8486        }
8487
8488        /**
8489         * Calculate a MeasureSpec value for measuring a child view in one dimension.
8490         *
8491         * @param parentSize Size of the parent view where the child will be placed
8492         * @param padding Total space currently consumed by other elements of the parent
8493         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
8494         *                       Generally obtained from the child view's LayoutParams
8495         * @param canScroll true if the parent RecyclerView can scroll in this dimension
8496         *
8497         * @return a MeasureSpec value for the child view
8498         * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
8499         */
8500        @Deprecated
8501        public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
8502                boolean canScroll) {
8503            int size = Math.max(0, parentSize - padding);
8504            int resultSize = 0;
8505            int resultMode = 0;
8506            if (canScroll) {
8507                if (childDimension >= 0) {
8508                    resultSize = childDimension;
8509                    resultMode = MeasureSpec.EXACTLY;
8510                } else {
8511                    // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
8512                    // instead using UNSPECIFIED.
8513                    resultSize = 0;
8514                    resultMode = MeasureSpec.UNSPECIFIED;
8515                }
8516            } else {
8517                if (childDimension >= 0) {
8518                    resultSize = childDimension;
8519                    resultMode = MeasureSpec.EXACTLY;
8520                } else if (childDimension == LayoutParams.MATCH_PARENT) {
8521                    resultSize = size;
8522                    // TODO this should be my spec.
8523                    resultMode = MeasureSpec.EXACTLY;
8524                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8525                    resultSize = size;
8526                    resultMode = MeasureSpec.AT_MOST;
8527                }
8528            }
8529            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
8530        }
8531
8532        /**
8533         * Calculate a MeasureSpec value for measuring a child view in one dimension.
8534         *
8535         * @param parentSize Size of the parent view where the child will be placed
8536         * @param parentMode The measurement spec mode of the parent
8537         * @param padding Total space currently consumed by other elements of parent
8538         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
8539         *                       Generally obtained from the child view's LayoutParams
8540         * @param canScroll true if the parent RecyclerView can scroll in this dimension
8541         *
8542         * @return a MeasureSpec value for the child view
8543         */
8544        public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
8545                int childDimension, boolean canScroll) {
8546            int size = Math.max(0, parentSize - padding);
8547            int resultSize = 0;
8548            int resultMode = 0;
8549            if (canScroll) {
8550                if (childDimension >= 0) {
8551                    resultSize = childDimension;
8552                    resultMode = MeasureSpec.EXACTLY;
8553                } else if (childDimension == LayoutParams.MATCH_PARENT) {
8554                    switch (parentMode) {
8555                        case MeasureSpec.AT_MOST:
8556                        case MeasureSpec.EXACTLY:
8557                            resultSize = size;
8558                            resultMode = parentMode;
8559                            break;
8560                        case MeasureSpec.UNSPECIFIED:
8561                            resultSize = 0;
8562                            resultMode = MeasureSpec.UNSPECIFIED;
8563                            break;
8564                    }
8565                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8566                    resultSize = 0;
8567                    resultMode = MeasureSpec.UNSPECIFIED;
8568                }
8569            } else {
8570                if (childDimension >= 0) {
8571                    resultSize = childDimension;
8572                    resultMode = MeasureSpec.EXACTLY;
8573                } else if (childDimension == LayoutParams.MATCH_PARENT) {
8574                    resultSize = size;
8575                    resultMode = parentMode;
8576                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8577                    resultSize = size;
8578                    if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
8579                        resultMode = MeasureSpec.AT_MOST;
8580                    } else {
8581                        resultMode = MeasureSpec.UNSPECIFIED;
8582                    }
8583
8584                }
8585            }
8586            //noinspection WrongConstant
8587            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
8588        }
8589
8590        /**
8591         * Returns the measured width of the given child, plus the additional size of
8592         * any insets applied by {@link ItemDecoration ItemDecorations}.
8593         *
8594         * @param child Child view to query
8595         * @return child's measured width plus <code>ItemDecoration</code> insets
8596         *
8597         * @see View#getMeasuredWidth()
8598         */
8599        public int getDecoratedMeasuredWidth(View child) {
8600            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8601            return child.getMeasuredWidth() + insets.left + insets.right;
8602        }
8603
8604        /**
8605         * Returns the measured height of the given child, plus the additional size of
8606         * any insets applied by {@link ItemDecoration ItemDecorations}.
8607         *
8608         * @param child Child view to query
8609         * @return child's measured height plus <code>ItemDecoration</code> insets
8610         *
8611         * @see View#getMeasuredHeight()
8612         */
8613        public int getDecoratedMeasuredHeight(View child) {
8614            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8615            return child.getMeasuredHeight() + insets.top + insets.bottom;
8616        }
8617
8618        /**
8619         * Lay out the given child view within the RecyclerView using coordinates that
8620         * include any current {@link ItemDecoration ItemDecorations}.
8621         *
8622         * <p>LayoutManagers should prefer working in sizes and coordinates that include
8623         * item decoration insets whenever possible. This allows the LayoutManager to effectively
8624         * ignore decoration insets within measurement and layout code. See the following
8625         * methods:</p>
8626         * <ul>
8627         *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
8628         *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
8629         *     <li>{@link #measureChild(View, int, int)}</li>
8630         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
8631         *     <li>{@link #getDecoratedLeft(View)}</li>
8632         *     <li>{@link #getDecoratedTop(View)}</li>
8633         *     <li>{@link #getDecoratedRight(View)}</li>
8634         *     <li>{@link #getDecoratedBottom(View)}</li>
8635         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
8636         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
8637         * </ul>
8638         *
8639         * @param child Child to lay out
8640         * @param left Left edge, with item decoration insets included
8641         * @param top Top edge, with item decoration insets included
8642         * @param right Right edge, with item decoration insets included
8643         * @param bottom Bottom edge, with item decoration insets included
8644         *
8645         * @see View#layout(int, int, int, int)
8646         * @see #layoutDecoratedWithMargins(View, int, int, int, int)
8647         */
8648        public void layoutDecorated(View child, int left, int top, int right, int bottom) {
8649            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8650            child.layout(left + insets.left, top + insets.top, right - insets.right,
8651                    bottom - insets.bottom);
8652        }
8653
8654        /**
8655         * Lay out the given child view within the RecyclerView using coordinates that
8656         * include any current {@link ItemDecoration ItemDecorations} and margins.
8657         *
8658         * <p>LayoutManagers should prefer working in sizes and coordinates that include
8659         * item decoration insets whenever possible. This allows the LayoutManager to effectively
8660         * ignore decoration insets within measurement and layout code. See the following
8661         * methods:</p>
8662         * <ul>
8663         *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
8664         *     <li>{@link #measureChild(View, int, int)}</li>
8665         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
8666         *     <li>{@link #getDecoratedLeft(View)}</li>
8667         *     <li>{@link #getDecoratedTop(View)}</li>
8668         *     <li>{@link #getDecoratedRight(View)}</li>
8669         *     <li>{@link #getDecoratedBottom(View)}</li>
8670         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
8671         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
8672         * </ul>
8673         *
8674         * @param child Child to lay out
8675         * @param left Left edge, with item decoration insets and left margin included
8676         * @param top Top edge, with item decoration insets and top margin included
8677         * @param right Right edge, with item decoration insets and right margin included
8678         * @param bottom Bottom edge, with item decoration insets and bottom margin included
8679         *
8680         * @see View#layout(int, int, int, int)
8681         * @see #layoutDecorated(View, int, int, int, int)
8682         */
8683        public void layoutDecoratedWithMargins(View child, int left, int top, int right,
8684                int bottom) {
8685            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8686            final Rect insets = lp.mDecorInsets;
8687            child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
8688                    right - insets.right - lp.rightMargin,
8689                    bottom - insets.bottom - lp.bottomMargin);
8690        }
8691
8692        /**
8693         * Calculates the bounding box of the View while taking into account its matrix changes
8694         * (translation, scale etc) with respect to the RecyclerView.
8695         * <p>
8696         * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
8697         * the View's matrix so that the decor offsets also go through the same transformation.
8698         *
8699         * @param child The ItemView whose bounding box should be calculated.
8700         * @param includeDecorInsets True if the decor insets should be included in the bounding box
8701         * @param out The rectangle into which the output will be written.
8702         */
8703        public void getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out) {
8704            if (includeDecorInsets) {
8705                Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8706                out.set(-insets.left, -insets.top,
8707                        child.getWidth() + insets.right, child.getHeight() + insets.bottom);
8708            } else {
8709                out.set(0, 0, child.getWidth(), child.getHeight());
8710            }
8711
8712            if (mRecyclerView != null) {
8713                final Matrix childMatrix = child.getMatrix();
8714                if (childMatrix != null && !childMatrix.isIdentity()) {
8715                    final RectF tempRectF = mRecyclerView.mTempRectF;
8716                    tempRectF.set(out);
8717                    childMatrix.mapRect(tempRectF);
8718                    out.set(
8719                            (int) Math.floor(tempRectF.left),
8720                            (int) Math.floor(tempRectF.top),
8721                            (int) Math.ceil(tempRectF.right),
8722                            (int) Math.ceil(tempRectF.bottom)
8723                    );
8724                }
8725            }
8726            out.offset(child.getLeft(), child.getTop());
8727        }
8728
8729        /**
8730         * Returns the bounds of the view including its decoration and margins.
8731         *
8732         * @param view The view element to check
8733         * @param outBounds A rect that will receive the bounds of the element including its
8734         *                  decoration and margins.
8735         */
8736        public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
8737            RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
8738        }
8739
8740        /**
8741         * Returns the left edge of the given child view within its parent, offset by any applied
8742         * {@link ItemDecoration ItemDecorations}.
8743         *
8744         * @param child Child to query
8745         * @return Child left edge with offsets applied
8746         * @see #getLeftDecorationWidth(View)
8747         */
8748        public int getDecoratedLeft(View child) {
8749            return child.getLeft() - getLeftDecorationWidth(child);
8750        }
8751
8752        /**
8753         * Returns the top edge of the given child view within its parent, offset by any applied
8754         * {@link ItemDecoration ItemDecorations}.
8755         *
8756         * @param child Child to query
8757         * @return Child top edge with offsets applied
8758         * @see #getTopDecorationHeight(View)
8759         */
8760        public int getDecoratedTop(View child) {
8761            return child.getTop() - getTopDecorationHeight(child);
8762        }
8763
8764        /**
8765         * Returns the right edge of the given child view within its parent, offset by any applied
8766         * {@link ItemDecoration ItemDecorations}.
8767         *
8768         * @param child Child to query
8769         * @return Child right edge with offsets applied
8770         * @see #getRightDecorationWidth(View)
8771         */
8772        public int getDecoratedRight(View child) {
8773            return child.getRight() + getRightDecorationWidth(child);
8774        }
8775
8776        /**
8777         * Returns the bottom edge of the given child view within its parent, offset by any applied
8778         * {@link ItemDecoration ItemDecorations}.
8779         *
8780         * @param child Child to query
8781         * @return Child bottom edge with offsets applied
8782         * @see #getBottomDecorationHeight(View)
8783         */
8784        public int getDecoratedBottom(View child) {
8785            return child.getBottom() + getBottomDecorationHeight(child);
8786        }
8787
8788        /**
8789         * Calculates the item decor insets applied to the given child and updates the provided
8790         * Rect instance with the inset values.
8791         * <ul>
8792         *     <li>The Rect's left is set to the total width of left decorations.</li>
8793         *     <li>The Rect's top is set to the total height of top decorations.</li>
8794         *     <li>The Rect's right is set to the total width of right decorations.</li>
8795         *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
8796         * </ul>
8797         * <p>
8798         * Note that item decorations are automatically calculated when one of the LayoutManager's
8799         * measure child methods is called. If you need to measure the child with custom specs via
8800         * {@link View#measure(int, int)}, you can use this method to get decorations.
8801         *
8802         * @param child The child view whose decorations should be calculated
8803         * @param outRect The Rect to hold result values
8804         */
8805        public void calculateItemDecorationsForChild(View child, Rect outRect) {
8806            if (mRecyclerView == null) {
8807                outRect.set(0, 0, 0, 0);
8808                return;
8809            }
8810            Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8811            outRect.set(insets);
8812        }
8813
8814        /**
8815         * Returns the total height of item decorations applied to child's top.
8816         * <p>
8817         * Note that this value is not updated until the View is measured or
8818         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8819         *
8820         * @param child Child to query
8821         * @return The total height of item decorations applied to the child's top.
8822         * @see #getDecoratedTop(View)
8823         * @see #calculateItemDecorationsForChild(View, Rect)
8824         */
8825        public int getTopDecorationHeight(View child) {
8826            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
8827        }
8828
8829        /**
8830         * Returns the total height of item decorations applied to child's bottom.
8831         * <p>
8832         * Note that this value is not updated until the View is measured or
8833         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8834         *
8835         * @param child Child to query
8836         * @return The total height of item decorations applied to the child's bottom.
8837         * @see #getDecoratedBottom(View)
8838         * @see #calculateItemDecorationsForChild(View, Rect)
8839         */
8840        public int getBottomDecorationHeight(View child) {
8841            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
8842        }
8843
8844        /**
8845         * Returns the total width of item decorations applied to child's left.
8846         * <p>
8847         * Note that this value is not updated until the View is measured or
8848         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8849         *
8850         * @param child Child to query
8851         * @return The total width of item decorations applied to the child's left.
8852         * @see #getDecoratedLeft(View)
8853         * @see #calculateItemDecorationsForChild(View, Rect)
8854         */
8855        public int getLeftDecorationWidth(View child) {
8856            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
8857        }
8858
8859        /**
8860         * Returns the total width of item decorations applied to child's right.
8861         * <p>
8862         * Note that this value is not updated until the View is measured or
8863         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8864         *
8865         * @param child Child to query
8866         * @return The total width of item decorations applied to the child's right.
8867         * @see #getDecoratedRight(View)
8868         * @see #calculateItemDecorationsForChild(View, Rect)
8869         */
8870        public int getRightDecorationWidth(View child) {
8871            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
8872        }
8873
8874        /**
8875         * Called when searching for a focusable view in the given direction has failed
8876         * for the current content of the RecyclerView.
8877         *
8878         * <p>This is the LayoutManager's opportunity to populate views in the given direction
8879         * to fulfill the request if it can. The LayoutManager should attach and return
8880         * the view to be focused. The default implementation returns null.</p>
8881         *
8882         * @param focused   The currently focused view
8883         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
8884         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
8885         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
8886         *                  or 0 for not applicable
8887         * @param recycler  The recycler to use for obtaining views for currently offscreen items
8888         * @param state     Transient state of RecyclerView
8889         * @return The chosen view to be focused
8890         */
8891        @Nullable
8892        public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
8893                State state) {
8894            return null;
8895        }
8896
8897        /**
8898         * This method gives a LayoutManager an opportunity to intercept the initial focus search
8899         * before the default behavior of {@link FocusFinder} is used. If this method returns
8900         * null FocusFinder will attempt to find a focusable child view. If it fails
8901         * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
8902         * will be called to give the LayoutManager an opportunity to add new views for items
8903         * that did not have attached views representing them. The LayoutManager should not add
8904         * or remove views from this method.
8905         *
8906         * @param focused The currently focused view
8907         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
8908         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
8909         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
8910         * @return A descendant view to focus or null to fall back to default behavior.
8911         *         The default implementation returns null.
8912         */
8913        public View onInterceptFocusSearch(View focused, int direction) {
8914            return null;
8915        }
8916
8917        /**
8918         * Called when a child of the RecyclerView wants a particular rectangle to be positioned
8919         * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
8920         * android.graphics.Rect, boolean)} for more details.
8921         *
8922         * <p>The base implementation will attempt to perform a standard programmatic scroll
8923         * to bring the given rect into view, within the padded area of the RecyclerView.</p>
8924         *
8925         * @param child The direct child making the request.
8926         * @param rect  The rectangle in the child's coordinates the child
8927         *              wishes to be on the screen.
8928         * @param immediate True to forbid animated or delayed scrolling,
8929         *                  false otherwise
8930         * @return Whether the group scrolled to handle the operation
8931         */
8932        public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
8933                boolean immediate) {
8934            final int parentLeft = getPaddingLeft();
8935            final int parentTop = getPaddingTop();
8936            final int parentRight = getWidth() - getPaddingRight();
8937            final int parentBottom = getHeight() - getPaddingBottom();
8938            final int childLeft = child.getLeft() + rect.left - child.getScrollX();
8939            final int childTop = child.getTop() + rect.top - child.getScrollY();
8940            final int childRight = childLeft + rect.width();
8941            final int childBottom = childTop + rect.height();
8942
8943            final int offScreenLeft = Math.min(0, childLeft - parentLeft);
8944            final int offScreenTop = Math.min(0, childTop - parentTop);
8945            final int offScreenRight = Math.max(0, childRight - parentRight);
8946            final int offScreenBottom = Math.max(0, childBottom - parentBottom);
8947
8948            // Favor the "start" layout direction over the end when bringing one side or the other
8949            // of a large rect into view. If we decide to bring in end because start is already
8950            // visible, limit the scroll such that start won't go out of bounds.
8951            final int dx;
8952            if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
8953                dx = offScreenRight != 0 ? offScreenRight
8954                        : Math.max(offScreenLeft, childRight - parentRight);
8955            } else {
8956                dx = offScreenLeft != 0 ? offScreenLeft
8957                        : Math.min(childLeft - parentLeft, offScreenRight);
8958            }
8959
8960            // Favor bringing the top into view over the bottom. If top is already visible and
8961            // we should scroll to make bottom visible, make sure top does not go out of bounds.
8962            final int dy = offScreenTop != 0 ? offScreenTop
8963                    : Math.min(childTop - parentTop, offScreenBottom);
8964
8965            if (dx != 0 || dy != 0) {
8966                if (immediate) {
8967                    parent.scrollBy(dx, dy);
8968                } else {
8969                    parent.smoothScrollBy(dx, dy);
8970                }
8971                return true;
8972            }
8973            return false;
8974        }
8975
8976        /**
8977         * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
8978         */
8979        @Deprecated
8980        public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
8981            // eat the request if we are in the middle of a scroll or layout
8982            return isSmoothScrolling() || parent.isComputingLayout();
8983        }
8984
8985        /**
8986         * Called when a descendant view of the RecyclerView requests focus.
8987         *
8988         * <p>A LayoutManager wishing to keep focused views aligned in a specific
8989         * portion of the view may implement that behavior in an override of this method.</p>
8990         *
8991         * <p>If the LayoutManager executes different behavior that should override the default
8992         * behavior of scrolling the focused child on screen instead of running alongside it,
8993         * this method should return true.</p>
8994         *
8995         * @param parent  The RecyclerView hosting this LayoutManager
8996         * @param state   Current state of RecyclerView
8997         * @param child   Direct child of the RecyclerView containing the newly focused view
8998         * @param focused The newly focused view. This may be the same view as child or it may be
8999         *                null
9000         * @return true if the default scroll behavior should be suppressed
9001         */
9002        public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
9003                View focused) {
9004            return onRequestChildFocus(parent, child, focused);
9005        }
9006
9007        /**
9008         * Called if the RecyclerView this LayoutManager is bound to has a different adapter set.
9009         * The LayoutManager may use this opportunity to clear caches and configure state such
9010         * that it can relayout appropriately with the new data and potentially new view types.
9011         *
9012         * <p>The default implementation removes all currently attached views.</p>
9013         *
9014         * @param oldAdapter The previous adapter instance. Will be null if there was previously no
9015         *                   adapter.
9016         * @param newAdapter The new adapter instance. Might be null if
9017         *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
9018         */
9019        public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
9020        }
9021
9022        /**
9023         * Called to populate focusable views within the RecyclerView.
9024         *
9025         * <p>The LayoutManager implementation should return <code>true</code> if the default
9026         * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
9027         * suppressed.</p>
9028         *
9029         * <p>The default implementation returns <code>false</code> to trigger RecyclerView
9030         * to fall back to the default ViewGroup behavior.</p>
9031         *
9032         * @param recyclerView The RecyclerView hosting this LayoutManager
9033         * @param views List of output views. This method should add valid focusable views
9034         *              to this list.
9035         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9036         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9037         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9038         * @param focusableMode The type of focusables to be added.
9039         *
9040         * @return true to suppress the default behavior, false to add default focusables after
9041         *         this method returns.
9042         *
9043         * @see #FOCUSABLES_ALL
9044         * @see #FOCUSABLES_TOUCH_MODE
9045         */
9046        public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,
9047                int direction, int focusableMode) {
9048            return false;
9049        }
9050
9051        /**
9052         * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving
9053         * detailed information on what has actually changed.
9054         *
9055         * @param recyclerView
9056         */
9057        public void onItemsChanged(RecyclerView recyclerView) {
9058        }
9059
9060        /**
9061         * Called when items have been added to the adapter. The LayoutManager may choose to
9062         * requestLayout if the inserted items would require refreshing the currently visible set
9063         * of child views. (e.g. currently empty space would be filled by appended items, etc.)
9064         *
9065         * @param recyclerView
9066         * @param positionStart
9067         * @param itemCount
9068         */
9069        public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
9070        }
9071
9072        /**
9073         * Called when items have been removed from the adapter.
9074         *
9075         * @param recyclerView
9076         * @param positionStart
9077         * @param itemCount
9078         */
9079        public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
9080        }
9081
9082        /**
9083         * Called when items have been changed in the adapter.
9084         * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
9085         * instead, then this callback will not be invoked.
9086         *
9087         * @param recyclerView
9088         * @param positionStart
9089         * @param itemCount
9090         */
9091        public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
9092        }
9093
9094        /**
9095         * Called when items have been changed in the adapter and with optional payload.
9096         * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
9097         *
9098         * @param recyclerView
9099         * @param positionStart
9100         * @param itemCount
9101         * @param payload
9102         */
9103        public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
9104                Object payload) {
9105            onItemsUpdated(recyclerView, positionStart, itemCount);
9106        }
9107
9108        /**
9109         * Called when an item is moved withing the adapter.
9110         * <p>
9111         * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
9112         * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
9113         * is called.
9114         *
9115         * @param recyclerView
9116         * @param from
9117         * @param to
9118         * @param itemCount
9119         */
9120        public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
9121
9122        }
9123
9124
9125        /**
9126         * <p>Override this method if you want to support scroll bars.</p>
9127         *
9128         * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
9129         *
9130         * <p>Default implementation returns 0.</p>
9131         *
9132         * @param state Current state of RecyclerView
9133         * @return The horizontal extent of the scrollbar's thumb
9134         * @see RecyclerView#computeHorizontalScrollExtent()
9135         */
9136        public int computeHorizontalScrollExtent(State state) {
9137            return 0;
9138        }
9139
9140        /**
9141         * <p>Override this method if you want to support scroll bars.</p>
9142         *
9143         * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
9144         *
9145         * <p>Default implementation returns 0.</p>
9146         *
9147         * @param state Current State of RecyclerView where you can find total item count
9148         * @return The horizontal offset of the scrollbar's thumb
9149         * @see RecyclerView#computeHorizontalScrollOffset()
9150         */
9151        public int computeHorizontalScrollOffset(State state) {
9152            return 0;
9153        }
9154
9155        /**
9156         * <p>Override this method if you want to support scroll bars.</p>
9157         *
9158         * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
9159         *
9160         * <p>Default implementation returns 0.</p>
9161         *
9162         * @param state Current State of RecyclerView where you can find total item count
9163         * @return The total horizontal range represented by the vertical scrollbar
9164         * @see RecyclerView#computeHorizontalScrollRange()
9165         */
9166        public int computeHorizontalScrollRange(State state) {
9167            return 0;
9168        }
9169
9170        /**
9171         * <p>Override this method if you want to support scroll bars.</p>
9172         *
9173         * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
9174         *
9175         * <p>Default implementation returns 0.</p>
9176         *
9177         * @param state Current state of RecyclerView
9178         * @return The vertical extent of the scrollbar's thumb
9179         * @see RecyclerView#computeVerticalScrollExtent()
9180         */
9181        public int computeVerticalScrollExtent(State state) {
9182            return 0;
9183        }
9184
9185        /**
9186         * <p>Override this method if you want to support scroll bars.</p>
9187         *
9188         * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
9189         *
9190         * <p>Default implementation returns 0.</p>
9191         *
9192         * @param state Current State of RecyclerView where you can find total item count
9193         * @return The vertical offset of the scrollbar's thumb
9194         * @see RecyclerView#computeVerticalScrollOffset()
9195         */
9196        public int computeVerticalScrollOffset(State state) {
9197            return 0;
9198        }
9199
9200        /**
9201         * <p>Override this method if you want to support scroll bars.</p>
9202         *
9203         * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
9204         *
9205         * <p>Default implementation returns 0.</p>
9206         *
9207         * @param state Current State of RecyclerView where you can find total item count
9208         * @return The total vertical range represented by the vertical scrollbar
9209         * @see RecyclerView#computeVerticalScrollRange()
9210         */
9211        public int computeVerticalScrollRange(State state) {
9212            return 0;
9213        }
9214
9215        /**
9216         * Measure the attached RecyclerView. Implementations must call
9217         * {@link #setMeasuredDimension(int, int)} before returning.
9218         *
9219         * <p>The default implementation will handle EXACTLY measurements and respect
9220         * the minimum width and height properties of the host RecyclerView if measured
9221         * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
9222         * will consume all available space.</p>
9223         *
9224         * @param recycler Recycler
9225         * @param state Transient state of RecyclerView
9226         * @param widthSpec Width {@link android.view.View.MeasureSpec}
9227         * @param heightSpec Height {@link android.view.View.MeasureSpec}
9228         */
9229        public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
9230            mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
9231        }
9232
9233        /**
9234         * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
9235         * host RecyclerView.
9236         *
9237         * @param widthSize Measured width
9238         * @param heightSize Measured height
9239         */
9240        public void setMeasuredDimension(int widthSize, int heightSize) {
9241            mRecyclerView.setMeasuredDimension(widthSize, heightSize);
9242        }
9243
9244        /**
9245         * @return The host RecyclerView's {@link View#getMinimumWidth()}
9246         */
9247        public int getMinimumWidth() {
9248            return mRecyclerView.getMinimumWidth();
9249        }
9250
9251        /**
9252         * @return The host RecyclerView's {@link View#getMinimumHeight()}
9253         */
9254        public int getMinimumHeight() {
9255            return mRecyclerView.getMinimumHeight();
9256        }
9257        /**
9258         * <p>Called when the LayoutManager should save its state. This is a good time to save your
9259         * scroll position, configuration and anything else that may be required to restore the same
9260         * layout state if the LayoutManager is recreated.</p>
9261         * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
9262         * restore. This will let you share information between your LayoutManagers but it is also
9263         * your responsibility to make sure they use the same parcelable class.</p>
9264         *
9265         * @return Necessary information for LayoutManager to be able to restore its state
9266         */
9267        public Parcelable onSaveInstanceState() {
9268            return null;
9269        }
9270
9271
9272        public void onRestoreInstanceState(Parcelable state) {
9273
9274        }
9275
9276        void stopSmoothScroller() {
9277            if (mSmoothScroller != null) {
9278                mSmoothScroller.stop();
9279            }
9280        }
9281
9282        private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
9283            if (mSmoothScroller == smoothScroller) {
9284                mSmoothScroller = null;
9285            }
9286        }
9287
9288        /**
9289         * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
9290         *
9291         * @param state The new scroll state for RecyclerView
9292         */
9293        public void onScrollStateChanged(int state) {
9294        }
9295
9296        /**
9297         * Removes all views and recycles them using the given recycler.
9298         * <p>
9299         * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
9300         * <p>
9301         * If a View is marked as "ignored", it is not removed nor recycled.
9302         *
9303         * @param recycler Recycler to use to recycle children
9304         * @see #removeAndRecycleView(View, Recycler)
9305         * @see #removeAndRecycleViewAt(int, Recycler)
9306         * @see #ignoreView(View)
9307         */
9308        public void removeAndRecycleAllViews(Recycler recycler) {
9309            for (int i = getChildCount() - 1; i >= 0; i--) {
9310                final View view = getChildAt(i);
9311                if (!getChildViewHolderInt(view).shouldIgnore()) {
9312                    removeAndRecycleViewAt(i, recycler);
9313                }
9314            }
9315        }
9316
9317        // called by accessibility delegate
9318        void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
9319            onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
9320        }
9321
9322        /**
9323         * Called by the AccessibilityDelegate when the information about the current layout should
9324         * be populated.
9325         * <p>
9326         * Default implementation adds a {@link
9327         * android.view.accessibility.AccessibilityNodeInfo.CollectionInfo}.
9328         * <p>
9329         * You should override
9330         * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
9331         * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
9332         * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
9333         * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
9334         * more accurate accessibility information.
9335         *
9336         * @param recycler The Recycler that can be used to convert view positions into adapter
9337         *                 positions
9338         * @param state    The current state of RecyclerView
9339         * @param info     The info that should be filled by the LayoutManager
9340         * @see View#onInitializeAccessibilityNodeInfo(
9341         *android.view.accessibility.AccessibilityNodeInfo)
9342         * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9343         * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9344         * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
9345         * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9346         */
9347        public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
9348                AccessibilityNodeInfo info) {
9349            if (mRecyclerView.canScrollVertically(-1)
9350                    || mRecyclerView.canScrollHorizontally(-1)) {
9351                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
9352                info.setScrollable(true);
9353            }
9354            if (mRecyclerView.canScrollVertically(1)
9355                    || mRecyclerView.canScrollHorizontally(1)) {
9356                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
9357                info.setScrollable(true);
9358            }
9359            final AccessibilityNodeInfo.CollectionInfo collectionInfo =
9360                    AccessibilityNodeInfo.CollectionInfo
9361                            .obtain(getRowCountForAccessibility(recycler, state),
9362                                    getColumnCountForAccessibility(recycler, state),
9363                                    isLayoutHierarchical(recycler, state),
9364                                    getSelectionModeForAccessibility(recycler, state));
9365            info.setCollectionInfo(collectionInfo);
9366        }
9367
9368        // called by accessibility delegate
9369        public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
9370            onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
9371        }
9372
9373        /**
9374         * Called by the accessibility delegate to initialize an accessibility event.
9375         * <p>
9376         * Default implementation adds item count and scroll information to the event.
9377         *
9378         * @param recycler The Recycler that can be used to convert view positions into adapter
9379         *                 positions
9380         * @param state    The current state of RecyclerView
9381         * @param event    The event instance to initialize
9382         * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
9383         */
9384        public void onInitializeAccessibilityEvent(Recycler recycler, State state,
9385                AccessibilityEvent event) {
9386            if (mRecyclerView == null || event == null) {
9387                return;
9388            }
9389            event.setScrollable(mRecyclerView.canScrollVertically(1)
9390                    || mRecyclerView.canScrollVertically(-1)
9391                    || mRecyclerView.canScrollHorizontally(-1)
9392                    || mRecyclerView.canScrollHorizontally(1));
9393
9394            if (mRecyclerView.mAdapter != null) {
9395                event.setItemCount(mRecyclerView.mAdapter.getItemCount());
9396            }
9397        }
9398
9399        // called by accessibility delegate
9400        void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfo info) {
9401            final ViewHolder vh = getChildViewHolderInt(host);
9402            // avoid trying to create accessibility node info for removed children
9403            if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
9404                onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
9405                        mRecyclerView.mState, host, info);
9406            }
9407        }
9408
9409        /**
9410         * Called by the AccessibilityDelegate when the accessibility information for a specific
9411         * item should be populated.
9412         * <p>
9413         * Default implementation adds basic positioning information about the item.
9414         *
9415         * @param recycler The Recycler that can be used to convert view positions into adapter
9416         *                 positions
9417         * @param state    The current state of RecyclerView
9418         * @param host     The child for which accessibility node info should be populated
9419         * @param info     The info to fill out about the item
9420         * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
9421         * android.view.accessibility.AccessibilityNodeInfo)
9422         */
9423        public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
9424                View host, AccessibilityNodeInfo info) {
9425            int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
9426            int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
9427            final AccessibilityNodeInfo.CollectionItemInfo itemInfo =
9428                    AccessibilityNodeInfo.CollectionItemInfo.obtain(rowIndexGuess, 1,
9429                            columnIndexGuess, 1, false, false);
9430            info.setCollectionItemInfo(itemInfo);
9431        }
9432
9433        /**
9434         * A LayoutManager can call this method to force RecyclerView to run simple animations in
9435         * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
9436         * change).
9437         * <p>
9438         * Note that, calling this method will not guarantee that RecyclerView will run animations
9439         * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
9440         * not run any animations but will still clear this flag after the layout is complete.
9441         *
9442         */
9443        public void requestSimpleAnimationsInNextLayout() {
9444            mRequestedSimpleAnimations = true;
9445        }
9446
9447        /**
9448         * Returns the selection mode for accessibility. Should be
9449         * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE},
9450         * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_SINGLE} or
9451         * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_MULTIPLE}.
9452         * <p>
9453         * Default implementation returns
9454         * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE}.
9455         *
9456         * @param recycler The Recycler that can be used to convert view positions into adapter
9457         *                 positions
9458         * @param state    The current state of RecyclerView
9459         * @return Selection mode for accessibility. Default implementation returns
9460         * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE}.
9461         */
9462        public int getSelectionModeForAccessibility(Recycler recycler, State state) {
9463            return AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_NONE;
9464        }
9465
9466        /**
9467         * Returns the number of rows for accessibility.
9468         * <p>
9469         * Default implementation returns the number of items in the adapter if LayoutManager
9470         * supports vertical scrolling or 1 if LayoutManager does not support vertical
9471         * scrolling.
9472         *
9473         * @param recycler The Recycler that can be used to convert view positions into adapter
9474         *                 positions
9475         * @param state    The current state of RecyclerView
9476         * @return The number of rows in LayoutManager for accessibility.
9477         */
9478        public int getRowCountForAccessibility(Recycler recycler, State state) {
9479            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
9480                return 1;
9481            }
9482            return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
9483        }
9484
9485        /**
9486         * Returns the number of columns for accessibility.
9487         * <p>
9488         * Default implementation returns the number of items in the adapter if LayoutManager
9489         * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
9490         * scrolling.
9491         *
9492         * @param recycler The Recycler that can be used to convert view positions into adapter
9493         *                 positions
9494         * @param state    The current state of RecyclerView
9495         * @return The number of rows in LayoutManager for accessibility.
9496         */
9497        public int getColumnCountForAccessibility(Recycler recycler, State state) {
9498            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
9499                return 1;
9500            }
9501            return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
9502        }
9503
9504        /**
9505         * Returns whether layout is hierarchical or not to be used for accessibility.
9506         * <p>
9507         * Default implementation returns false.
9508         *
9509         * @param recycler The Recycler that can be used to convert view positions into adapter
9510         *                 positions
9511         * @param state    The current state of RecyclerView
9512         * @return True if layout is hierarchical.
9513         */
9514        public boolean isLayoutHierarchical(Recycler recycler, State state) {
9515            return false;
9516        }
9517
9518        // called by accessibility delegate
9519        boolean performAccessibilityAction(int action, Bundle args) {
9520            return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
9521                    action, args);
9522        }
9523
9524        /**
9525         * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
9526         *
9527         * @param recycler  The Recycler that can be used to convert view positions into adapter
9528         *                  positions
9529         * @param state     The current state of RecyclerView
9530         * @param action    The action to perform
9531         * @param args      Optional action arguments
9532         * @see View#performAccessibilityAction(int, android.os.Bundle)
9533         */
9534        public boolean performAccessibilityAction(Recycler recycler, State state, int action,
9535                Bundle args) {
9536            if (mRecyclerView == null) {
9537                return false;
9538            }
9539            int vScroll = 0, hScroll = 0;
9540            switch (action) {
9541                case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
9542                    if (mRecyclerView.canScrollVertically(-1)) {
9543                        vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
9544                    }
9545                    if (mRecyclerView.canScrollHorizontally(-1)) {
9546                        hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
9547                    }
9548                    break;
9549                case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
9550                    if (mRecyclerView.canScrollVertically(1)) {
9551                        vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
9552                    }
9553                    if (mRecyclerView.canScrollHorizontally(1)) {
9554                        hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
9555                    }
9556                    break;
9557            }
9558            if (vScroll == 0 && hScroll == 0) {
9559                return false;
9560            }
9561            mRecyclerView.scrollBy(hScroll, vScroll);
9562            return true;
9563        }
9564
9565        // called by accessibility delegate
9566        boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
9567            return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
9568                    view, action, args);
9569        }
9570
9571        /**
9572         * Called by AccessibilityDelegate when an accessibility action is requested on one of the
9573         * children of LayoutManager.
9574         * <p>
9575         * Default implementation does not do anything.
9576         *
9577         * @param recycler The Recycler that can be used to convert view positions into adapter
9578         *                 positions
9579         * @param state    The current state of RecyclerView
9580         * @param view     The child view on which the action is performed
9581         * @param action   The action to perform
9582         * @param args     Optional action arguments
9583         * @return true if action is handled
9584         * @see View#performAccessibilityAction(int, android.os.Bundle)
9585         */
9586        public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
9587                int action, Bundle args) {
9588            return false;
9589        }
9590
9591        /**
9592         * Parse the xml attributes to get the most common properties used by layout managers.
9593         *
9594         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
9595         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
9596         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
9597         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
9598         *
9599         * @return an object containing the properties as specified in the attrs.
9600         */
9601        public static Properties getProperties(Context context, AttributeSet attrs,
9602                int defStyleAttr, int defStyleRes) {
9603            Properties properties = new Properties();
9604            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
9605                    defStyleAttr, defStyleRes);
9606            properties.orientation = a.getInt(R.styleable.RecyclerView_orientation, VERTICAL);
9607            properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
9608            properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
9609            properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
9610            a.recycle();
9611            return properties;
9612        }
9613
9614        void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
9615            setMeasureSpecs(
9616                    MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
9617                    MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
9618            );
9619        }
9620
9621        /**
9622         * Internal API to allow LayoutManagers to be measured twice.
9623         * <p>
9624         * This is not public because LayoutManagers should be able to handle their layouts in one
9625         * pass but it is very convenient to make existing LayoutManagers support wrapping content
9626         * when both orientations are undefined.
9627         * <p>
9628         * This API will be removed after default LayoutManagers properly implement wrap content in
9629         * non-scroll orientation.
9630         */
9631        boolean shouldMeasureTwice() {
9632            return false;
9633        }
9634
9635        boolean hasFlexibleChildInBothOrientations() {
9636            final int childCount = getChildCount();
9637            for (int i = 0; i < childCount; i++) {
9638                final View child = getChildAt(i);
9639                final ViewGroup.LayoutParams lp = child.getLayoutParams();
9640                if (lp.width < 0 && lp.height < 0) {
9641                    return true;
9642                }
9643            }
9644            return false;
9645        }
9646
9647        /**
9648         * Some general properties that a LayoutManager may want to use.
9649         */
9650        public static class Properties {
9651            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation */
9652            public int orientation;
9653            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount */
9654            public int spanCount;
9655            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout */
9656            public boolean reverseLayout;
9657            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd */
9658            public boolean stackFromEnd;
9659        }
9660    }
9661
9662    /**
9663     * An ItemDecoration allows the application to add a special drawing and layout offset
9664     * to specific item views from the adapter's data set. This can be useful for drawing dividers
9665     * between items, highlights, visual grouping boundaries and more.
9666     *
9667     * <p>All ItemDecorations are drawn in the order they were added, before the item
9668     * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
9669     * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
9670     * RecyclerView.State)}.</p>
9671     */
9672    public abstract static class ItemDecoration {
9673        /**
9674         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
9675         * Any content drawn by this method will be drawn before the item views are drawn,
9676         * and will thus appear underneath the views.
9677         *
9678         * @param c Canvas to draw into
9679         * @param parent RecyclerView this ItemDecoration is drawing into
9680         * @param state The current state of RecyclerView
9681         */
9682        public void onDraw(Canvas c, RecyclerView parent, State state) {
9683            onDraw(c, parent);
9684        }
9685
9686        /**
9687         * @deprecated
9688         * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
9689         */
9690        @Deprecated
9691        public void onDraw(Canvas c, RecyclerView parent) {
9692        }
9693
9694        /**
9695         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
9696         * Any content drawn by this method will be drawn after the item views are drawn
9697         * and will thus appear over the views.
9698         *
9699         * @param c Canvas to draw into
9700         * @param parent RecyclerView this ItemDecoration is drawing into
9701         * @param state The current state of RecyclerView.
9702         */
9703        public void onDrawOver(Canvas c, RecyclerView parent, State state) {
9704            onDrawOver(c, parent);
9705        }
9706
9707        /**
9708         * @deprecated
9709         * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
9710         */
9711        @Deprecated
9712        public void onDrawOver(Canvas c, RecyclerView parent) {
9713        }
9714
9715
9716        /**
9717         * @deprecated
9718         * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
9719         */
9720        @Deprecated
9721        public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
9722            outRect.set(0, 0, 0, 0);
9723        }
9724
9725        /**
9726         * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
9727         * the number of pixels that the item view should be inset by, similar to padding or margin.
9728         * The default implementation sets the bounds of outRect to 0 and returns.
9729         *
9730         * <p>
9731         * If this ItemDecoration does not affect the positioning of item views, it should set
9732         * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
9733         * before returning.
9734         *
9735         * <p>
9736         * If you need to access Adapter for additional data, you can call
9737         * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
9738         * View.
9739         *
9740         * @param outRect Rect to receive the output.
9741         * @param view    The child view to decorate
9742         * @param parent  RecyclerView this ItemDecoration is decorating
9743         * @param state   The current state of RecyclerView.
9744         */
9745        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
9746            getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
9747                    parent);
9748        }
9749    }
9750
9751    /**
9752     * An OnItemTouchListener allows the application to intercept touch events in progress at the
9753     * view hierarchy level of the RecyclerView before those touch events are considered for
9754     * RecyclerView's own scrolling behavior.
9755     *
9756     * <p>This can be useful for applications that wish to implement various forms of gestural
9757     * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
9758     * a touch interaction already in progress even if the RecyclerView is already handling that
9759     * gesture stream itself for the purposes of scrolling.</p>
9760     *
9761     * @see SimpleOnItemTouchListener
9762     */
9763    public interface OnItemTouchListener {
9764        /**
9765         * Silently observe and/or take over touch events sent to the RecyclerView
9766         * before they are handled by either the RecyclerView itself or its child views.
9767         *
9768         * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
9769         * in the order in which each listener was added, before any other touch processing
9770         * by the RecyclerView itself or child views occurs.</p>
9771         *
9772         * @param e MotionEvent describing the touch event. All coordinates are in
9773         *          the RecyclerView's coordinate system.
9774         * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
9775         *         to continue with the current behavior and continue observing future events in
9776         *         the gesture.
9777         */
9778        boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
9779
9780        /**
9781         * Process a touch event as part of a gesture that was claimed by returning true from
9782         * a previous call to {@link #onInterceptTouchEvent}.
9783         *
9784         * @param e MotionEvent describing the touch event. All coordinates are in
9785         *          the RecyclerView's coordinate system.
9786         */
9787        void onTouchEvent(RecyclerView rv, MotionEvent e);
9788
9789        /**
9790         * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
9791         * intercept touch events with
9792         * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
9793         *
9794         * @param disallowIntercept True if the child does not want the parent to
9795         *            intercept touch events.
9796         * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
9797         */
9798        void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
9799    }
9800
9801    /**
9802     * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies
9803     * and default return values.
9804     * <p>
9805     * You may prefer to extend this class if you don't need to override all methods. Another
9806     * benefit of using this class is future compatibility. As the interface may change, we'll
9807     * always provide a default implementation on this class so that your code won't break when
9808     * you update to a new version of the support library.
9809     */
9810    public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
9811        @Override
9812        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
9813            return false;
9814        }
9815
9816        @Override
9817        public void onTouchEvent(RecyclerView rv, MotionEvent e) {
9818        }
9819
9820        @Override
9821        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
9822        }
9823    }
9824
9825
9826    /**
9827     * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
9828     * has occurred on that RecyclerView.
9829     * <p>
9830     * @see RecyclerView#addOnScrollListener(OnScrollListener)
9831     * @see RecyclerView#clearOnChildAttachStateChangeListeners()
9832     *
9833     */
9834    public abstract static class OnScrollListener {
9835        /**
9836         * Callback method to be invoked when RecyclerView's scroll state changes.
9837         *
9838         * @param recyclerView The RecyclerView whose scroll state has changed.
9839         * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
9840         *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
9841         */
9842        public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
9843
9844        /**
9845         * Callback method to be invoked when the RecyclerView has been scrolled. This will be
9846         * called after the scroll has completed.
9847         * <p>
9848         * This callback will also be called if visible item range changes after a layout
9849         * calculation. In that case, dx and dy will be 0.
9850         *
9851         * @param recyclerView The RecyclerView which scrolled.
9852         * @param dx The amount of horizontal scroll.
9853         * @param dy The amount of vertical scroll.
9854         */
9855        public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
9856    }
9857
9858    /**
9859     * A RecyclerListener can be set on a RecyclerView to receive messages whenever
9860     * a view is recycled.
9861     *
9862     * @see RecyclerView#setRecyclerListener(RecyclerListener)
9863     */
9864    public interface RecyclerListener {
9865
9866        /**
9867         * This method is called whenever the view in the ViewHolder is recycled.
9868         *
9869         * RecyclerView calls this method right before clearing ViewHolder's internal data and
9870         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
9871         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
9872         * its adapter position.
9873         *
9874         * @param holder The ViewHolder containing the view that was recycled
9875         */
9876        void onViewRecycled(ViewHolder holder);
9877    }
9878
9879    /**
9880     * A Listener interface that can be attached to a RecylcerView to get notified
9881     * whenever a ViewHolder is attached to or detached from RecyclerView.
9882     */
9883    public interface OnChildAttachStateChangeListener {
9884
9885        /**
9886         * Called when a view is attached to the RecyclerView.
9887         *
9888         * @param view The View which is attached to the RecyclerView
9889         */
9890        void onChildViewAttachedToWindow(View view);
9891
9892        /**
9893         * Called when a view is detached from RecyclerView.
9894         *
9895         * @param view The View which is being detached from the RecyclerView
9896         */
9897        void onChildViewDetachedFromWindow(View view);
9898    }
9899
9900    /**
9901     * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
9902     *
9903     * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
9904     * potentially expensive {@link View#findViewById(int)} results.</p>
9905     *
9906     * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
9907     * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
9908     * their own custom ViewHolder implementations to store data that makes binding view contents
9909     * easier. Implementations should assume that individual item views will hold strong references
9910     * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
9911     * strong references to extra off-screen item views for caching purposes</p>
9912     */
9913    public abstract static class ViewHolder {
9914        public final View itemView;
9915        WeakReference<RecyclerView> mNestedRecyclerView;
9916        int mPosition = NO_POSITION;
9917        int mOldPosition = NO_POSITION;
9918        long mItemId = NO_ID;
9919        int mItemViewType = INVALID_TYPE;
9920        int mPreLayoutPosition = NO_POSITION;
9921
9922        // The item that this holder is shadowing during an item change event/animation
9923        ViewHolder mShadowedHolder = null;
9924        // The item that is shadowing this holder during an item change event/animation
9925        ViewHolder mShadowingHolder = null;
9926
9927        /**
9928         * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
9929         * are all valid.
9930         */
9931        static final int FLAG_BOUND = 1 << 0;
9932
9933        /**
9934         * The data this ViewHolder's view reflects is stale and needs to be rebound
9935         * by the adapter. mPosition and mItemId are consistent.
9936         */
9937        static final int FLAG_UPDATE = 1 << 1;
9938
9939        /**
9940         * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
9941         * are not to be trusted and may no longer match the item view type.
9942         * This ViewHolder must be fully rebound to different data.
9943         */
9944        static final int FLAG_INVALID = 1 << 2;
9945
9946        /**
9947         * This ViewHolder points at data that represents an item previously removed from the
9948         * data set. Its view may still be used for things like outgoing animations.
9949         */
9950        static final int FLAG_REMOVED = 1 << 3;
9951
9952        /**
9953         * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
9954         * and is intended to keep views around during animations.
9955         */
9956        static final int FLAG_NOT_RECYCLABLE = 1 << 4;
9957
9958        /**
9959         * This ViewHolder is returned from scrap which means we are expecting an addView call
9960         * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
9961         * the end of the layout pass and then recycled by RecyclerView if it is not added back to
9962         * the RecyclerView.
9963         */
9964        static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
9965
9966        /**
9967         * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
9968         * it unless LayoutManager is replaced.
9969         * It is still fully visible to the LayoutManager.
9970         */
9971        static final int FLAG_IGNORE = 1 << 7;
9972
9973        /**
9974         * When the View is detached form the parent, we set this flag so that we can take correct
9975         * action when we need to remove it or add it back.
9976         */
9977        static final int FLAG_TMP_DETACHED = 1 << 8;
9978
9979        /**
9980         * Set when we can no longer determine the adapter position of this ViewHolder until it is
9981         * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
9982         * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
9983         * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
9984         * re-calculated.
9985         */
9986        static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
9987
9988        /**
9989         * Set when a addChangePayload(null) is called
9990         */
9991        static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
9992
9993        /**
9994         * Used by ItemAnimator when a ViewHolder's position changes
9995         */
9996        static final int FLAG_MOVED = 1 << 11;
9997
9998        /**
9999         * Used by ItemAnimator when a ViewHolder appears in pre-layout
10000         */
10001        static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
10002
10003        static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
10004
10005        /**
10006         * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
10007         * hidden list (as if it was scrap) without being recycled in between.
10008         *
10009         * When a ViewHolder is hidden, there are 2 paths it can be re-used:
10010         *   a) Animation ends, view is recycled and used from the recycle pool.
10011         *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
10012         *
10013         * This flag is used to represent "case b" where the ViewHolder is reused without being
10014         * recycled (thus "bounced" from the hidden list). This state requires special handling
10015         * because the ViewHolder must be added to pre layout maps for animations as if it was
10016         * already there.
10017         */
10018        static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
10019
10020        private int mFlags;
10021
10022        private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
10023
10024        List<Object> mPayloads = null;
10025        List<Object> mUnmodifiedPayloads = null;
10026
10027        private int mIsRecyclableCount = 0;
10028
10029        // If non-null, view is currently considered scrap and may be reused for other data by the
10030        // scrap container.
10031        private Recycler mScrapContainer = null;
10032        // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
10033        private boolean mInChangeScrap = false;
10034
10035        // Saves isImportantForAccessibility value for the view item while it's in hidden state and
10036        // marked as unimportant for accessibility.
10037        private int mWasImportantForAccessibilityBeforeHidden =
10038                View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10039        // set if we defer the accessibility state change of the view holder
10040        @VisibleForTesting
10041        int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
10042
10043        /**
10044         * Is set when VH is bound from the adapter and cleaned right before it is sent to
10045         * {@link RecycledViewPool}.
10046         */
10047        RecyclerView mOwnerRecyclerView;
10048
10049        public ViewHolder(View itemView) {
10050            if (itemView == null) {
10051                throw new IllegalArgumentException("itemView may not be null");
10052            }
10053            this.itemView = itemView;
10054        }
10055
10056        void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
10057            addFlags(ViewHolder.FLAG_REMOVED);
10058            offsetPosition(offset, applyToPreLayout);
10059            mPosition = mNewPosition;
10060        }
10061
10062        void offsetPosition(int offset, boolean applyToPreLayout) {
10063            if (mOldPosition == NO_POSITION) {
10064                mOldPosition = mPosition;
10065            }
10066            if (mPreLayoutPosition == NO_POSITION) {
10067                mPreLayoutPosition = mPosition;
10068            }
10069            if (applyToPreLayout) {
10070                mPreLayoutPosition += offset;
10071            }
10072            mPosition += offset;
10073            if (itemView.getLayoutParams() != null) {
10074                ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
10075            }
10076        }
10077
10078        void clearOldPosition() {
10079            mOldPosition = NO_POSITION;
10080            mPreLayoutPosition = NO_POSITION;
10081        }
10082
10083        void saveOldPosition() {
10084            if (mOldPosition == NO_POSITION) {
10085                mOldPosition = mPosition;
10086            }
10087        }
10088
10089        boolean shouldIgnore() {
10090            return (mFlags & FLAG_IGNORE) != 0;
10091        }
10092
10093        /**
10094         * @deprecated This method is deprecated because its meaning is ambiguous due to the async
10095         * handling of adapter updates. Please use {@link #getLayoutPosition()} or
10096         * {@link #getAdapterPosition()} depending on your use case.
10097         *
10098         * @see #getLayoutPosition()
10099         * @see #getAdapterPosition()
10100         */
10101        @Deprecated
10102        public final int getPosition() {
10103            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10104        }
10105
10106        /**
10107         * Returns the position of the ViewHolder in terms of the latest layout pass.
10108         * <p>
10109         * This position is mostly used by RecyclerView components to be consistent while
10110         * RecyclerView lazily processes adapter updates.
10111         * <p>
10112         * For performance and animation reasons, RecyclerView batches all adapter updates until the
10113         * next layout pass. This may cause mismatches between the Adapter position of the item and
10114         * the position it had in the latest layout calculations.
10115         * <p>
10116         * LayoutManagers should always call this method while doing calculations based on item
10117         * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
10118         * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
10119         * of the item.
10120         * <p>
10121         * If LayoutManager needs to call an external method that requires the adapter position of
10122         * the item, it can use {@link #getAdapterPosition()} or
10123         * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
10124         *
10125         * @return Returns the adapter position of the ViewHolder in the latest layout pass.
10126         * @see #getAdapterPosition()
10127         */
10128        public final int getLayoutPosition() {
10129            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10130        }
10131
10132        /**
10133         * Returns the Adapter position of the item represented by this ViewHolder.
10134         * <p>
10135         * Note that this might be different than the {@link #getLayoutPosition()} if there are
10136         * pending adapter updates but a new layout pass has not happened yet.
10137         * <p>
10138         * RecyclerView does not handle any adapter updates until the next layout traversal. This
10139         * may create temporary inconsistencies between what user sees on the screen and what
10140         * adapter contents have. This inconsistency is not important since it will be less than
10141         * 16ms but it might be a problem if you want to use ViewHolder position to access the
10142         * adapter. Sometimes, you may need to get the exact adapter position to do
10143         * some actions in response to user events. In that case, you should use this method which
10144         * will calculate the Adapter position of the ViewHolder.
10145         * <p>
10146         * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
10147         * next layout pass, the return value of this method will be {@link #NO_POSITION}.
10148         *
10149         * @return The adapter position of the item if it still exists in the adapter.
10150         * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
10151         * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
10152         * layout pass or the ViewHolder has already been recycled.
10153         */
10154        public final int getAdapterPosition() {
10155            if (mOwnerRecyclerView == null) {
10156                return NO_POSITION;
10157            }
10158            return mOwnerRecyclerView.getAdapterPositionFor(this);
10159        }
10160
10161        /**
10162         * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
10163         * to perform animations.
10164         * <p>
10165         * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
10166         * adapter index in the previous layout.
10167         *
10168         * @return The previous adapter index of the Item represented by this ViewHolder or
10169         * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
10170         * complete).
10171         */
10172        public final int getOldPosition() {
10173            return mOldPosition;
10174        }
10175
10176        /**
10177         * Returns The itemId represented by this ViewHolder.
10178         *
10179         * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
10180         * otherwise
10181         */
10182        public final long getItemId() {
10183            return mItemId;
10184        }
10185
10186        /**
10187         * @return The view type of this ViewHolder.
10188         */
10189        public final int getItemViewType() {
10190            return mItemViewType;
10191        }
10192
10193        boolean isScrap() {
10194            return mScrapContainer != null;
10195        }
10196
10197        void unScrap() {
10198            mScrapContainer.unscrapView(this);
10199        }
10200
10201        boolean wasReturnedFromScrap() {
10202            return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
10203        }
10204
10205        void clearReturnedFromScrapFlag() {
10206            mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
10207        }
10208
10209        void clearTmpDetachFlag() {
10210            mFlags = mFlags & ~FLAG_TMP_DETACHED;
10211        }
10212
10213        void stopIgnoring() {
10214            mFlags = mFlags & ~FLAG_IGNORE;
10215        }
10216
10217        void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
10218            mScrapContainer = recycler;
10219            mInChangeScrap = isChangeScrap;
10220        }
10221
10222        boolean isInvalid() {
10223            return (mFlags & FLAG_INVALID) != 0;
10224        }
10225
10226        boolean needsUpdate() {
10227            return (mFlags & FLAG_UPDATE) != 0;
10228        }
10229
10230        boolean isBound() {
10231            return (mFlags & FLAG_BOUND) != 0;
10232        }
10233
10234        boolean isRemoved() {
10235            return (mFlags & FLAG_REMOVED) != 0;
10236        }
10237
10238        boolean hasAnyOfTheFlags(int flags) {
10239            return (mFlags & flags) != 0;
10240        }
10241
10242        boolean isTmpDetached() {
10243            return (mFlags & FLAG_TMP_DETACHED) != 0;
10244        }
10245
10246        boolean isAdapterPositionUnknown() {
10247            return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
10248        }
10249
10250        void setFlags(int flags, int mask) {
10251            mFlags = (mFlags & ~mask) | (flags & mask);
10252        }
10253
10254        void addFlags(int flags) {
10255            mFlags |= flags;
10256        }
10257
10258        void addChangePayload(Object payload) {
10259            if (payload == null) {
10260                addFlags(FLAG_ADAPTER_FULLUPDATE);
10261            } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10262                createPayloadsIfNeeded();
10263                mPayloads.add(payload);
10264            }
10265        }
10266
10267        private void createPayloadsIfNeeded() {
10268            if (mPayloads == null) {
10269                mPayloads = new ArrayList<Object>();
10270                mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
10271            }
10272        }
10273
10274        void clearPayload() {
10275            if (mPayloads != null) {
10276                mPayloads.clear();
10277            }
10278            mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
10279        }
10280
10281        List<Object> getUnmodifiedPayloads() {
10282            if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10283                if (mPayloads == null || mPayloads.size() == 0) {
10284                    // Initial state,  no update being called.
10285                    return FULLUPDATE_PAYLOADS;
10286                }
10287                // there are none-null payloads
10288                return mUnmodifiedPayloads;
10289            } else {
10290                // a full update has been called.
10291                return FULLUPDATE_PAYLOADS;
10292            }
10293        }
10294
10295        void resetInternal() {
10296            mFlags = 0;
10297            mPosition = NO_POSITION;
10298            mOldPosition = NO_POSITION;
10299            mItemId = NO_ID;
10300            mPreLayoutPosition = NO_POSITION;
10301            mIsRecyclableCount = 0;
10302            mShadowedHolder = null;
10303            mShadowingHolder = null;
10304            clearPayload();
10305            mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10306            mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
10307            clearNestedRecyclerViewIfNotNested(this);
10308        }
10309
10310        /**
10311         * Called when the child view enters the hidden state
10312         */
10313        private void onEnteredHiddenState(RecyclerView parent) {
10314            // While the view item is in hidden state, make it invisible for the accessibility.
10315            mWasImportantForAccessibilityBeforeHidden =
10316                    itemView.getImportantForAccessibility();
10317            parent.setChildImportantForAccessibilityInternal(this,
10318                    View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
10319        }
10320
10321        /**
10322         * Called when the child view leaves the hidden state
10323         */
10324        private void onLeftHiddenState(RecyclerView parent) {
10325            parent.setChildImportantForAccessibilityInternal(this,
10326                    mWasImportantForAccessibilityBeforeHidden);
10327            mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10328        }
10329
10330        @Override
10331        public String toString() {
10332            final StringBuilder sb = new StringBuilder("ViewHolder{"
10333                    + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
10334                    + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
10335            if (isScrap()) {
10336                sb.append(" scrap ")
10337                        .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
10338            }
10339            if (isInvalid()) sb.append(" invalid");
10340            if (!isBound()) sb.append(" unbound");
10341            if (needsUpdate()) sb.append(" update");
10342            if (isRemoved()) sb.append(" removed");
10343            if (shouldIgnore()) sb.append(" ignored");
10344            if (isTmpDetached()) sb.append(" tmpDetached");
10345            if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
10346            if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
10347
10348            if (itemView.getParent() == null) sb.append(" no parent");
10349            sb.append("}");
10350            return sb.toString();
10351        }
10352
10353        /**
10354         * Informs the recycler whether this item can be recycled. Views which are not
10355         * recyclable will not be reused for other items until setIsRecyclable() is
10356         * later set to true. Calls to setIsRecyclable() should always be paired (one
10357         * call to setIsRecyclabe(false) should always be matched with a later call to
10358         * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
10359         * reference-counted.
10360         *
10361         * @param recyclable Whether this item is available to be recycled. Default value
10362         * is true.
10363         *
10364         * @see #isRecyclable()
10365         */
10366        public final void setIsRecyclable(boolean recyclable) {
10367            mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
10368            if (mIsRecyclableCount < 0) {
10369                mIsRecyclableCount = 0;
10370                if (DEBUG) {
10371                    throw new RuntimeException("isRecyclable decremented below 0: "
10372                            + "unmatched pair of setIsRecyable() calls for " + this);
10373                }
10374                Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: "
10375                        + "unmatched pair of setIsRecyable() calls for " + this);
10376            } else if (!recyclable && mIsRecyclableCount == 1) {
10377                mFlags |= FLAG_NOT_RECYCLABLE;
10378            } else if (recyclable && mIsRecyclableCount == 0) {
10379                mFlags &= ~FLAG_NOT_RECYCLABLE;
10380            }
10381            if (DEBUG) {
10382                Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
10383            }
10384        }
10385
10386        /**
10387         * @return true if this item is available to be recycled, false otherwise.
10388         *
10389         * @see #setIsRecyclable(boolean)
10390         */
10391        public final boolean isRecyclable() {
10392            return (mFlags & FLAG_NOT_RECYCLABLE) == 0
10393                    && !itemView.hasTransientState();
10394        }
10395
10396        /**
10397         * Returns whether we have animations referring to this view holder or not.
10398         * This is similar to isRecyclable flag but does not check transient state.
10399         */
10400        private boolean shouldBeKeptAsChild() {
10401            return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
10402        }
10403
10404        /**
10405         * @return True if ViewHolder is not referenced by RecyclerView animations but has
10406         * transient state which will prevent it from being recycled.
10407         */
10408        private boolean doesTransientStatePreventRecycling() {
10409            return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && itemView.hasTransientState();
10410        }
10411
10412        boolean isUpdated() {
10413            return (mFlags & FLAG_UPDATE) != 0;
10414        }
10415    }
10416
10417    /**
10418     * This method is here so that we can control the important for a11y changes and test it.
10419     */
10420    @VisibleForTesting
10421    boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
10422            int importantForAccessibility) {
10423        if (isComputingLayout()) {
10424            viewHolder.mPendingAccessibilityState = importantForAccessibility;
10425            mPendingAccessibilityImportanceChange.add(viewHolder);
10426            return false;
10427        }
10428        viewHolder.itemView.setImportantForAccessibility(importantForAccessibility);
10429        return true;
10430    }
10431
10432    void dispatchPendingImportantForAccessibilityChanges() {
10433        for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
10434            ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
10435            if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
10436                continue;
10437            }
10438            int state = viewHolder.mPendingAccessibilityState;
10439            if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
10440                //noinspection WrongConstant
10441                viewHolder.itemView.setImportantForAccessibility(state);
10442                viewHolder.mPendingAccessibilityState =
10443                        ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
10444            }
10445        }
10446        mPendingAccessibilityImportanceChange.clear();
10447    }
10448
10449    int getAdapterPositionFor(ViewHolder viewHolder) {
10450        if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
10451                | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
10452                || !viewHolder.isBound()) {
10453            return RecyclerView.NO_POSITION;
10454        }
10455        return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
10456    }
10457
10458    /**
10459     * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
10460     * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
10461     * to create their own subclass of this <code>LayoutParams</code> class
10462     * to store any additional required per-child view metadata about the layout.
10463     */
10464    public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
10465        ViewHolder mViewHolder;
10466        final Rect mDecorInsets = new Rect();
10467        boolean mInsetsDirty = true;
10468        // Flag is set to true if the view is bound while it is detached from RV.
10469        // In this case, we need to manually call invalidate after view is added to guarantee that
10470        // invalidation is populated through the View hierarchy
10471        boolean mPendingInvalidate = false;
10472
10473        public LayoutParams(Context c, AttributeSet attrs) {
10474            super(c, attrs);
10475        }
10476
10477        public LayoutParams(int width, int height) {
10478            super(width, height);
10479        }
10480
10481        public LayoutParams(MarginLayoutParams source) {
10482            super(source);
10483        }
10484
10485        public LayoutParams(ViewGroup.LayoutParams source) {
10486            super(source);
10487        }
10488
10489        public LayoutParams(LayoutParams source) {
10490            super((ViewGroup.LayoutParams) source);
10491        }
10492
10493        /**
10494         * Returns true if the view this LayoutParams is attached to needs to have its content
10495         * updated from the corresponding adapter.
10496         *
10497         * @return true if the view should have its content updated
10498         */
10499        public boolean viewNeedsUpdate() {
10500            return mViewHolder.needsUpdate();
10501        }
10502
10503        /**
10504         * Returns true if the view this LayoutParams is attached to is now representing
10505         * potentially invalid data. A LayoutManager should scrap/recycle it.
10506         *
10507         * @return true if the view is invalid
10508         */
10509        public boolean isViewInvalid() {
10510            return mViewHolder.isInvalid();
10511        }
10512
10513        /**
10514         * Returns true if the adapter data item corresponding to the view this LayoutParams
10515         * is attached to has been removed from the data set. A LayoutManager may choose to
10516         * treat it differently in order to animate its outgoing or disappearing state.
10517         *
10518         * @return true if the item the view corresponds to was removed from the data set
10519         */
10520        public boolean isItemRemoved() {
10521            return mViewHolder.isRemoved();
10522        }
10523
10524        /**
10525         * Returns true if the adapter data item corresponding to the view this LayoutParams
10526         * is attached to has been changed in the data set. A LayoutManager may choose to
10527         * treat it differently in order to animate its changing state.
10528         *
10529         * @return true if the item the view corresponds to was changed in the data set
10530         */
10531        public boolean isItemChanged() {
10532            return mViewHolder.isUpdated();
10533        }
10534
10535        /**
10536         * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
10537         */
10538        @Deprecated
10539        public int getViewPosition() {
10540            return mViewHolder.getPosition();
10541        }
10542
10543        /**
10544         * Returns the adapter position that the view this LayoutParams is attached to corresponds
10545         * to as of latest layout calculation.
10546         *
10547         * @return the adapter position this view as of latest layout pass
10548         */
10549        public int getViewLayoutPosition() {
10550            return mViewHolder.getLayoutPosition();
10551        }
10552
10553        /**
10554         * Returns the up-to-date adapter position that the view this LayoutParams is attached to
10555         * corresponds to.
10556         *
10557         * @return the up-to-date adapter position this view. It may return
10558         * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
10559         * its up-to-date position cannot be calculated.
10560         */
10561        public int getViewAdapterPosition() {
10562            return mViewHolder.getAdapterPosition();
10563        }
10564    }
10565
10566    /**
10567     * Observer base class for watching changes to an {@link Adapter}.
10568     * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
10569     */
10570    public abstract static class AdapterDataObserver {
10571        public void onChanged() {
10572            // Do nothing
10573        }
10574
10575        public void onItemRangeChanged(int positionStart, int itemCount) {
10576            // do nothing
10577        }
10578
10579        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
10580            // fallback to onItemRangeChanged(positionStart, itemCount) if app
10581            // does not override this method.
10582            onItemRangeChanged(positionStart, itemCount);
10583        }
10584
10585        public void onItemRangeInserted(int positionStart, int itemCount) {
10586            // do nothing
10587        }
10588
10589        public void onItemRangeRemoved(int positionStart, int itemCount) {
10590            // do nothing
10591        }
10592
10593        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
10594            // do nothing
10595        }
10596    }
10597
10598    /**
10599     * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
10600     * provides methods to trigger a programmatic scroll.</p>
10601     *
10602     * @see LinearSmoothScroller
10603     */
10604    public abstract static class SmoothScroller {
10605
10606        private int mTargetPosition = RecyclerView.NO_POSITION;
10607
10608        private RecyclerView mRecyclerView;
10609
10610        private LayoutManager mLayoutManager;
10611
10612        private boolean mPendingInitialRun;
10613
10614        private boolean mRunning;
10615
10616        private View mTargetView;
10617
10618        private final Action mRecyclingAction;
10619
10620        public SmoothScroller() {
10621            mRecyclingAction = new Action(0, 0);
10622        }
10623
10624        /**
10625         * Starts a smooth scroll for the given target position.
10626         * <p>In each animation step, {@link RecyclerView} will check
10627         * for the target view and call either
10628         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
10629         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
10630         * SmoothScroller is stopped.</p>
10631         *
10632         * <p>Note that if RecyclerView finds the target view, it will automatically stop the
10633         * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
10634         * stop calling SmoothScroller in each animation step.</p>
10635         */
10636        void start(RecyclerView recyclerView, LayoutManager layoutManager) {
10637            mRecyclerView = recyclerView;
10638            mLayoutManager = layoutManager;
10639            if (mTargetPosition == RecyclerView.NO_POSITION) {
10640                throw new IllegalArgumentException("Invalid target position");
10641            }
10642            mRecyclerView.mState.mTargetPosition = mTargetPosition;
10643            mRunning = true;
10644            mPendingInitialRun = true;
10645            mTargetView = findViewByPosition(getTargetPosition());
10646            onStart();
10647            mRecyclerView.mViewFlinger.postOnAnimation();
10648        }
10649
10650        public void setTargetPosition(int targetPosition) {
10651            mTargetPosition = targetPosition;
10652        }
10653
10654        /**
10655         * @return The LayoutManager to which this SmoothScroller is attached. Will return
10656         * <code>null</code> after the SmoothScroller is stopped.
10657         */
10658        @Nullable
10659        public LayoutManager getLayoutManager() {
10660            return mLayoutManager;
10661        }
10662
10663        /**
10664         * Stops running the SmoothScroller in each animation callback. Note that this does not
10665         * cancel any existing {@link Action} updated by
10666         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
10667         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
10668         */
10669        protected final void stop() {
10670            if (!mRunning) {
10671                return;
10672            }
10673            onStop();
10674            mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
10675            mTargetView = null;
10676            mTargetPosition = RecyclerView.NO_POSITION;
10677            mPendingInitialRun = false;
10678            mRunning = false;
10679            // trigger a cleanup
10680            mLayoutManager.onSmoothScrollerStopped(this);
10681            // clear references to avoid any potential leak by a custom smooth scroller
10682            mLayoutManager = null;
10683            mRecyclerView = null;
10684        }
10685
10686        /**
10687         * Returns true if SmoothScroller has been started but has not received the first
10688         * animation
10689         * callback yet.
10690         *
10691         * @return True if this SmoothScroller is waiting to start
10692         */
10693        public boolean isPendingInitialRun() {
10694            return mPendingInitialRun;
10695        }
10696
10697
10698        /**
10699         * @return True if SmoothScroller is currently active
10700         */
10701        public boolean isRunning() {
10702            return mRunning;
10703        }
10704
10705        /**
10706         * Returns the adapter position of the target item
10707         *
10708         * @return Adapter position of the target item or
10709         * {@link RecyclerView#NO_POSITION} if no target view is set.
10710         */
10711        public int getTargetPosition() {
10712            return mTargetPosition;
10713        }
10714
10715        private void onAnimation(int dx, int dy) {
10716            final RecyclerView recyclerView = mRecyclerView;
10717            if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
10718                stop();
10719            }
10720            mPendingInitialRun = false;
10721            if (mTargetView != null) {
10722                // verify target position
10723                if (getChildPosition(mTargetView) == mTargetPosition) {
10724                    onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
10725                    mRecyclingAction.runIfNecessary(recyclerView);
10726                    stop();
10727                } else {
10728                    Log.e(TAG, "Passed over target position while smooth scrolling.");
10729                    mTargetView = null;
10730                }
10731            }
10732            if (mRunning) {
10733                onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
10734                boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
10735                mRecyclingAction.runIfNecessary(recyclerView);
10736                if (hadJumpTarget) {
10737                    // It is not stopped so needs to be restarted
10738                    if (mRunning) {
10739                        mPendingInitialRun = true;
10740                        recyclerView.mViewFlinger.postOnAnimation();
10741                    } else {
10742                        stop(); // done
10743                    }
10744                }
10745            }
10746        }
10747
10748        /**
10749         * @see RecyclerView#getChildLayoutPosition(android.view.View)
10750         */
10751        public int getChildPosition(View view) {
10752            return mRecyclerView.getChildLayoutPosition(view);
10753        }
10754
10755        /**
10756         * @see RecyclerView.LayoutManager#getChildCount()
10757         */
10758        public int getChildCount() {
10759            return mRecyclerView.mLayout.getChildCount();
10760        }
10761
10762        /**
10763         * @see RecyclerView.LayoutManager#findViewByPosition(int)
10764         */
10765        public View findViewByPosition(int position) {
10766            return mRecyclerView.mLayout.findViewByPosition(position);
10767        }
10768
10769        /**
10770         * @see RecyclerView#scrollToPosition(int)
10771         * @deprecated Use {@link Action#jumpTo(int)}.
10772         */
10773        @Deprecated
10774        public void instantScrollToPosition(int position) {
10775            mRecyclerView.scrollToPosition(position);
10776        }
10777
10778        protected void onChildAttachedToWindow(View child) {
10779            if (getChildPosition(child) == getTargetPosition()) {
10780                mTargetView = child;
10781                if (DEBUG) {
10782                    Log.d(TAG, "smooth scroll target view has been attached");
10783                }
10784            }
10785        }
10786
10787        /**
10788         * Normalizes the vector.
10789         * @param scrollVector The vector that points to the target scroll position
10790         */
10791        protected void normalize(PointF scrollVector) {
10792            final double magnitude = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y
10793                    * scrollVector.y);
10794            scrollVector.x /= magnitude;
10795            scrollVector.y /= magnitude;
10796        }
10797
10798        /**
10799         * Called when smooth scroll is started. This might be a good time to do setup.
10800         */
10801        protected abstract void onStart();
10802
10803        /**
10804         * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
10805         * @see #stop()
10806         */
10807        protected abstract void onStop();
10808
10809        /**
10810         * <p>RecyclerView will call this method each time it scrolls until it can find the target
10811         * position in the layout.</p>
10812         * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
10813         * provided {@link Action} to define the next scroll.</p>
10814         *
10815         * @param dx        Last scroll amount horizontally
10816         * @param dy        Last scroll amount vertically
10817         * @param state     Transient state of RecyclerView
10818         * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
10819         *                  update this object.
10820         */
10821        protected abstract void onSeekTargetStep(int dx, int dy, State state, Action action);
10822
10823        /**
10824         * Called when the target position is laid out. This is the last callback SmoothScroller
10825         * will receive and it should update the provided {@link Action} to define the scroll
10826         * details towards the target view.
10827         * @param targetView    The view element which render the target position.
10828         * @param state         Transient state of RecyclerView
10829         * @param action        Action instance that you should update to define final scroll action
10830         *                      towards the targetView
10831         */
10832        protected abstract void onTargetFound(View targetView, State state, Action action);
10833
10834        /**
10835         * Holds information about a smooth scroll request by a {@link SmoothScroller}.
10836         */
10837        public static class Action {
10838
10839            public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
10840
10841            private int mDx;
10842
10843            private int mDy;
10844
10845            private int mDuration;
10846
10847            private int mJumpToPosition = NO_POSITION;
10848
10849            private Interpolator mInterpolator;
10850
10851            private boolean mChanged = false;
10852
10853            // we track this variable to inform custom implementer if they are updating the action
10854            // in every animation callback
10855            private int mConsecutiveUpdates = 0;
10856
10857            /**
10858             * @param dx Pixels to scroll horizontally
10859             * @param dy Pixels to scroll vertically
10860             */
10861            public Action(int dx, int dy) {
10862                this(dx, dy, UNDEFINED_DURATION, null);
10863            }
10864
10865            /**
10866             * @param dx       Pixels to scroll horizontally
10867             * @param dy       Pixels to scroll vertically
10868             * @param duration Duration of the animation in milliseconds
10869             */
10870            public Action(int dx, int dy, int duration) {
10871                this(dx, dy, duration, null);
10872            }
10873
10874            /**
10875             * @param dx           Pixels to scroll horizontally
10876             * @param dy           Pixels to scroll vertically
10877             * @param duration     Duration of the animation in milliseconds
10878             * @param interpolator Interpolator to be used when calculating scroll position in each
10879             *                     animation step
10880             */
10881            public Action(int dx, int dy, int duration, Interpolator interpolator) {
10882                mDx = dx;
10883                mDy = dy;
10884                mDuration = duration;
10885                mInterpolator = interpolator;
10886            }
10887
10888            /**
10889             * Instead of specifying pixels to scroll, use the target position to jump using
10890             * {@link RecyclerView#scrollToPosition(int)}.
10891             * <p>
10892             * You may prefer using this method if scroll target is really far away and you prefer
10893             * to jump to a location and smooth scroll afterwards.
10894             * <p>
10895             * Note that calling this method takes priority over other update methods such as
10896             * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
10897             * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
10898             * {@link #jumpTo(int)}, the other changes will not be considered for this animation
10899             * frame.
10900             *
10901             * @param targetPosition The target item position to scroll to using instant scrolling.
10902             */
10903            public void jumpTo(int targetPosition) {
10904                mJumpToPosition = targetPosition;
10905            }
10906
10907            boolean hasJumpTarget() {
10908                return mJumpToPosition >= 0;
10909            }
10910
10911            void runIfNecessary(RecyclerView recyclerView) {
10912                if (mJumpToPosition >= 0) {
10913                    final int position = mJumpToPosition;
10914                    mJumpToPosition = NO_POSITION;
10915                    recyclerView.jumpToPositionForSmoothScroller(position);
10916                    mChanged = false;
10917                    return;
10918                }
10919                if (mChanged) {
10920                    validate();
10921                    if (mInterpolator == null) {
10922                        if (mDuration == UNDEFINED_DURATION) {
10923                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
10924                        } else {
10925                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
10926                        }
10927                    } else {
10928                        recyclerView.mViewFlinger.smoothScrollBy(
10929                                mDx, mDy, mDuration, mInterpolator);
10930                    }
10931                    mConsecutiveUpdates++;
10932                    if (mConsecutiveUpdates > 10) {
10933                        // A new action is being set in every animation step. This looks like a bad
10934                        // implementation. Inform developer.
10935                        Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
10936                                + " you are not changing it unless necessary");
10937                    }
10938                    mChanged = false;
10939                } else {
10940                    mConsecutiveUpdates = 0;
10941                }
10942            }
10943
10944            private void validate() {
10945                if (mInterpolator != null && mDuration < 1) {
10946                    throw new IllegalStateException("If you provide an interpolator, you must"
10947                            + " set a positive duration");
10948                } else if (mDuration < 1) {
10949                    throw new IllegalStateException("Scroll duration must be a positive number");
10950                }
10951            }
10952
10953            public int getDx() {
10954                return mDx;
10955            }
10956
10957            public void setDx(int dx) {
10958                mChanged = true;
10959                mDx = dx;
10960            }
10961
10962            public int getDy() {
10963                return mDy;
10964            }
10965
10966            public void setDy(int dy) {
10967                mChanged = true;
10968                mDy = dy;
10969            }
10970
10971            public int getDuration() {
10972                return mDuration;
10973            }
10974
10975            public void setDuration(int duration) {
10976                mChanged = true;
10977                mDuration = duration;
10978            }
10979
10980            public Interpolator getInterpolator() {
10981                return mInterpolator;
10982            }
10983
10984            /**
10985             * Sets the interpolator to calculate scroll steps
10986             * @param interpolator The interpolator to use. If you specify an interpolator, you must
10987             *                     also set the duration.
10988             * @see #setDuration(int)
10989             */
10990            public void setInterpolator(Interpolator interpolator) {
10991                mChanged = true;
10992                mInterpolator = interpolator;
10993            }
10994
10995            /**
10996             * Updates the action with given parameters.
10997             * @param dx Pixels to scroll horizontally
10998             * @param dy Pixels to scroll vertically
10999             * @param duration Duration of the animation in milliseconds
11000             * @param interpolator Interpolator to be used when calculating scroll position in each
11001             *                     animation step
11002             */
11003            public void update(int dx, int dy, int duration, Interpolator interpolator) {
11004                mDx = dx;
11005                mDy = dy;
11006                mDuration = duration;
11007                mInterpolator = interpolator;
11008                mChanged = true;
11009            }
11010        }
11011
11012        /**
11013         * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
11014         * to provide a hint to a {@link SmoothScroller} about the location of the target position.
11015         */
11016        public interface ScrollVectorProvider {
11017            /**
11018             * Should calculate the vector that points to the direction where the target position
11019             * can be found.
11020             * <p>
11021             * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
11022             * the target position.
11023             * <p>
11024             * The magnitude of the vector is not important. It is always normalized before being
11025             * used by the {@link LinearSmoothScroller}.
11026             * <p>
11027             * LayoutManager should not check whether the position exists in the adapter or not.
11028             *
11029             * @param targetPosition the target position to which the returned vector should point
11030             *
11031             * @return the scroll vector for a given position.
11032             */
11033            PointF computeScrollVectorForPosition(int targetPosition);
11034        }
11035    }
11036
11037    static class AdapterDataObservable extends Observable<AdapterDataObserver> {
11038        public boolean hasObservers() {
11039            return !mObservers.isEmpty();
11040        }
11041
11042        public void notifyChanged() {
11043            // since onChanged() is implemented by the app, it could do anything, including
11044            // removing itself from {@link mObservers} - and that could cause problems if
11045            // an iterator is used on the ArrayList {@link mObservers}.
11046            // to avoid such problems, just march thru the list in the reverse order.
11047            for (int i = mObservers.size() - 1; i >= 0; i--) {
11048                mObservers.get(i).onChanged();
11049            }
11050        }
11051
11052        public void notifyItemRangeChanged(int positionStart, int itemCount) {
11053            notifyItemRangeChanged(positionStart, itemCount, null);
11054        }
11055
11056        public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
11057            // since onItemRangeChanged() is implemented by the app, it could do anything, including
11058            // removing itself from {@link mObservers} - and that could cause problems if
11059            // an iterator is used on the ArrayList {@link mObservers}.
11060            // to avoid such problems, just march thru the list in the reverse order.
11061            for (int i = mObservers.size() - 1; i >= 0; i--) {
11062                mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
11063            }
11064        }
11065
11066        public void notifyItemRangeInserted(int positionStart, int itemCount) {
11067            // since onItemRangeInserted() is implemented by the app, it could do anything,
11068            // including removing itself from {@link mObservers} - and that could cause problems if
11069            // an iterator is used on the ArrayList {@link mObservers}.
11070            // to avoid such problems, just march thru the list in the reverse order.
11071            for (int i = mObservers.size() - 1; i >= 0; i--) {
11072                mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
11073            }
11074        }
11075
11076        public void notifyItemRangeRemoved(int positionStart, int itemCount) {
11077            // since onItemRangeRemoved() is implemented by the app, it could do anything, including
11078            // removing itself from {@link mObservers} - and that could cause problems if
11079            // an iterator is used on the ArrayList {@link mObservers}.
11080            // to avoid such problems, just march thru the list in the reverse order.
11081            for (int i = mObservers.size() - 1; i >= 0; i--) {
11082                mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
11083            }
11084        }
11085
11086        public void notifyItemMoved(int fromPosition, int toPosition) {
11087            for (int i = mObservers.size() - 1; i >= 0; i--) {
11088                mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
11089            }
11090        }
11091    }
11092
11093    /**
11094     * This is public so that the CREATOR can be access on cold launch.
11095     * @hide
11096     */
11097    public static class SavedState extends AbsSavedState {
11098
11099        Parcelable mLayoutState;
11100
11101        /**
11102         * called by CREATOR
11103         */
11104        SavedState(Parcel in) {
11105            super(in);
11106            mLayoutState = in.readParcelable(LayoutManager.class.getClassLoader());
11107        }
11108
11109        /**
11110         * Called by onSaveInstanceState
11111         */
11112        SavedState(Parcelable superState) {
11113            super(superState);
11114        }
11115
11116        @Override
11117        public void writeToParcel(Parcel dest, int flags) {
11118            super.writeToParcel(dest, flags);
11119            dest.writeParcelable(mLayoutState, 0);
11120        }
11121
11122        void copyFrom(SavedState other) {
11123            mLayoutState = other.mLayoutState;
11124        }
11125
11126        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
11127                    @Override
11128                    public SavedState createFromParcel(Parcel in) {
11129                        return new SavedState(in);
11130                    }
11131
11132                    @Override
11133                    public SavedState[] newArray(int size) {
11134                        return new SavedState[size];
11135                    }
11136                };
11137    }
11138    /**
11139     * <p>Contains useful information about the current RecyclerView state like target scroll
11140     * position or view focus. State object can also keep arbitrary data, identified by resource
11141     * ids.</p>
11142     * <p>Often times, RecyclerView components will need to pass information between each other.
11143     * To provide a well defined data bus between components, RecyclerView passes the same State
11144     * object to component callbacks and these components can use it to exchange data.</p>
11145     * <p>If you implement custom components, you can use State's put/get/remove methods to pass
11146     * data between your components without needing to manage their lifecycles.</p>
11147     */
11148    public static class State {
11149        static final int STEP_START = 1;
11150        static final int STEP_LAYOUT = 1 << 1;
11151        static final int STEP_ANIMATIONS = 1 << 2;
11152
11153        void assertLayoutStep(int accepted) {
11154            if ((accepted & mLayoutStep) == 0) {
11155                throw new IllegalStateException("Layout state should be one of "
11156                        + Integer.toBinaryString(accepted) + " but it is "
11157                        + Integer.toBinaryString(mLayoutStep));
11158            }
11159        }
11160
11161
11162        /** Owned by SmoothScroller */
11163        private int mTargetPosition = RecyclerView.NO_POSITION;
11164
11165        private SparseArray<Object> mData;
11166
11167        ////////////////////////////////////////////////////////////////////////////////////////////
11168        // Fields below are carried from one layout pass to the next
11169        ////////////////////////////////////////////////////////////////////////////////////////////
11170
11171        /**
11172         * Number of items adapter had in the previous layout.
11173         */
11174        int mPreviousLayoutItemCount = 0;
11175
11176        /**
11177         * Number of items that were NOT laid out but has been deleted from the adapter after the
11178         * previous layout.
11179         */
11180        int mDeletedInvisibleItemCountSincePreviousLayout = 0;
11181
11182        ////////////////////////////////////////////////////////////////////////////////////////////
11183        // Fields below must be updated or cleared before they are used (generally before a pass)
11184        ////////////////////////////////////////////////////////////////////////////////////////////
11185
11186        @IntDef(flag = true, value = {
11187                STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
11188        })
11189        @Retention(RetentionPolicy.SOURCE)
11190        @interface LayoutState {}
11191
11192        @LayoutState
11193        int mLayoutStep = STEP_START;
11194
11195        /**
11196         * Number of items adapter has.
11197         */
11198        int mItemCount = 0;
11199
11200        boolean mStructureChanged = false;
11201
11202        boolean mInPreLayout = false;
11203
11204        boolean mTrackOldChangeHolders = false;
11205
11206        boolean mIsMeasuring = false;
11207
11208        ////////////////////////////////////////////////////////////////////////////////////////////
11209        // Fields below are always reset outside of the pass (or passes) that use them
11210        ////////////////////////////////////////////////////////////////////////////////////////////
11211
11212        boolean mRunSimpleAnimations = false;
11213
11214        boolean mRunPredictiveAnimations = false;
11215
11216        /**
11217         * This data is saved before a layout calculation happens. After the layout is finished,
11218         * if the previously focused view has been replaced with another view for the same item, we
11219         * move the focus to the new item automatically.
11220         */
11221        int mFocusedItemPosition;
11222        long mFocusedItemId;
11223        // when a sub child has focus, record its id and see if we can directly request focus on
11224        // that one instead
11225        int mFocusedSubChildId;
11226
11227        ////////////////////////////////////////////////////////////////////////////////////////////
11228
11229        State reset() {
11230            mTargetPosition = RecyclerView.NO_POSITION;
11231            if (mData != null) {
11232                mData.clear();
11233            }
11234            mItemCount = 0;
11235            mStructureChanged = false;
11236            mIsMeasuring = false;
11237            return this;
11238        }
11239
11240        /**
11241         * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
11242         * prior to any layout passes.
11243         *
11244         * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
11245         * that Recycler#getViewForPosition() can function safely.</p>
11246         */
11247        void prepareForNestedPrefetch(Adapter adapter) {
11248            mLayoutStep = STEP_START;
11249            mItemCount = adapter.getItemCount();
11250            mStructureChanged = false;
11251            mInPreLayout = false;
11252            mTrackOldChangeHolders = false;
11253            mIsMeasuring = false;
11254        }
11255
11256        /**
11257         * Returns true if the RecyclerView is currently measuring the layout. This value is
11258         * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
11259         * has non-exact measurement specs.
11260         * <p>
11261         * Note that if the LayoutManager supports predictive animations and it is calculating the
11262         * pre-layout step, this value will be {@code false} even if the RecyclerView is in
11263         * {@code onMeasure} call. This is because pre-layout means the previous state of the
11264         * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
11265         * LayoutManager is always guaranteed to receive another call to
11266         * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
11267         *
11268         * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
11269         */
11270        public boolean isMeasuring() {
11271            return mIsMeasuring;
11272        }
11273
11274        /**
11275         * Returns true if
11276         * @return
11277         */
11278        public boolean isPreLayout() {
11279            return mInPreLayout;
11280        }
11281
11282        /**
11283         * Returns whether RecyclerView will run predictive animations in this layout pass
11284         * or not.
11285         *
11286         * @return true if RecyclerView is calculating predictive animations to be run at the end
11287         *         of the layout pass.
11288         */
11289        public boolean willRunPredictiveAnimations() {
11290            return mRunPredictiveAnimations;
11291        }
11292
11293        /**
11294         * Returns whether RecyclerView will run simple animations in this layout pass
11295         * or not.
11296         *
11297         * @return true if RecyclerView is calculating simple animations to be run at the end of
11298         *         the layout pass.
11299         */
11300        public boolean willRunSimpleAnimations() {
11301            return mRunSimpleAnimations;
11302        }
11303
11304        /**
11305         * Removes the mapping from the specified id, if there was any.
11306         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
11307         *                   preserve cross functionality and avoid conflicts.
11308         */
11309        public void remove(int resourceId) {
11310            if (mData == null) {
11311                return;
11312            }
11313            mData.remove(resourceId);
11314        }
11315
11316        /**
11317         * Gets the Object mapped from the specified id, or <code>null</code>
11318         * if no such data exists.
11319         *
11320         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
11321         *                   to
11322         *                   preserve cross functionality and avoid conflicts.
11323         */
11324        public <T> T get(int resourceId) {
11325            if (mData == null) {
11326                return null;
11327            }
11328            return (T) mData.get(resourceId);
11329        }
11330
11331        /**
11332         * Adds a mapping from the specified id to the specified value, replacing the previous
11333         * mapping from the specified key if there was one.
11334         *
11335         * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
11336         *                   preserve cross functionality and avoid conflicts.
11337         * @param data       The data you want to associate with the resourceId.
11338         */
11339        public void put(int resourceId, Object data) {
11340            if (mData == null) {
11341                mData = new SparseArray<Object>();
11342            }
11343            mData.put(resourceId, data);
11344        }
11345
11346        /**
11347         * If scroll is triggered to make a certain item visible, this value will return the
11348         * adapter index of that item.
11349         * @return Adapter index of the target item or
11350         * {@link RecyclerView#NO_POSITION} if there is no target
11351         * position.
11352         */
11353        public int getTargetScrollPosition() {
11354            return mTargetPosition;
11355        }
11356
11357        /**
11358         * Returns if current scroll has a target position.
11359         * @return true if scroll is being triggered to make a certain position visible
11360         * @see #getTargetScrollPosition()
11361         */
11362        public boolean hasTargetScrollPosition() {
11363            return mTargetPosition != RecyclerView.NO_POSITION;
11364        }
11365
11366        /**
11367         * @return true if the structure of the data set has changed since the last call to
11368         *         onLayoutChildren, false otherwise
11369         */
11370        public boolean didStructureChange() {
11371            return mStructureChanged;
11372        }
11373
11374        /**
11375         * Returns the total number of items that can be laid out. Note that this number is not
11376         * necessarily equal to the number of items in the adapter, so you should always use this
11377         * number for your position calculations and never access the adapter directly.
11378         * <p>
11379         * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
11380         * data changes on existing Views. These calculations are used to decide which animations
11381         * should be run.
11382         * <p>
11383         * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
11384         * present the correct state to LayoutManager in pre-layout pass.
11385         * <p>
11386         * For example, a newly added item is not included in pre-layout item count because
11387         * pre-layout reflects the contents of the adapter before the item is added. Behind the
11388         * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
11389         * LayoutManager does not know about the new item's existence in pre-layout. The item will
11390         * be available in second layout pass and will be included in the item count. Similar
11391         * adjustments are made for moved and removed items as well.
11392         * <p>
11393         * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
11394         *
11395         * @return The number of items currently available
11396         * @see LayoutManager#getItemCount()
11397         */
11398        public int getItemCount() {
11399            return mInPreLayout
11400                    ? (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout)
11401                    : mItemCount;
11402        }
11403
11404        @Override
11405        public String toString() {
11406            return "State{"
11407                    + "mTargetPosition=" + mTargetPosition
11408                    + ", mData=" + mData
11409                    + ", mItemCount=" + mItemCount
11410                    + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
11411                    + ", mDeletedInvisibleItemCountSincePreviousLayout="
11412                    + mDeletedInvisibleItemCountSincePreviousLayout
11413                    + ", mStructureChanged=" + mStructureChanged
11414                    + ", mInPreLayout=" + mInPreLayout
11415                    + ", mRunSimpleAnimations=" + mRunSimpleAnimations
11416                    + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
11417                    + '}';
11418        }
11419    }
11420
11421    /**
11422     * This class defines the behavior of fling if the developer wishes to handle it.
11423     * <p>
11424     * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
11425     *
11426     * @see #setOnFlingListener(OnFlingListener)
11427     */
11428    public abstract static class OnFlingListener {
11429
11430        /**
11431         * Override this to handle a fling given the velocities in both x and y directions.
11432         * Note that this method will only be called if the associated {@link LayoutManager}
11433         * supports scrolling and the fling is not handled by nested scrolls first.
11434         *
11435         * @param velocityX the fling velocity on the X axis
11436         * @param velocityY the fling velocity on the Y axis
11437         *
11438         * @return true if the fling washandled, false otherwise.
11439         */
11440        public abstract boolean onFling(int velocityX, int velocityY);
11441    }
11442
11443    /**
11444     * Internal listener that manages items after animations finish. This is how items are
11445     * retained (not recycled) during animations, but allowed to be recycled afterwards.
11446     * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
11447     * method on the animator's listener when it is done animating any item.
11448     */
11449    private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
11450
11451        ItemAnimatorRestoreListener() {
11452        }
11453
11454        @Override
11455        public void onAnimationFinished(ViewHolder item) {
11456            item.setIsRecyclable(true);
11457            if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
11458                item.mShadowedHolder = null;
11459            }
11460            // always null this because an OldViewHolder can never become NewViewHolder w/o being
11461            // recycled.
11462            item.mShadowingHolder = null;
11463            if (!item.shouldBeKeptAsChild()) {
11464                if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
11465                    removeDetachedView(item.itemView, false);
11466                }
11467            }
11468        }
11469    }
11470
11471    /**
11472     * This class defines the animations that take place on items as changes are made
11473     * to the adapter.
11474     *
11475     * Subclasses of ItemAnimator can be used to implement custom animations for actions on
11476     * ViewHolder items. The RecyclerView will manage retaining these items while they
11477     * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
11478     * when a ViewHolder's animation is finished. In other words, there must be a matching
11479     * {@link #dispatchAnimationFinished(ViewHolder)} call for each
11480     * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
11481     * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11482     * animateChange()}
11483     * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
11484     * and
11485     * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11486     * animateDisappearance()} call.
11487     *
11488     * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
11489     *
11490     * @see #setItemAnimator(ItemAnimator)
11491     */
11492    @SuppressWarnings("UnusedParameters")
11493    public abstract static class ItemAnimator {
11494
11495        /**
11496         * The Item represented by this ViewHolder is updated.
11497         * <p>
11498         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11499         */
11500        public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
11501
11502        /**
11503         * The Item represented by this ViewHolder is removed from the adapter.
11504         * <p>
11505         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11506         */
11507        public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
11508
11509        /**
11510         * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
11511         * represented by this ViewHolder is invalid.
11512         * <p>
11513         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11514         */
11515        public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
11516
11517        /**
11518         * The position of the Item represented by this ViewHolder has been changed. This flag is
11519         * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
11520         * any adapter change that may have a side effect on this item. (e.g. The item before this
11521         * one has been removed from the Adapter).
11522         * <p>
11523         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11524         */
11525        public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
11526
11527        /**
11528         * This ViewHolder was not laid out but has been added to the layout in pre-layout state
11529         * by the {@link LayoutManager}. This means that the item was already in the Adapter but
11530         * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
11531         * to add new items in pre-layout to specify their virtual location when they are invisible
11532         * (e.g. to specify the item should <i>animate in</i> from below the visible area).
11533         * <p>
11534         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11535         */
11536        public static final int FLAG_APPEARED_IN_PRE_LAYOUT =
11537                ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
11538
11539        /**
11540         * The set of flags that might be passed to
11541         * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11542         */
11543        @IntDef(flag = true, value = {
11544                FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
11545                FLAG_APPEARED_IN_PRE_LAYOUT
11546        })
11547        @Retention(RetentionPolicy.SOURCE)
11548        public @interface AdapterChanges {}
11549        private ItemAnimatorListener mListener = null;
11550        private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
11551                new ArrayList<ItemAnimatorFinishedListener>();
11552
11553        private long mAddDuration = 120;
11554        private long mRemoveDuration = 120;
11555        private long mMoveDuration = 250;
11556        private long mChangeDuration = 250;
11557
11558        /**
11559         * Gets the current duration for which all move animations will run.
11560         *
11561         * @return The current move duration
11562         */
11563        public long getMoveDuration() {
11564            return mMoveDuration;
11565        }
11566
11567        /**
11568         * Sets the duration for which all move animations will run.
11569         *
11570         * @param moveDuration The move duration
11571         */
11572        public void setMoveDuration(long moveDuration) {
11573            mMoveDuration = moveDuration;
11574        }
11575
11576        /**
11577         * Gets the current duration for which all add animations will run.
11578         *
11579         * @return The current add duration
11580         */
11581        public long getAddDuration() {
11582            return mAddDuration;
11583        }
11584
11585        /**
11586         * Sets the duration for which all add animations will run.
11587         *
11588         * @param addDuration The add duration
11589         */
11590        public void setAddDuration(long addDuration) {
11591            mAddDuration = addDuration;
11592        }
11593
11594        /**
11595         * Gets the current duration for which all remove animations will run.
11596         *
11597         * @return The current remove duration
11598         */
11599        public long getRemoveDuration() {
11600            return mRemoveDuration;
11601        }
11602
11603        /**
11604         * Sets the duration for which all remove animations will run.
11605         *
11606         * @param removeDuration The remove duration
11607         */
11608        public void setRemoveDuration(long removeDuration) {
11609            mRemoveDuration = removeDuration;
11610        }
11611
11612        /**
11613         * Gets the current duration for which all change animations will run.
11614         *
11615         * @return The current change duration
11616         */
11617        public long getChangeDuration() {
11618            return mChangeDuration;
11619        }
11620
11621        /**
11622         * Sets the duration for which all change animations will run.
11623         *
11624         * @param changeDuration The change duration
11625         */
11626        public void setChangeDuration(long changeDuration) {
11627            mChangeDuration = changeDuration;
11628        }
11629
11630        /**
11631         * Internal only:
11632         * Sets the listener that must be called when the animator is finished
11633         * animating the item (or immediately if no animation happens). This is set
11634         * internally and is not intended to be set by external code.
11635         *
11636         * @param listener The listener that must be called.
11637         */
11638        void setListener(ItemAnimatorListener listener) {
11639            mListener = listener;
11640        }
11641
11642        /**
11643         * Called by the RecyclerView before the layout begins. Item animator should record
11644         * necessary information about the View before it is potentially rebound, moved or removed.
11645         * <p>
11646         * The data returned from this method will be passed to the related <code>animate**</code>
11647         * methods.
11648         * <p>
11649         * Note that this method may be called after pre-layout phase if LayoutManager adds new
11650         * Views to the layout in pre-layout pass.
11651         * <p>
11652         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
11653         * the View and the adapter change flags.
11654         *
11655         * @param state       The current State of RecyclerView which includes some useful data
11656         *                    about the layout that will be calculated.
11657         * @param viewHolder  The ViewHolder whose information should be recorded.
11658         * @param changeFlags Additional information about what changes happened in the Adapter
11659         *                    about the Item represented by this ViewHolder. For instance, if
11660         *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
11661         * @param payloads    The payload list that was previously passed to
11662         *                    {@link Adapter#notifyItemChanged(int, Object)} or
11663         *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
11664         *
11665         * @return An ItemHolderInfo instance that preserves necessary information about the
11666         * ViewHolder. This object will be passed back to related <code>animate**</code> methods
11667         * after layout is complete.
11668         *
11669         * @see #recordPostLayoutInformation(State, ViewHolder)
11670         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11671         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11672         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11673         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11674         */
11675        public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
11676                @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
11677                @NonNull List<Object> payloads) {
11678            return obtainHolderInfo().setFrom(viewHolder);
11679        }
11680
11681        /**
11682         * Called by the RecyclerView after the layout is complete. Item animator should record
11683         * necessary information about the View's final state.
11684         * <p>
11685         * The data returned from this method will be passed to the related <code>animate**</code>
11686         * methods.
11687         * <p>
11688         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
11689         * the View.
11690         *
11691         * @param state      The current State of RecyclerView which includes some useful data about
11692         *                   the layout that will be calculated.
11693         * @param viewHolder The ViewHolder whose information should be recorded.
11694         *
11695         * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
11696         * This object will be passed back to related <code>animate**</code> methods when
11697         * RecyclerView decides how items should be animated.
11698         *
11699         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11700         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11701         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11702         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11703         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11704         */
11705        public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
11706                @NonNull ViewHolder viewHolder) {
11707            return obtainHolderInfo().setFrom(viewHolder);
11708        }
11709
11710        /**
11711         * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
11712         * <p>
11713         * This means that the View was a child of the LayoutManager when layout started but has
11714         * been removed by the LayoutManager. It might have been removed from the adapter or simply
11715         * become invisible due to other factors. You can distinguish these two cases by checking
11716         * the change flags that were passed to
11717         * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11718         * <p>
11719         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
11720         * animation callback method which will be called by the RecyclerView depends on the
11721         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
11722         * LayoutManager's decision whether to layout the changed version of a disappearing
11723         * ViewHolder or not. RecyclerView will call
11724         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11725         * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
11726         * returns {@code false} from
11727         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
11728         * LayoutManager lays out a new disappearing view that holds the updated information.
11729         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
11730         * <p>
11731         * If LayoutManager supports predictive animations, it might provide a target disappear
11732         * location for the View by laying it out in that location. When that happens,
11733         * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
11734         * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
11735         * <p>
11736         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11737         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11738         * decides not to animate the view).
11739         *
11740         * @param viewHolder    The ViewHolder which should be animated
11741         * @param preLayoutInfo The information that was returned from
11742         *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11743         * @param postLayoutInfo The information that was returned from
11744         *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
11745         *                       null if the LayoutManager did not layout the item.
11746         *
11747         * @return true if a later call to {@link #runPendingAnimations()} is requested,
11748         * false otherwise.
11749         */
11750        public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
11751                @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
11752
11753        /**
11754         * Called by the RecyclerView when a ViewHolder is added to the layout.
11755         * <p>
11756         * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
11757         * but has  been added by the LayoutManager. It might be newly added to the adapter or
11758         * simply become visible due to other factors.
11759         * <p>
11760         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11761         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11762         * decides not to animate the view).
11763         *
11764         * @param viewHolder     The ViewHolder which should be animated
11765         * @param preLayoutInfo  The information that was returned from
11766         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11767         *                       Might be null if Item was just added to the adapter or
11768         *                       LayoutManager does not support predictive animations or it could
11769         *                       not predict that this ViewHolder will become visible.
11770         * @param postLayoutInfo The information that was returned from {@link
11771         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11772         *
11773         * @return true if a later call to {@link #runPendingAnimations()} is requested,
11774         * false otherwise.
11775         */
11776        public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
11777                @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11778
11779        /**
11780         * Called by the RecyclerView when a ViewHolder is present in both before and after the
11781         * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
11782         * for it or a {@link Adapter#notifyDataSetChanged()} call.
11783         * <p>
11784         * This ViewHolder still represents the same data that it was representing when the layout
11785         * started but its position / size may be changed by the LayoutManager.
11786         * <p>
11787         * If the Item's layout position didn't change, RecyclerView still calls this method because
11788         * it does not track this information (or does not necessarily know that an animation is
11789         * not required). Your ItemAnimator should handle this case and if there is nothing to
11790         * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
11791         * <code>false</code>.
11792         * <p>
11793         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11794         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11795         * decides not to animate the view).
11796         *
11797         * @param viewHolder     The ViewHolder which should be animated
11798         * @param preLayoutInfo  The information that was returned from
11799         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11800         * @param postLayoutInfo The information that was returned from {@link
11801         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11802         *
11803         * @return true if a later call to {@link #runPendingAnimations()} is requested,
11804         * false otherwise.
11805         */
11806        public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
11807                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11808
11809        /**
11810         * Called by the RecyclerView when an adapter item is present both before and after the
11811         * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
11812         * for it. This method may also be called when
11813         * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
11814         * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
11815         * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
11816         * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
11817         * called for the new ViewHolder and the old one will be recycled.
11818         * <p>
11819         * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
11820         * a good possibility that item contents didn't really change but it is rebound from the
11821         * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
11822         * screen didn't change and your animator should handle this case as well and avoid creating
11823         * unnecessary animations.
11824         * <p>
11825         * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
11826         * previous presentation of the item as-is and supply a new ViewHolder for the updated
11827         * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
11828         * This is useful if you don't know the contents of the Item and would like
11829         * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
11830         * <p>
11831         * When you are writing a custom item animator for your layout, it might be more performant
11832         * and elegant to re-use the same ViewHolder and animate the content changes manually.
11833         * <p>
11834         * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
11835         * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
11836         * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
11837         * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
11838         * which represent the same Item. In that case, only the new ViewHolder is visible
11839         * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
11840         * <p>
11841         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
11842         * ViewHolder when their animation is complete
11843         * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
11844         * animate the view).
11845         * <p>
11846         *  If oldHolder and newHolder are the same instance, you should call
11847         * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
11848         * <p>
11849         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
11850         * animation callback method which will be called by the RecyclerView depends on the
11851         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
11852         * LayoutManager's decision whether to layout the changed version of a disappearing
11853         * ViewHolder or not. RecyclerView will call
11854         * {@code animateChange} instead of
11855         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11856         * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
11857         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
11858         * LayoutManager lays out a new disappearing view that holds the updated information.
11859         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
11860         *
11861         * @param oldHolder     The ViewHolder before the layout is started, might be the same
11862         *                      instance with newHolder.
11863         * @param newHolder     The ViewHolder after the layout is finished, might be the same
11864         *                      instance with oldHolder.
11865         * @param preLayoutInfo  The information that was returned from
11866         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11867         * @param postLayoutInfo The information that was returned from {@link
11868         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11869         *
11870         * @return true if a later call to {@link #runPendingAnimations()} is requested,
11871         * false otherwise.
11872         */
11873        public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
11874                @NonNull ViewHolder newHolder,
11875                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11876
11877        @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
11878            int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
11879            if (viewHolder.isInvalid()) {
11880                return FLAG_INVALIDATED;
11881            }
11882            if ((flags & FLAG_INVALIDATED) == 0) {
11883                final int oldPos = viewHolder.getOldPosition();
11884                final int pos = viewHolder.getAdapterPosition();
11885                if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
11886                    flags |= FLAG_MOVED;
11887                }
11888            }
11889            return flags;
11890        }
11891
11892        /**
11893         * Called when there are pending animations waiting to be started. This state
11894         * is governed by the return values from
11895         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11896         * animateAppearance()},
11897         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11898         * animateChange()}
11899         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11900         * animatePersistence()}, and
11901         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11902         * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
11903         * called later to start the associated animations. runPendingAnimations() will be scheduled
11904         * to be run on the next frame.
11905         */
11906        public abstract void runPendingAnimations();
11907
11908        /**
11909         * Method called when an animation on a view should be ended immediately.
11910         * This could happen when other events, like scrolling, occur, so that
11911         * animating views can be quickly put into their proper end locations.
11912         * Implementations should ensure that any animations running on the item
11913         * are canceled and affected properties are set to their end values.
11914         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
11915         * animation since the animations are effectively done when this method is called.
11916         *
11917         * @param item The item for which an animation should be stopped.
11918         */
11919        public abstract void endAnimation(ViewHolder item);
11920
11921        /**
11922         * Method called when all item animations should be ended immediately.
11923         * This could happen when other events, like scrolling, occur, so that
11924         * animating views can be quickly put into their proper end locations.
11925         * Implementations should ensure that any animations running on any items
11926         * are canceled and affected properties are set to their end values.
11927         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
11928         * animation since the animations are effectively done when this method is called.
11929         */
11930        public abstract void endAnimations();
11931
11932        /**
11933         * Method which returns whether there are any item animations currently running.
11934         * This method can be used to determine whether to delay other actions until
11935         * animations end.
11936         *
11937         * @return true if there are any item animations currently running, false otherwise.
11938         */
11939        public abstract boolean isRunning();
11940
11941        /**
11942         * Method to be called by subclasses when an animation is finished.
11943         * <p>
11944         * For each call RecyclerView makes to
11945         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11946         * animateAppearance()},
11947         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11948         * animatePersistence()}, or
11949         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11950         * animateDisappearance()}, there
11951         * should
11952         * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
11953         * <p>
11954         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11955         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
11956         * and <code>newHolder</code>  (if they are not the same instance).
11957         *
11958         * @param viewHolder The ViewHolder whose animation is finished.
11959         * @see #onAnimationFinished(ViewHolder)
11960         */
11961        public final void dispatchAnimationFinished(ViewHolder viewHolder) {
11962            onAnimationFinished(viewHolder);
11963            if (mListener != null) {
11964                mListener.onAnimationFinished(viewHolder);
11965            }
11966        }
11967
11968        /**
11969         * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
11970         * ItemAnimator.
11971         *
11972         * @param viewHolder The ViewHolder whose animation is finished. There might still be other
11973         *                   animations running on this ViewHolder.
11974         * @see #dispatchAnimationFinished(ViewHolder)
11975         */
11976        public void onAnimationFinished(ViewHolder viewHolder) {
11977        }
11978
11979        /**
11980         * Method to be called by subclasses when an animation is started.
11981         * <p>
11982         * For each call RecyclerView makes to
11983         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11984         * animateAppearance()},
11985         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11986         * animatePersistence()}, or
11987         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11988         * animateDisappearance()}, there should be a matching
11989         * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
11990         * <p>
11991         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11992         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
11993         * and <code>newHolder</code> (if they are not the same instance).
11994         * <p>
11995         * If your ItemAnimator decides not to animate a ViewHolder, it should call
11996         * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
11997         * {@link #dispatchAnimationStarted(ViewHolder)}.
11998         *
11999         * @param viewHolder The ViewHolder whose animation is starting.
12000         * @see #onAnimationStarted(ViewHolder)
12001         */
12002        public final void dispatchAnimationStarted(ViewHolder viewHolder) {
12003            onAnimationStarted(viewHolder);
12004        }
12005
12006        /**
12007         * Called when a new animation is started on the given ViewHolder.
12008         *
12009         * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
12010         *                   might already be animating and this might be another animation.
12011         * @see #dispatchAnimationStarted(ViewHolder)
12012         */
12013        public void onAnimationStarted(ViewHolder viewHolder) {
12014
12015        }
12016
12017        /**
12018         * Like {@link #isRunning()}, this method returns whether there are any item
12019         * animations currently running. Additionally, the listener passed in will be called
12020         * when there are no item animations running, either immediately (before the method
12021         * returns) if no animations are currently running, or when the currently running
12022         * animations are {@link #dispatchAnimationsFinished() finished}.
12023         *
12024         * <p>Note that the listener is transient - it is either called immediately and not
12025         * stored at all, or stored only until it is called when running animations
12026         * are finished sometime later.</p>
12027         *
12028         * @param listener A listener to be called immediately if no animations are running
12029         * or later when currently-running animations have finished. A null listener is
12030         * equivalent to calling {@link #isRunning()}.
12031         * @return true if there are any item animations currently running, false otherwise.
12032         */
12033        public final boolean isRunning(ItemAnimatorFinishedListener listener) {
12034            boolean running = isRunning();
12035            if (listener != null) {
12036                if (!running) {
12037                    listener.onAnimationsFinished();
12038                } else {
12039                    mFinishedListeners.add(listener);
12040                }
12041            }
12042            return running;
12043        }
12044
12045        /**
12046         * When an item is changed, ItemAnimator can decide whether it wants to re-use
12047         * the same ViewHolder for animations or RecyclerView should create a copy of the
12048         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12049         * <p>
12050         * Note that this method will only be called if the {@link ViewHolder} still has the same
12051         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12052         * both {@link ViewHolder}s in the
12053         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12054         * <p>
12055         * If your application is using change payloads, you can override
12056         * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
12057         *
12058         * @param viewHolder The ViewHolder which represents the changed item's old content.
12059         *
12060         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
12061         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
12062         *         ItemAnimator to animate. Default implementation returns <code>true</code>.
12063         *
12064         * @see #canReuseUpdatedViewHolder(ViewHolder, List)
12065         */
12066        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
12067            return true;
12068        }
12069
12070        /**
12071         * When an item is changed, ItemAnimator can decide whether it wants to re-use
12072         * the same ViewHolder for animations or RecyclerView should create a copy of the
12073         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12074         * <p>
12075         * Note that this method will only be called if the {@link ViewHolder} still has the same
12076         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12077         * both {@link ViewHolder}s in the
12078         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12079         *
12080         * @param viewHolder The ViewHolder which represents the changed item's old content.
12081         * @param payloads A non-null list of merged payloads that were sent with change
12082         *                 notifications. Can be empty if the adapter is invalidated via
12083         *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
12084         *                 payloads will be passed into
12085         *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
12086         *                 method <b>if</b> this method returns <code>true</code>.
12087         *
12088         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
12089         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
12090         *         ItemAnimator to animate. Default implementation calls
12091         *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
12092         *
12093         * @see #canReuseUpdatedViewHolder(ViewHolder)
12094         */
12095        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
12096                @NonNull List<Object> payloads) {
12097            return canReuseUpdatedViewHolder(viewHolder);
12098        }
12099
12100        /**
12101         * This method should be called by ItemAnimator implementations to notify
12102         * any listeners that all pending and active item animations are finished.
12103         */
12104        public final void dispatchAnimationsFinished() {
12105            final int count = mFinishedListeners.size();
12106            for (int i = 0; i < count; ++i) {
12107                mFinishedListeners.get(i).onAnimationsFinished();
12108            }
12109            mFinishedListeners.clear();
12110        }
12111
12112        /**
12113         * Returns a new {@link ItemHolderInfo} which will be used to store information about the
12114         * ViewHolder. This information will later be passed into <code>animate**</code> methods.
12115         * <p>
12116         * You can override this method if you want to extend {@link ItemHolderInfo} and provide
12117         * your own instances.
12118         *
12119         * @return A new {@link ItemHolderInfo}.
12120         */
12121        public ItemHolderInfo obtainHolderInfo() {
12122            return new ItemHolderInfo();
12123        }
12124
12125        /**
12126         * The interface to be implemented by listeners to animation events from this
12127         * ItemAnimator. This is used internally and is not intended for developers to
12128         * create directly.
12129         */
12130        interface ItemAnimatorListener {
12131            void onAnimationFinished(ViewHolder item);
12132        }
12133
12134        /**
12135         * This interface is used to inform listeners when all pending or running animations
12136         * in an ItemAnimator are finished. This can be used, for example, to delay an action
12137         * in a data set until currently-running animations are complete.
12138         *
12139         * @see #isRunning(ItemAnimatorFinishedListener)
12140         */
12141        public interface ItemAnimatorFinishedListener {
12142            /**
12143             * Notifies when all pending or running animations in an ItemAnimator are finished.
12144             */
12145            void onAnimationsFinished();
12146        }
12147
12148        /**
12149         * A simple data structure that holds information about an item's bounds.
12150         * This information is used in calculating item animations. Default implementation of
12151         * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
12152         * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
12153         * structure. You can extend this class if you would like to keep more information about
12154         * the Views.
12155         * <p>
12156         * If you want to provide your own implementation but still use `super` methods to record
12157         * basic information, you can override {@link #obtainHolderInfo()} to provide your own
12158         * instances.
12159         */
12160        public static class ItemHolderInfo {
12161
12162            /**
12163             * The left edge of the View (excluding decorations)
12164             */
12165            public int left;
12166
12167            /**
12168             * The top edge of the View (excluding decorations)
12169             */
12170            public int top;
12171
12172            /**
12173             * The right edge of the View (excluding decorations)
12174             */
12175            public int right;
12176
12177            /**
12178             * The bottom edge of the View (excluding decorations)
12179             */
12180            public int bottom;
12181
12182            /**
12183             * The change flags that were passed to
12184             * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
12185             */
12186            @AdapterChanges
12187            public int changeFlags;
12188
12189            public ItemHolderInfo() {
12190            }
12191
12192            /**
12193             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
12194             * the given ViewHolder. Clears all {@link #changeFlags}.
12195             *
12196             * @param holder The ViewHolder whose bounds should be copied.
12197             * @return This {@link ItemHolderInfo}
12198             */
12199            public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder) {
12200                return setFrom(holder, 0);
12201            }
12202
12203            /**
12204             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
12205             * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
12206             *
12207             * @param holder The ViewHolder whose bounds should be copied.
12208             * @param flags  The adapter change flags that were passed into
12209             *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
12210             *               List)}.
12211             * @return This {@link ItemHolderInfo}
12212             */
12213            public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder,
12214                    @AdapterChanges int flags) {
12215                final View view = holder.itemView;
12216                this.left = view.getLeft();
12217                this.top = view.getTop();
12218                this.right = view.getRight();
12219                this.bottom = view.getBottom();
12220                return this;
12221            }
12222        }
12223    }
12224
12225    @Override
12226    protected int getChildDrawingOrder(int childCount, int i) {
12227        if (mChildDrawingOrderCallback == null) {
12228            return super.getChildDrawingOrder(childCount, i);
12229        } else {
12230            return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
12231        }
12232    }
12233
12234    /**
12235     * A callback interface that can be used to alter the drawing order of RecyclerView children.
12236     * <p>
12237     * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
12238     * that applies to that method also applies to this callback. For example, changing the drawing
12239     * order of two views will not have any effect if their elevation values are different since
12240     * elevation overrides the result of this callback.
12241     */
12242    public interface ChildDrawingOrderCallback {
12243        /**
12244         * Returns the index of the child to draw for this iteration. Override this
12245         * if you want to change the drawing order of children. By default, it
12246         * returns i.
12247         *
12248         * @param i The current iteration.
12249         * @return The index of the child to draw this iteration.
12250         *
12251         * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
12252         */
12253        int onGetChildDrawingOrder(int childCount, int i);
12254    }
12255}
12256