RecyclerView.java revision a031fe7234ecf5d8ef3b2bc3aa652e79cdb5004e
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18package android.support.v7.widget;
19
20import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
21import static android.support.v4.view.ViewCompat.TYPE_NON_TOUCH;
22import static android.support.v4.view.ViewCompat.TYPE_TOUCH;
23
24import android.content.Context;
25import android.content.res.Resources;
26import android.content.res.TypedArray;
27import android.database.Observable;
28import android.graphics.Canvas;
29import android.graphics.Matrix;
30import android.graphics.PointF;
31import android.graphics.Rect;
32import android.graphics.RectF;
33import android.graphics.drawable.Drawable;
34import android.graphics.drawable.StateListDrawable;
35import android.os.Build;
36import android.os.Bundle;
37import android.os.Parcel;
38import android.os.Parcelable;
39import android.os.SystemClock;
40import android.support.annotation.CallSuper;
41import android.support.annotation.IntDef;
42import android.support.annotation.NonNull;
43import android.support.annotation.Nullable;
44import android.support.annotation.RestrictTo;
45import android.support.annotation.VisibleForTesting;
46import android.support.v4.os.TraceCompat;
47import android.support.v4.view.AbsSavedState;
48import android.support.v4.view.InputDeviceCompat;
49import android.support.v4.view.MotionEventCompat;
50import android.support.v4.view.NestedScrollingChild2;
51import android.support.v4.view.NestedScrollingChildHelper;
52import android.support.v4.view.ScrollingView;
53import android.support.v4.view.ViewCompat;
54import android.support.v4.view.ViewConfigurationCompat;
55import android.support.v4.view.accessibility.AccessibilityEventCompat;
56import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
57import android.support.v4.widget.EdgeEffectCompat;
58import android.support.v7.recyclerview.R;
59import android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
60import android.util.AttributeSet;
61import android.util.Log;
62import android.util.SparseArray;
63import android.view.Display;
64import android.view.FocusFinder;
65import android.view.InputDevice;
66import android.view.MotionEvent;
67import android.view.VelocityTracker;
68import android.view.View;
69import android.view.ViewConfiguration;
70import android.view.ViewGroup;
71import android.view.ViewParent;
72import android.view.accessibility.AccessibilityEvent;
73import android.view.accessibility.AccessibilityManager;
74import android.view.animation.Interpolator;
75import android.widget.EdgeEffect;
76import android.widget.OverScroller;
77
78import java.lang.annotation.Retention;
79import java.lang.annotation.RetentionPolicy;
80import java.lang.ref.WeakReference;
81import java.lang.reflect.Constructor;
82import java.lang.reflect.InvocationTargetException;
83import java.util.ArrayList;
84import java.util.Collections;
85import java.util.List;
86
87/**
88 * A flexible view for providing a limited window into a large data set.
89 *
90 * <h3>Glossary of terms:</h3>
91 *
92 * <ul>
93 *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
94 *     that represent items in a data set.</li>
95 *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
96 *     <li><em>Index:</em> The index of an attached child view as used in a call to
97 *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
98 *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
99 *     to a <em>position</em> within the adapter.</li>
100 *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
101 *     position may be placed in a cache for later reuse to display the same type of data again
102 *     later. This can drastically improve performance by skipping initial layout inflation
103 *     or construction.</li>
104 *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
105 *     state during layout. Scrap views may be reused without becoming fully detached
106 *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
107 *     by the adapter if the view was considered <em>dirty</em>.</li>
108 *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
109 *     being displayed.</li>
110 * </ul>
111 *
112 * <h4>Positions in RecyclerView:</h4>
113 * <p>
114 * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
115 * {@link LayoutManager} to be able to detect data set changes in batches during a layout
116 * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
117 * It also helps with performance because all view bindings happen at the same time and unnecessary
118 * bindings are avoided.
119 * <p>
120 * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
121 * <ul>
122 *     <li>layout position: Position of an item in the latest layout calculation. This is the
123 *     position from the LayoutManager's perspective.</li>
124 *     <li>adapter position: Position of an item in the adapter. This is the position from
125 *     the Adapter's perspective.</li>
126 * </ul>
127 * <p>
128 * These two positions are the same except the time between dispatching <code>adapter.notify*
129 * </code> events and calculating the updated layout.
130 * <p>
131 * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
132 * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
133 * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
134 * last layout calculation. You can rely on these positions to be consistent with what user is
135 * currently seeing on the screen. For example, if you have a list of items on the screen and user
136 * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
137 * is seeing.
138 * <p>
139 * The other set of position related methods are in the form of
140 * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
141 * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
142 * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
143 * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
144 * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
145 * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
146 * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
147 * <code>null</code> results from these methods.
148 * <p>
149 * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
150 * writing an {@link Adapter}, you probably want to use adapter positions.
151 *
152 * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_layoutManager
153 */
154public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild2 {
155
156    static final String TAG = "RecyclerView";
157
158    static final boolean DEBUG = false;
159
160    static final boolean VERBOSE_TRACING = false;
161
162    private static final int[]  NESTED_SCROLLING_ATTRS =
163            {16843830 /* android.R.attr.nestedScrollingEnabled */};
164
165    private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
166
167    /**
168     * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
169     * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
170     * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
171     * recursively traverses itemView and invalidates display list for each ViewGroup that matches
172     * this criteria.
173     */
174    static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
175            || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
176    /**
177     * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
178     * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
179     * 0 when mode is unspecified.
180     */
181    static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
182
183    static final boolean POST_UPDATES_ON_ANIMATION = Build.VERSION.SDK_INT >= 16;
184
185    /**
186     * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
187     * RenderThread but before the next frame begins. We schedule prefetch work in this window.
188     */
189    private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
190
191    /**
192     * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
193     * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
194     */
195    private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
196
197    /**
198     * on API 15-, a focused child can still be considered a focused child of RV even after
199     * it's being removed or its focusable flag is set to false. This is because when this focused
200     * child is detached, the reference to this child is not removed in clearFocus. API 16 and above
201     * properly handle this case by calling ensureInputFocusOnFirstFocusable or rootViewRequestFocus
202     * to request focus on a new child, which will clear the focus on the old (detached) child as a
203     * side-effect.
204     */
205    private static final boolean IGNORE_DETACHED_FOCUSED_CHILD = Build.VERSION.SDK_INT <= 15;
206
207    static final boolean DISPATCH_TEMP_DETACH = false;
208    public static final int HORIZONTAL = 0;
209    public static final int VERTICAL = 1;
210
211    public static final int NO_POSITION = -1;
212    public static final long NO_ID = -1;
213    public static final int INVALID_TYPE = -1;
214
215    /**
216     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
217     * that the RecyclerView should use the standard touch slop for smooth,
218     * continuous scrolling.
219     */
220    public static final int TOUCH_SLOP_DEFAULT = 0;
221
222    /**
223     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
224     * that the RecyclerView should use the standard touch slop for scrolling
225     * widgets that snap to a page or other coarse-grained barrier.
226     */
227    public static final int TOUCH_SLOP_PAGING = 1;
228
229    static final int MAX_SCROLL_DURATION = 2000;
230
231    /**
232     * RecyclerView is calculating a scroll.
233     * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
234     * it. Try to avoid using EditText, focusable views or handle them with care.
235     */
236    static final String TRACE_SCROLL_TAG = "RV Scroll";
237
238    /**
239     * OnLayout has been called by the View system.
240     * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
241     * update themselves directly. This will cause a full re-layout but when it happens via the
242     * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
243     */
244    private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
245
246    /**
247     * NotifyDataSetChanged or equal has been called.
248     * If this is taking a long time, try sending granular notify adapter changes instead of just
249     * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
250     * might help.
251     */
252    private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
253
254    /**
255     * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
256     * If this is taking a long time, you may have dispatched too many Adapter updates causing too
257     * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
258     * methods.
259     */
260    private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
261
262    /**
263     * RecyclerView is rebinding a View.
264     * If this is taking a lot of time, consider optimizing your layout or make sure you are not
265     * doing extra operations in onBindViewHolder call.
266     */
267    static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
268
269    /**
270     * RecyclerView is attempting to pre-populate off screen views.
271     */
272    static final String TRACE_PREFETCH_TAG = "RV Prefetch";
273
274    /**
275     * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
276     * RecyclerView.
277     */
278    static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
279
280    /**
281     * RecyclerView is creating a new View.
282     * If too many of these present in Systrace:
283     * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
284     * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
285     * > Adapter#onFailedToRecycleView(ViewHolder)})
286     *
287     * - There might be too many item view types.
288     * > Try merging them
289     *
290     * - There might be too many itemChange animations and not enough space in RecyclerPool.
291     * >Try increasing your pool size and item cache size.
292     */
293    static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
294    private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
295            new Class[]{Context.class, AttributeSet.class, int.class, int.class};
296
297    private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
298
299    final Recycler mRecycler = new Recycler();
300
301    private SavedState mPendingSavedState;
302
303    /**
304     * Handles adapter updates
305     */
306    AdapterHelper mAdapterHelper;
307
308    /**
309     * Handles abstraction between LayoutManager children and RecyclerView children
310     */
311    ChildHelper mChildHelper;
312
313    /**
314     * Keeps data about views to be used for animations
315     */
316    final ViewInfoStore mViewInfoStore = new ViewInfoStore();
317
318    /**
319     * Prior to L, there is no way to query this variable which is why we override the setter and
320     * track it here.
321     */
322    boolean mClipToPadding;
323
324    /**
325     * Note: this Runnable is only ever posted if:
326     * 1) We've been through first layout
327     * 2) We know we have a fixed size (mHasFixedSize)
328     * 3) We're attached
329     */
330    final Runnable mUpdateChildViewsRunnable = new Runnable() {
331        @Override
332        public void run() {
333            if (!mFirstLayoutComplete || isLayoutRequested()) {
334                // a layout request will happen, we should not do layout here.
335                return;
336            }
337            if (!mIsAttached) {
338                requestLayout();
339                // if we are not attached yet, mark us as requiring layout and skip
340                return;
341            }
342            if (mLayoutFrozen) {
343                mLayoutRequestEaten = true;
344                return; //we'll process updates when ice age ends.
345            }
346            consumePendingUpdateOperations();
347        }
348    };
349
350    final Rect mTempRect = new Rect();
351    private final Rect mTempRect2 = new Rect();
352    final RectF mTempRectF = new RectF();
353    Adapter mAdapter;
354    @VisibleForTesting LayoutManager mLayout;
355    RecyclerListener mRecyclerListener;
356    final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
357    private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
358            new ArrayList<>();
359    private OnItemTouchListener mActiveOnItemTouchListener;
360    boolean mIsAttached;
361    boolean mHasFixedSize;
362    boolean mEnableFastScroller;
363    @VisibleForTesting boolean mFirstLayoutComplete;
364
365    // Counting lock to control whether we should ignore requestLayout calls from children or not.
366    private int mEatRequestLayout = 0;
367
368    boolean mLayoutRequestEaten;
369    boolean mLayoutFrozen;
370    private boolean mIgnoreMotionEventTillDown;
371
372    // binary OR of change events that were eaten during a layout or scroll.
373    private int mEatenAccessibilityChangeFlags;
374    boolean mAdapterUpdateDuringMeasure;
375
376    private final AccessibilityManager mAccessibilityManager;
377    private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
378
379    /**
380     * Set to true when an adapter data set changed notification is received.
381     * In that case, we cannot run any animations since we don't know what happened until layout.
382     *
383     * Attached items are invalid until next layout, at which point layout will animate/replace
384     * items as necessary, building up content from the (effectively) new adapter from scratch.
385     *
386     * Cached items must be discarded when setting this to true, so that the cache may be freely
387     * used by prefetching until the next layout occurs.
388     *
389     * @see #setDataSetChangedAfterLayout()
390     */
391    boolean mDataSetHasChangedAfterLayout = false;
392
393    /**
394     * This variable is incremented during a dispatchLayout and/or scroll.
395     * Some methods should not be called during these periods (e.g. adapter data change).
396     * Doing so will create hard to find bugs so we better check it and throw an exception.
397     *
398     * @see #assertInLayoutOrScroll(String)
399     * @see #assertNotInLayoutOrScroll(String)
400     */
401    private int mLayoutOrScrollCounter = 0;
402
403    /**
404     * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
405     * (for API compatibility).
406     * <p>
407     * It is a bad practice for a developer to update the data in a scroll callback since it is
408     * potentially called during a layout.
409     */
410    private int mDispatchScrollCounter = 0;
411
412    private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
413
414    ItemAnimator mItemAnimator = new DefaultItemAnimator();
415
416    private static final int INVALID_POINTER = -1;
417
418    /**
419     * The RecyclerView is not currently scrolling.
420     * @see #getScrollState()
421     */
422    public static final int SCROLL_STATE_IDLE = 0;
423
424    /**
425     * The RecyclerView is currently being dragged by outside input such as user touch input.
426     * @see #getScrollState()
427     */
428    public static final int SCROLL_STATE_DRAGGING = 1;
429
430    /**
431     * The RecyclerView is currently animating to a final position while not under
432     * outside control.
433     * @see #getScrollState()
434     */
435    public static final int SCROLL_STATE_SETTLING = 2;
436
437    static final long FOREVER_NS = Long.MAX_VALUE;
438
439    // Touch/scrolling handling
440
441    private int mScrollState = SCROLL_STATE_IDLE;
442    private int mScrollPointerId = INVALID_POINTER;
443    private VelocityTracker mVelocityTracker;
444    private int mInitialTouchX;
445    private int mInitialTouchY;
446    private int mLastTouchX;
447    private int mLastTouchY;
448    private int mTouchSlop;
449    private OnFlingListener mOnFlingListener;
450    private final int mMinFlingVelocity;
451    private final int mMaxFlingVelocity;
452
453    // This value is used when handling rotary encoder generic motion events.
454    private float mScaledHorizontalScrollFactor = Float.MIN_VALUE;
455    private float mScaledVerticalScrollFactor = Float.MIN_VALUE;
456
457    private boolean mPreserveFocusAfterLayout = true;
458
459    final ViewFlinger mViewFlinger = new ViewFlinger();
460
461    GapWorker mGapWorker;
462    GapWorker.LayoutPrefetchRegistryImpl mPrefetchRegistry =
463            ALLOW_THREAD_GAP_WORK ? new GapWorker.LayoutPrefetchRegistryImpl() : null;
464
465    final State mState = new State();
466
467    private OnScrollListener mScrollListener;
468    private List<OnScrollListener> mScrollListeners;
469
470    // For use in item animations
471    boolean mItemsAddedOrRemoved = false;
472    boolean mItemsChanged = false;
473    private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
474            new ItemAnimatorRestoreListener();
475    boolean mPostedAnimatorRunner = false;
476    RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
477    private ChildDrawingOrderCallback mChildDrawingOrderCallback;
478
479    // simple array to keep min and max child position during a layout calculation
480    // preserved not to create a new one in each layout pass
481    private final int[] mMinMaxLayoutPositions = new int[2];
482
483    private NestedScrollingChildHelper mScrollingChildHelper;
484    private final int[] mScrollOffset = new int[2];
485    private final int[] mScrollConsumed = new int[2];
486    private final int[] mNestedOffsets = new int[2];
487
488    /**
489     * These are views that had their a11y importance changed during a layout. We defer these events
490     * until the end of the layout because a11y service may make sync calls back to the RV while
491     * the View's state is undefined.
492     */
493    @VisibleForTesting
494    final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList<>();
495
496    private Runnable mItemAnimatorRunner = new Runnable() {
497        @Override
498        public void run() {
499            if (mItemAnimator != null) {
500                mItemAnimator.runPendingAnimations();
501            }
502            mPostedAnimatorRunner = false;
503        }
504    };
505
506    static final Interpolator sQuinticInterpolator = new Interpolator() {
507        @Override
508        public float getInterpolation(float t) {
509            t -= 1.0f;
510            return t * t * t * t * t + 1.0f;
511        }
512    };
513
514    /**
515     * The callback to convert view info diffs into animations.
516     */
517    private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
518            new ViewInfoStore.ProcessCallback() {
519                @Override
520                public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
521                        @Nullable ItemHolderInfo postInfo) {
522                    mRecycler.unscrapView(viewHolder);
523                    animateDisappearance(viewHolder, info, postInfo);
524                }
525                @Override
526                public void processAppeared(ViewHolder viewHolder,
527                        ItemHolderInfo preInfo, ItemHolderInfo info) {
528                    animateAppearance(viewHolder, preInfo, info);
529                }
530
531                @Override
532                public void processPersistent(ViewHolder viewHolder,
533                        @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
534                    viewHolder.setIsRecyclable(false);
535                    if (mDataSetHasChangedAfterLayout) {
536                        // since it was rebound, use change instead as we'll be mapping them from
537                        // stable ids. If stable ids were false, we would not be running any
538                        // animations
539                        if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo,
540                                postInfo)) {
541                            postAnimationRunner();
542                        }
543                    } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
544                        postAnimationRunner();
545                    }
546                }
547                @Override
548                public void unused(ViewHolder viewHolder) {
549                    mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
550                }
551            };
552
553    public RecyclerView(Context context) {
554        this(context, null);
555    }
556
557    public RecyclerView(Context context, @Nullable AttributeSet attrs) {
558        this(context, attrs, 0);
559    }
560
561    public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
562        super(context, attrs, defStyle);
563        if (attrs != null) {
564            TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
565            mClipToPadding = a.getBoolean(0, true);
566            a.recycle();
567        } else {
568            mClipToPadding = true;
569        }
570        setScrollContainer(true);
571        setFocusableInTouchMode(true);
572
573        final ViewConfiguration vc = ViewConfiguration.get(context);
574        mTouchSlop = vc.getScaledTouchSlop();
575        mScaledHorizontalScrollFactor =
576                ViewConfigurationCompat.getScaledHorizontalScrollFactor(vc, context);
577        mScaledVerticalScrollFactor =
578                ViewConfigurationCompat.getScaledVerticalScrollFactor(vc, context);
579        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
580        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
581        setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
582
583        mItemAnimator.setListener(mItemAnimatorListener);
584        initAdapterManager();
585        initChildrenHelper();
586        // If not explicitly specified this view is important for accessibility.
587        if (ViewCompat.getImportantForAccessibility(this)
588                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
589            ViewCompat.setImportantForAccessibility(this,
590                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
591        }
592        mAccessibilityManager = (AccessibilityManager) getContext()
593                .getSystemService(Context.ACCESSIBILITY_SERVICE);
594        setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
595        // Create the layoutManager if specified.
596
597        boolean nestedScrollingEnabled = true;
598
599        if (attrs != null) {
600            int defStyleRes = 0;
601            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
602                    defStyle, defStyleRes);
603            String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
604            int descendantFocusability = a.getInt(
605                    R.styleable.RecyclerView_android_descendantFocusability, -1);
606            if (descendantFocusability == -1) {
607                setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
608            }
609            mEnableFastScroller = a.getBoolean(R.styleable.RecyclerView_fastScrollEnabled, false);
610            if (mEnableFastScroller) {
611                StateListDrawable verticalThumbDrawable = (StateListDrawable) a
612                        .getDrawable(R.styleable.RecyclerView_fastScrollVerticalThumbDrawable);
613                Drawable verticalTrackDrawable = a
614                        .getDrawable(R.styleable.RecyclerView_fastScrollVerticalTrackDrawable);
615                StateListDrawable horizontalThumbDrawable = (StateListDrawable) a
616                        .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalThumbDrawable);
617                Drawable horizontalTrackDrawable = a
618                        .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalTrackDrawable);
619                initFastScroller(verticalThumbDrawable, verticalTrackDrawable,
620                        horizontalThumbDrawable, horizontalTrackDrawable);
621            }
622            a.recycle();
623            createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
624
625            if (Build.VERSION.SDK_INT >= 21) {
626                a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
627                        defStyle, defStyleRes);
628                nestedScrollingEnabled = a.getBoolean(0, true);
629                a.recycle();
630            }
631        } else {
632            setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
633        }
634
635        // Re-set whether nested scrolling is enabled so that it is set on all API levels
636        setNestedScrollingEnabled(nestedScrollingEnabled);
637    }
638
639    /**
640     * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
641     * @return An instance of AccessibilityDelegateCompat used by RecyclerView
642     */
643    public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
644        return mAccessibilityDelegate;
645    }
646
647    /**
648     * Sets the accessibility delegate compatibility implementation used by RecyclerView.
649     * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
650     */
651    public void setAccessibilityDelegateCompat(
652            RecyclerViewAccessibilityDelegate accessibilityDelegate) {
653        mAccessibilityDelegate = accessibilityDelegate;
654        ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
655    }
656
657    /**
658     * Instantiate and set a LayoutManager, if specified in the attributes.
659     */
660    private void createLayoutManager(Context context, String className, AttributeSet attrs,
661            int defStyleAttr, int defStyleRes) {
662        if (className != null) {
663            className = className.trim();
664            if (className.length() != 0) {  // Can't use isEmpty since it was added in API 9.
665                className = getFullClassName(context, className);
666                try {
667                    ClassLoader classLoader;
668                    if (isInEditMode()) {
669                        // Stupid layoutlib cannot handle simple class loaders.
670                        classLoader = this.getClass().getClassLoader();
671                    } else {
672                        classLoader = context.getClassLoader();
673                    }
674                    Class<? extends LayoutManager> layoutManagerClass =
675                            classLoader.loadClass(className).asSubclass(LayoutManager.class);
676                    Constructor<? extends LayoutManager> constructor;
677                    Object[] constructorArgs = null;
678                    try {
679                        constructor = layoutManagerClass
680                                .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
681                        constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
682                    } catch (NoSuchMethodException e) {
683                        try {
684                            constructor = layoutManagerClass.getConstructor();
685                        } catch (NoSuchMethodException e1) {
686                            e1.initCause(e);
687                            throw new IllegalStateException(attrs.getPositionDescription()
688                                    + ": Error creating LayoutManager " + className, e1);
689                        }
690                    }
691                    constructor.setAccessible(true);
692                    setLayoutManager(constructor.newInstance(constructorArgs));
693                } catch (ClassNotFoundException e) {
694                    throw new IllegalStateException(attrs.getPositionDescription()
695                            + ": Unable to find LayoutManager " + className, e);
696                } catch (InvocationTargetException e) {
697                    throw new IllegalStateException(attrs.getPositionDescription()
698                            + ": Could not instantiate the LayoutManager: " + className, e);
699                } catch (InstantiationException e) {
700                    throw new IllegalStateException(attrs.getPositionDescription()
701                            + ": Could not instantiate the LayoutManager: " + className, e);
702                } catch (IllegalAccessException e) {
703                    throw new IllegalStateException(attrs.getPositionDescription()
704                            + ": Cannot access non-public constructor " + className, e);
705                } catch (ClassCastException e) {
706                    throw new IllegalStateException(attrs.getPositionDescription()
707                            + ": Class is not a LayoutManager " + className, e);
708                }
709            }
710        }
711    }
712
713    private String getFullClassName(Context context, String className) {
714        if (className.charAt(0) == '.') {
715            return context.getPackageName() + className;
716        }
717        if (className.contains(".")) {
718            return className;
719        }
720        return RecyclerView.class.getPackage().getName() + '.' + className;
721    }
722
723    private void initChildrenHelper() {
724        mChildHelper = new ChildHelper(new ChildHelper.Callback() {
725            @Override
726            public int getChildCount() {
727                return RecyclerView.this.getChildCount();
728            }
729
730            @Override
731            public void addView(View child, int index) {
732                if (VERBOSE_TRACING) {
733                    TraceCompat.beginSection("RV addView");
734                }
735                RecyclerView.this.addView(child, index);
736                if (VERBOSE_TRACING) {
737                    TraceCompat.endSection();
738                }
739                dispatchChildAttached(child);
740            }
741
742            @Override
743            public int indexOfChild(View view) {
744                return RecyclerView.this.indexOfChild(view);
745            }
746
747            @Override
748            public void removeViewAt(int index) {
749                final View child = RecyclerView.this.getChildAt(index);
750                if (child != null) {
751                    dispatchChildDetached(child);
752
753                    // Clear any android.view.animation.Animation that may prevent the item from
754                    // detaching when being removed. If a child is re-added before the
755                    // lazy detach occurs, it will receive invalid attach/detach sequencing.
756                    child.clearAnimation();
757                }
758                if (VERBOSE_TRACING) {
759                    TraceCompat.beginSection("RV removeViewAt");
760                }
761                RecyclerView.this.removeViewAt(index);
762                if (VERBOSE_TRACING) {
763                    TraceCompat.endSection();
764                }
765            }
766
767            @Override
768            public View getChildAt(int offset) {
769                return RecyclerView.this.getChildAt(offset);
770            }
771
772            @Override
773            public void removeAllViews() {
774                final int count = getChildCount();
775                for (int i = 0; i < count; i++) {
776                    View child = getChildAt(i);
777                    dispatchChildDetached(child);
778
779                    // Clear any android.view.animation.Animation that may prevent the item from
780                    // detaching when being removed. If a child is re-added before the
781                    // lazy detach occurs, it will receive invalid attach/detach sequencing.
782                    child.clearAnimation();
783                }
784                RecyclerView.this.removeAllViews();
785            }
786
787            @Override
788            public ViewHolder getChildViewHolder(View view) {
789                return getChildViewHolderInt(view);
790            }
791
792            @Override
793            public void attachViewToParent(View child, int index,
794                    ViewGroup.LayoutParams layoutParams) {
795                final ViewHolder vh = getChildViewHolderInt(child);
796                if (vh != null) {
797                    if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
798                        throw new IllegalArgumentException("Called attach on a child which is not"
799                                + " detached: " + vh);
800                    }
801                    if (DEBUG) {
802                        Log.d(TAG, "reAttach " + vh);
803                    }
804                    vh.clearTmpDetachFlag();
805                }
806                RecyclerView.this.attachViewToParent(child, index, layoutParams);
807            }
808
809            @Override
810            public void detachViewFromParent(int offset) {
811                final View view = getChildAt(offset);
812                if (view != null) {
813                    final ViewHolder vh = getChildViewHolderInt(view);
814                    if (vh != null) {
815                        if (vh.isTmpDetached() && !vh.shouldIgnore()) {
816                            throw new IllegalArgumentException("called detach on an already"
817                                    + " detached child " + vh);
818                        }
819                        if (DEBUG) {
820                            Log.d(TAG, "tmpDetach " + vh);
821                        }
822                        vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
823                    }
824                }
825                RecyclerView.this.detachViewFromParent(offset);
826            }
827
828            @Override
829            public void onEnteredHiddenState(View child) {
830                final ViewHolder vh = getChildViewHolderInt(child);
831                if (vh != null) {
832                    vh.onEnteredHiddenState(RecyclerView.this);
833                }
834            }
835
836            @Override
837            public void onLeftHiddenState(View child) {
838                final ViewHolder vh = getChildViewHolderInt(child);
839                if (vh != null) {
840                    vh.onLeftHiddenState(RecyclerView.this);
841                }
842            }
843        });
844    }
845
846    void initAdapterManager() {
847        mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
848            @Override
849            public ViewHolder findViewHolder(int position) {
850                final ViewHolder vh = findViewHolderForPosition(position, true);
851                if (vh == null) {
852                    return null;
853                }
854                // ensure it is not hidden because for adapter helper, the only thing matter is that
855                // LM thinks view is a child.
856                if (mChildHelper.isHidden(vh.itemView)) {
857                    if (DEBUG) {
858                        Log.d(TAG, "assuming view holder cannot be find because it is hidden");
859                    }
860                    return null;
861                }
862                return vh;
863            }
864
865            @Override
866            public void offsetPositionsForRemovingInvisible(int start, int count) {
867                offsetPositionRecordsForRemove(start, count, true);
868                mItemsAddedOrRemoved = true;
869                mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
870            }
871
872            @Override
873            public void offsetPositionsForRemovingLaidOutOrNewView(
874                    int positionStart, int itemCount) {
875                offsetPositionRecordsForRemove(positionStart, itemCount, false);
876                mItemsAddedOrRemoved = true;
877            }
878
879
880            @Override
881            public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
882                viewRangeUpdate(positionStart, itemCount, payload);
883                mItemsChanged = true;
884            }
885
886            @Override
887            public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
888                dispatchUpdate(op);
889            }
890
891            void dispatchUpdate(AdapterHelper.UpdateOp op) {
892                switch (op.cmd) {
893                    case AdapterHelper.UpdateOp.ADD:
894                        mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
895                        break;
896                    case AdapterHelper.UpdateOp.REMOVE:
897                        mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
898                        break;
899                    case AdapterHelper.UpdateOp.UPDATE:
900                        mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
901                                op.payload);
902                        break;
903                    case AdapterHelper.UpdateOp.MOVE:
904                        mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
905                        break;
906                }
907            }
908
909            @Override
910            public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
911                dispatchUpdate(op);
912            }
913
914            @Override
915            public void offsetPositionsForAdd(int positionStart, int itemCount) {
916                offsetPositionRecordsForInsert(positionStart, itemCount);
917                mItemsAddedOrRemoved = true;
918            }
919
920            @Override
921            public void offsetPositionsForMove(int from, int to) {
922                offsetPositionRecordsForMove(from, to);
923                // should we create mItemsMoved ?
924                mItemsAddedOrRemoved = true;
925            }
926        });
927    }
928
929    /**
930     * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
931     * size is not affected by the adapter contents. RecyclerView can still change its size based
932     * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
933     * size of its children or contents of its adapter (except the number of items in the adapter).
934     * <p>
935     * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
936     * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
937     *
938     * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
939     */
940    public void setHasFixedSize(boolean hasFixedSize) {
941        mHasFixedSize = hasFixedSize;
942    }
943
944    /**
945     * @return true if the app has specified that changes in adapter content cannot change
946     * the size of the RecyclerView itself.
947     */
948    public boolean hasFixedSize() {
949        return mHasFixedSize;
950    }
951
952    @Override
953    public void setClipToPadding(boolean clipToPadding) {
954        if (clipToPadding != mClipToPadding) {
955            invalidateGlows();
956        }
957        mClipToPadding = clipToPadding;
958        super.setClipToPadding(clipToPadding);
959        if (mFirstLayoutComplete) {
960            requestLayout();
961        }
962    }
963
964    /**
965     * Returns whether this RecyclerView will clip its children to its padding, and resize (but
966     * not clip) any EdgeEffect to the padded region, if padding is present.
967     * <p>
968     * By default, children are clipped to the padding of their parent
969     * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
970     *
971     * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
972     *         clip) any EdgeEffect to the padded region, false otherwise.
973     *
974     * @attr name android:clipToPadding
975     */
976    @Override
977    public boolean getClipToPadding() {
978        return mClipToPadding;
979    }
980
981    /**
982     * Configure the scrolling touch slop for a specific use case.
983     *
984     * Set up the RecyclerView's scrolling motion threshold based on common usages.
985     * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
986     *
987     * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
988     *                     the intended usage of this RecyclerView
989     */
990    public void setScrollingTouchSlop(int slopConstant) {
991        final ViewConfiguration vc = ViewConfiguration.get(getContext());
992        switch (slopConstant) {
993            default:
994                Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
995                        + slopConstant + "; using default value");
996                // fall-through
997            case TOUCH_SLOP_DEFAULT:
998                mTouchSlop = vc.getScaledTouchSlop();
999                break;
1000
1001            case TOUCH_SLOP_PAGING:
1002                mTouchSlop = vc.getScaledPagingTouchSlop();
1003                break;
1004        }
1005    }
1006
1007    /**
1008     * Swaps the current adapter with the provided one. It is similar to
1009     * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
1010     * {@link ViewHolder} and does not clear the RecycledViewPool.
1011     * <p>
1012     * Note that it still calls onAdapterChanged callbacks.
1013     *
1014     * @param adapter The new adapter to set, or null to set no adapter.
1015     * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
1016     *                                      Views. If adapters have stable ids and/or you want to
1017     *                                      animate the disappearing views, you may prefer to set
1018     *                                      this to false.
1019     * @see #setAdapter(Adapter)
1020     */
1021    public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
1022        // bail out if layout is frozen
1023        setLayoutFrozen(false);
1024        setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
1025        setDataSetChangedAfterLayout();
1026        requestLayout();
1027    }
1028    /**
1029     * Set a new adapter to provide child views on demand.
1030     * <p>
1031     * When adapter is changed, all existing views are recycled back to the pool. If the pool has
1032     * only one adapter, it will be cleared.
1033     *
1034     * @param adapter The new adapter to set, or null to set no adapter.
1035     * @see #swapAdapter(Adapter, boolean)
1036     */
1037    public void setAdapter(Adapter adapter) {
1038        // bail out if layout is frozen
1039        setLayoutFrozen(false);
1040        setAdapterInternal(adapter, false, true);
1041        requestLayout();
1042    }
1043
1044    /**
1045     * Removes and recycles all views - both those currently attached, and those in the Recycler.
1046     */
1047    void removeAndRecycleViews() {
1048        // end all running animations
1049        if (mItemAnimator != null) {
1050            mItemAnimator.endAnimations();
1051        }
1052        // Since animations are ended, mLayout.children should be equal to
1053        // recyclerView.children. This may not be true if item animator's end does not work as
1054        // expected. (e.g. not release children instantly). It is safer to use mLayout's child
1055        // count.
1056        if (mLayout != null) {
1057            mLayout.removeAndRecycleAllViews(mRecycler);
1058            mLayout.removeAndRecycleScrapInt(mRecycler);
1059        }
1060        // we should clear it here before adapters are swapped to ensure correct callbacks.
1061        mRecycler.clear();
1062    }
1063
1064    /**
1065     * Replaces the current adapter with the new one and triggers listeners.
1066     * @param adapter The new adapter
1067     * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
1068     *                               item types with the current adapter (helps us avoid cache
1069     *                               invalidation).
1070     * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
1071     *                               compatibleWithPrevious is false, this parameter is ignored.
1072     */
1073    private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
1074            boolean removeAndRecycleViews) {
1075        if (mAdapter != null) {
1076            mAdapter.unregisterAdapterDataObserver(mObserver);
1077            mAdapter.onDetachedFromRecyclerView(this);
1078        }
1079        if (!compatibleWithPrevious || removeAndRecycleViews) {
1080            removeAndRecycleViews();
1081        }
1082        mAdapterHelper.reset();
1083        final Adapter oldAdapter = mAdapter;
1084        mAdapter = adapter;
1085        if (adapter != null) {
1086            adapter.registerAdapterDataObserver(mObserver);
1087            adapter.onAttachedToRecyclerView(this);
1088        }
1089        if (mLayout != null) {
1090            mLayout.onAdapterChanged(oldAdapter, mAdapter);
1091        }
1092        mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
1093        mState.mStructureChanged = true;
1094        markKnownViewsInvalid();
1095    }
1096
1097    /**
1098     * Retrieves the previously set adapter or null if no adapter is set.
1099     *
1100     * @return The previously set adapter
1101     * @see #setAdapter(Adapter)
1102     */
1103    public Adapter getAdapter() {
1104        return mAdapter;
1105    }
1106
1107    /**
1108     * Register a listener that will be notified whenever a child view is recycled.
1109     *
1110     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1111     * that a child view is no longer needed. If an application associates expensive
1112     * or heavyweight data with item views, this may be a good place to release
1113     * or free those resources.</p>
1114     *
1115     * @param listener Listener to register, or null to clear
1116     */
1117    public void setRecyclerListener(RecyclerListener listener) {
1118        mRecyclerListener = listener;
1119    }
1120
1121    /**
1122     * <p>Return the offset of the RecyclerView's text baseline from the its top
1123     * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
1124     * this method returns -1.</p>
1125     *
1126     * @return the offset of the baseline within the RecyclerView's bounds or -1
1127     *         if baseline alignment is not supported
1128     */
1129    @Override
1130    public int getBaseline() {
1131        if (mLayout != null) {
1132            return mLayout.getBaseline();
1133        } else {
1134            return super.getBaseline();
1135        }
1136    }
1137
1138    /**
1139     * Register a listener that will be notified whenever a child view is attached to or detached
1140     * from RecyclerView.
1141     *
1142     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1143     * that a child view is no longer needed. If an application associates expensive
1144     * or heavyweight data with item views, this may be a good place to release
1145     * or free those resources.</p>
1146     *
1147     * @param listener Listener to register
1148     */
1149    public void addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1150        if (mOnChildAttachStateListeners == null) {
1151            mOnChildAttachStateListeners = new ArrayList<>();
1152        }
1153        mOnChildAttachStateListeners.add(listener);
1154    }
1155
1156    /**
1157     * Removes the provided listener from child attached state listeners list.
1158     *
1159     * @param listener Listener to unregister
1160     */
1161    public void removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1162        if (mOnChildAttachStateListeners == null) {
1163            return;
1164        }
1165        mOnChildAttachStateListeners.remove(listener);
1166    }
1167
1168    /**
1169     * Removes all listeners that were added via
1170     * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
1171     */
1172    public void clearOnChildAttachStateChangeListeners() {
1173        if (mOnChildAttachStateListeners != null) {
1174            mOnChildAttachStateListeners.clear();
1175        }
1176    }
1177
1178    /**
1179     * Set the {@link LayoutManager} that this RecyclerView will use.
1180     *
1181     * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
1182     * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
1183     * layout arrangements for child views. These arrangements are controlled by the
1184     * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
1185     *
1186     * <p>Several default strategies are provided for common uses such as lists and grids.</p>
1187     *
1188     * @param layout LayoutManager to use
1189     */
1190    public void setLayoutManager(LayoutManager layout) {
1191        if (layout == mLayout) {
1192            return;
1193        }
1194        stopScroll();
1195        // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
1196        // chance that LayoutManagers will re-use views.
1197        if (mLayout != null) {
1198            // end all running animations
1199            if (mItemAnimator != null) {
1200                mItemAnimator.endAnimations();
1201            }
1202            mLayout.removeAndRecycleAllViews(mRecycler);
1203            mLayout.removeAndRecycleScrapInt(mRecycler);
1204            mRecycler.clear();
1205
1206            if (mIsAttached) {
1207                mLayout.dispatchDetachedFromWindow(this, mRecycler);
1208            }
1209            mLayout.setRecyclerView(null);
1210            mLayout = null;
1211        } else {
1212            mRecycler.clear();
1213        }
1214        // this is just a defensive measure for faulty item animators.
1215        mChildHelper.removeAllViewsUnfiltered();
1216        mLayout = layout;
1217        if (layout != null) {
1218            if (layout.mRecyclerView != null) {
1219                throw new IllegalArgumentException("LayoutManager " + layout
1220                        + " is already attached to a RecyclerView: " + layout.mRecyclerView);
1221            }
1222            mLayout.setRecyclerView(this);
1223            if (mIsAttached) {
1224                mLayout.dispatchAttachedToWindow(this);
1225            }
1226        }
1227        mRecycler.updateViewCacheSize();
1228        requestLayout();
1229    }
1230
1231    /**
1232     * Set a {@link OnFlingListener} for this {@link RecyclerView}.
1233     * <p>
1234     * If the {@link OnFlingListener} is set then it will receive
1235     * calls to {@link #fling(int,int)} and will be able to intercept them.
1236     *
1237     * @param onFlingListener The {@link OnFlingListener} instance.
1238     */
1239    public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
1240        mOnFlingListener = onFlingListener;
1241    }
1242
1243    /**
1244     * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
1245     *
1246     * @return The {@link OnFlingListener} instance currently set (can be null).
1247     */
1248    @Nullable
1249    public OnFlingListener getOnFlingListener() {
1250        return mOnFlingListener;
1251    }
1252
1253    @Override
1254    protected Parcelable onSaveInstanceState() {
1255        SavedState state = new SavedState(super.onSaveInstanceState());
1256        if (mPendingSavedState != null) {
1257            state.copyFrom(mPendingSavedState);
1258        } else if (mLayout != null) {
1259            state.mLayoutState = mLayout.onSaveInstanceState();
1260        } else {
1261            state.mLayoutState = null;
1262        }
1263
1264        return state;
1265    }
1266
1267    @Override
1268    protected void onRestoreInstanceState(Parcelable state) {
1269        if (!(state instanceof SavedState)) {
1270            super.onRestoreInstanceState(state);
1271            return;
1272        }
1273
1274        mPendingSavedState = (SavedState) state;
1275        super.onRestoreInstanceState(mPendingSavedState.getSuperState());
1276        if (mLayout != null && mPendingSavedState.mLayoutState != null) {
1277            mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
1278        }
1279    }
1280
1281    /**
1282     * Override to prevent freezing of any views created by the adapter.
1283     */
1284    @Override
1285    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1286        dispatchFreezeSelfOnly(container);
1287    }
1288
1289    /**
1290     * Override to prevent thawing of any views created by the adapter.
1291     */
1292    @Override
1293    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1294        dispatchThawSelfOnly(container);
1295    }
1296
1297    /**
1298     * Adds a view to the animatingViews list.
1299     * mAnimatingViews holds the child views that are currently being kept around
1300     * purely for the purpose of being animated out of view. They are drawn as a regular
1301     * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
1302     * as they are managed separately from the regular child views.
1303     * @param viewHolder The ViewHolder to be removed
1304     */
1305    private void addAnimatingView(ViewHolder viewHolder) {
1306        final View view = viewHolder.itemView;
1307        final boolean alreadyParented = view.getParent() == this;
1308        mRecycler.unscrapView(getChildViewHolder(view));
1309        if (viewHolder.isTmpDetached()) {
1310            // re-attach
1311            mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
1312        } else if (!alreadyParented) {
1313            mChildHelper.addView(view, true);
1314        } else {
1315            mChildHelper.hide(view);
1316        }
1317    }
1318
1319    /**
1320     * Removes a view from the animatingViews list.
1321     * @param view The view to be removed
1322     * @see #addAnimatingView(RecyclerView.ViewHolder)
1323     * @return true if an animating view is removed
1324     */
1325    boolean removeAnimatingView(View view) {
1326        eatRequestLayout();
1327        final boolean removed = mChildHelper.removeViewIfHidden(view);
1328        if (removed) {
1329            final ViewHolder viewHolder = getChildViewHolderInt(view);
1330            mRecycler.unscrapView(viewHolder);
1331            mRecycler.recycleViewHolderInternal(viewHolder);
1332            if (DEBUG) {
1333                Log.d(TAG, "after removing animated view: " + view + ", " + this);
1334            }
1335        }
1336        // only clear request eaten flag if we removed the view.
1337        resumeRequestLayout(!removed);
1338        return removed;
1339    }
1340
1341    /**
1342     * Return the {@link LayoutManager} currently responsible for
1343     * layout policy for this RecyclerView.
1344     *
1345     * @return The currently bound LayoutManager
1346     */
1347    public LayoutManager getLayoutManager() {
1348        return mLayout;
1349    }
1350
1351    /**
1352     * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
1353     * if no pool is set for this view a new one will be created. See
1354     * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
1355     *
1356     * @return The pool used to store recycled item views for reuse.
1357     * @see #setRecycledViewPool(RecycledViewPool)
1358     */
1359    public RecycledViewPool getRecycledViewPool() {
1360        return mRecycler.getRecycledViewPool();
1361    }
1362
1363    /**
1364     * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
1365     * This can be useful if you have multiple RecyclerViews with adapters that use the same
1366     * view types, for example if you have several data sets with the same kinds of item views
1367     * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
1368     *
1369     * @param pool Pool to set. If this parameter is null a new pool will be created and used.
1370     */
1371    public void setRecycledViewPool(RecycledViewPool pool) {
1372        mRecycler.setRecycledViewPool(pool);
1373    }
1374
1375    /**
1376     * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
1377     *
1378     * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
1379     *
1380     * @see ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)
1381     */
1382    public void setViewCacheExtension(ViewCacheExtension extension) {
1383        mRecycler.setViewCacheExtension(extension);
1384    }
1385
1386    /**
1387     * Set the number of offscreen views to retain before adding them to the potentially shared
1388     * {@link #getRecycledViewPool() recycled view pool}.
1389     *
1390     * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
1391     * a LayoutManager to reuse those views unmodified without needing to return to the adapter
1392     * to rebind them.</p>
1393     *
1394     * @param size Number of views to cache offscreen before returning them to the general
1395     *             recycled view pool
1396     */
1397    public void setItemViewCacheSize(int size) {
1398        mRecycler.setViewCacheSize(size);
1399    }
1400
1401    /**
1402     * Return the current scrolling state of the RecyclerView.
1403     *
1404     * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
1405     * {@link #SCROLL_STATE_SETTLING}
1406     */
1407    public int getScrollState() {
1408        return mScrollState;
1409    }
1410
1411    void setScrollState(int state) {
1412        if (state == mScrollState) {
1413            return;
1414        }
1415        if (DEBUG) {
1416            Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
1417                    new Exception());
1418        }
1419        mScrollState = state;
1420        if (state != SCROLL_STATE_SETTLING) {
1421            stopScrollersInternal();
1422        }
1423        dispatchOnScrollStateChanged(state);
1424    }
1425
1426    /**
1427     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1428     * affect both measurement and drawing of individual item views.
1429     *
1430     * <p>Item decorations are ordered. Decorations placed earlier in the list will
1431     * be run/queried/drawn first for their effects on item views. Padding added to views
1432     * will be nested; a padding added by an earlier decoration will mean further
1433     * item decorations in the list will be asked to draw/pad within the previous decoration's
1434     * given area.</p>
1435     *
1436     * @param decor Decoration to add
1437     * @param index Position in the decoration chain to insert this decoration at. If this value
1438     *              is negative the decoration will be added at the end.
1439     */
1440    public void addItemDecoration(ItemDecoration decor, int index) {
1441        if (mLayout != null) {
1442            mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
1443                    + " layout");
1444        }
1445        if (mItemDecorations.isEmpty()) {
1446            setWillNotDraw(false);
1447        }
1448        if (index < 0) {
1449            mItemDecorations.add(decor);
1450        } else {
1451            mItemDecorations.add(index, decor);
1452        }
1453        markItemDecorInsetsDirty();
1454        requestLayout();
1455    }
1456
1457    /**
1458     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1459     * affect both measurement and drawing of individual item views.
1460     *
1461     * <p>Item decorations are ordered. Decorations placed earlier in the list will
1462     * be run/queried/drawn first for their effects on item views. Padding added to views
1463     * will be nested; a padding added by an earlier decoration will mean further
1464     * item decorations in the list will be asked to draw/pad within the previous decoration's
1465     * given area.</p>
1466     *
1467     * @param decor Decoration to add
1468     */
1469    public void addItemDecoration(ItemDecoration decor) {
1470        addItemDecoration(decor, -1);
1471    }
1472
1473    /**
1474     * Returns an {@link ItemDecoration} previously added to this RecyclerView.
1475     *
1476     * @param index The index position of the desired ItemDecoration.
1477     * @return the ItemDecoration at index position, or null if invalid index.
1478     */
1479    public ItemDecoration getItemDecorationAt(int index) {
1480        if (index < 0 || index >= mItemDecorations.size()) {
1481            return null;
1482        }
1483
1484        return mItemDecorations.get(index);
1485    }
1486
1487    /**
1488     * Remove an {@link ItemDecoration} from this RecyclerView.
1489     *
1490     * <p>The given decoration will no longer impact the measurement and drawing of
1491     * item views.</p>
1492     *
1493     * @param decor Decoration to remove
1494     * @see #addItemDecoration(ItemDecoration)
1495     */
1496    public void removeItemDecoration(ItemDecoration decor) {
1497        if (mLayout != null) {
1498            mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
1499                    + " layout");
1500        }
1501        mItemDecorations.remove(decor);
1502        if (mItemDecorations.isEmpty()) {
1503            setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
1504        }
1505        markItemDecorInsetsDirty();
1506        requestLayout();
1507    }
1508
1509    /**
1510     * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
1511     * <p>
1512     * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
1513     * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
1514     * true if childDrawingOrderCallback is not null, false otherwise.
1515     * <p>
1516     * Note that child drawing order may be overridden by View's elevation.
1517     *
1518     * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
1519     *                                  system.
1520     */
1521    public void setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback) {
1522        if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
1523            return;
1524        }
1525        mChildDrawingOrderCallback = childDrawingOrderCallback;
1526        setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
1527    }
1528
1529    /**
1530     * Set a listener that will be notified of any changes in scroll state or position.
1531     *
1532     * @param listener Listener to set or null to clear
1533     *
1534     * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
1535     *             {@link #removeOnScrollListener(OnScrollListener)}
1536     */
1537    @Deprecated
1538    public void setOnScrollListener(OnScrollListener listener) {
1539        mScrollListener = listener;
1540    }
1541
1542    /**
1543     * Add a listener that will be notified of any changes in scroll state or position.
1544     *
1545     * <p>Components that add a listener should take care to remove it when finished.
1546     * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
1547     * to remove all attached listeners.</p>
1548     *
1549     * @param listener listener to set or null to clear
1550     */
1551    public void addOnScrollListener(OnScrollListener listener) {
1552        if (mScrollListeners == null) {
1553            mScrollListeners = new ArrayList<>();
1554        }
1555        mScrollListeners.add(listener);
1556    }
1557
1558    /**
1559     * Remove a listener that was notified of any changes in scroll state or position.
1560     *
1561     * @param listener listener to set or null to clear
1562     */
1563    public void removeOnScrollListener(OnScrollListener listener) {
1564        if (mScrollListeners != null) {
1565            mScrollListeners.remove(listener);
1566        }
1567    }
1568
1569    /**
1570     * Remove all secondary listener that were notified of any changes in scroll state or position.
1571     */
1572    public void clearOnScrollListeners() {
1573        if (mScrollListeners != null) {
1574            mScrollListeners.clear();
1575        }
1576    }
1577
1578    /**
1579     * Convenience method to scroll to a certain position.
1580     *
1581     * RecyclerView does not implement scrolling logic, rather forwards the call to
1582     * {@link android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
1583     * @param position Scroll to this adapter position
1584     * @see android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)
1585     */
1586    public void scrollToPosition(int position) {
1587        if (mLayoutFrozen) {
1588            return;
1589        }
1590        stopScroll();
1591        if (mLayout == null) {
1592            Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
1593                    + "Call setLayoutManager with a non-null argument.");
1594            return;
1595        }
1596        mLayout.scrollToPosition(position);
1597        awakenScrollBars();
1598    }
1599
1600    void jumpToPositionForSmoothScroller(int position) {
1601        if (mLayout == null) {
1602            return;
1603        }
1604        mLayout.scrollToPosition(position);
1605        awakenScrollBars();
1606    }
1607
1608    /**
1609     * Starts a smooth scroll to an adapter position.
1610     * <p>
1611     * To support smooth scrolling, you must override
1612     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
1613     * {@link SmoothScroller}.
1614     * <p>
1615     * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
1616     * provide a custom smooth scroll logic, override
1617     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
1618     * LayoutManager.
1619     *
1620     * @param position The adapter position to scroll to
1621     * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
1622     */
1623    public void smoothScrollToPosition(int position) {
1624        if (mLayoutFrozen) {
1625            return;
1626        }
1627        if (mLayout == null) {
1628            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
1629                    + "Call setLayoutManager with a non-null argument.");
1630            return;
1631        }
1632        mLayout.smoothScrollToPosition(this, mState, position);
1633    }
1634
1635    @Override
1636    public void scrollTo(int x, int y) {
1637        Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
1638                + "Use scrollToPosition instead");
1639    }
1640
1641    @Override
1642    public void scrollBy(int x, int y) {
1643        if (mLayout == null) {
1644            Log.e(TAG, "Cannot scroll without a LayoutManager set. "
1645                    + "Call setLayoutManager with a non-null argument.");
1646            return;
1647        }
1648        if (mLayoutFrozen) {
1649            return;
1650        }
1651        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1652        final boolean canScrollVertical = mLayout.canScrollVertically();
1653        if (canScrollHorizontal || canScrollVertical) {
1654            scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
1655        }
1656    }
1657
1658    /**
1659     * Helper method reflect data changes to the state.
1660     * <p>
1661     * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
1662     * but data actually changed.
1663     * <p>
1664     * This method consumes all deferred changes to avoid that case.
1665     */
1666    void consumePendingUpdateOperations() {
1667        if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
1668            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1669            dispatchLayout();
1670            TraceCompat.endSection();
1671            return;
1672        }
1673        if (!mAdapterHelper.hasPendingUpdates()) {
1674            return;
1675        }
1676
1677        // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
1678        // of the visible items is affected and if not, just ignore the change.
1679        if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
1680                .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
1681                        | AdapterHelper.UpdateOp.MOVE)) {
1682            TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
1683            eatRequestLayout();
1684            onEnterLayoutOrScroll();
1685            mAdapterHelper.preProcess();
1686            if (!mLayoutRequestEaten) {
1687                if (hasUpdatedView()) {
1688                    dispatchLayout();
1689                } else {
1690                    // no need to layout, clean state
1691                    mAdapterHelper.consumePostponedUpdates();
1692                }
1693            }
1694            resumeRequestLayout(true);
1695            onExitLayoutOrScroll();
1696            TraceCompat.endSection();
1697        } else if (mAdapterHelper.hasPendingUpdates()) {
1698            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1699            dispatchLayout();
1700            TraceCompat.endSection();
1701        }
1702    }
1703
1704    /**
1705     * @return True if an existing view holder needs to be updated
1706     */
1707    private boolean hasUpdatedView() {
1708        final int childCount = mChildHelper.getChildCount();
1709        for (int i = 0; i < childCount; i++) {
1710            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
1711            if (holder == null || holder.shouldIgnore()) {
1712                continue;
1713            }
1714            if (holder.isUpdated()) {
1715                return true;
1716            }
1717        }
1718        return false;
1719    }
1720
1721    /**
1722     * Does not perform bounds checking. Used by internal methods that have already validated input.
1723     * <p>
1724     * It also reports any unused scroll request to the related EdgeEffect.
1725     *
1726     * @param x The amount of horizontal scroll request
1727     * @param y The amount of vertical scroll request
1728     * @param ev The originating MotionEvent, or null if not from a touch event.
1729     *
1730     * @return Whether any scroll was consumed in either direction.
1731     */
1732    boolean scrollByInternal(int x, int y, MotionEvent ev) {
1733        int unconsumedX = 0, unconsumedY = 0;
1734        int consumedX = 0, consumedY = 0;
1735
1736        consumePendingUpdateOperations();
1737        if (mAdapter != null) {
1738            eatRequestLayout();
1739            onEnterLayoutOrScroll();
1740            TraceCompat.beginSection(TRACE_SCROLL_TAG);
1741            if (x != 0) {
1742                consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
1743                unconsumedX = x - consumedX;
1744            }
1745            if (y != 0) {
1746                consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
1747                unconsumedY = y - consumedY;
1748            }
1749            TraceCompat.endSection();
1750            repositionShadowingViews();
1751            onExitLayoutOrScroll();
1752            resumeRequestLayout(false);
1753        }
1754        if (!mItemDecorations.isEmpty()) {
1755            invalidate();
1756        }
1757
1758        if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset,
1759                TYPE_TOUCH)) {
1760            // Update the last touch co-ords, taking any scroll offset into account
1761            mLastTouchX -= mScrollOffset[0];
1762            mLastTouchY -= mScrollOffset[1];
1763            if (ev != null) {
1764                ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
1765            }
1766            mNestedOffsets[0] += mScrollOffset[0];
1767            mNestedOffsets[1] += mScrollOffset[1];
1768        } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
1769            if (ev != null && !MotionEventCompat.isFromSource(ev, InputDevice.SOURCE_MOUSE)) {
1770                pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
1771            }
1772            considerReleasingGlowsOnScroll(x, y);
1773        }
1774        if (consumedX != 0 || consumedY != 0) {
1775            dispatchOnScrolled(consumedX, consumedY);
1776        }
1777        if (!awakenScrollBars()) {
1778            invalidate();
1779        }
1780        return consumedX != 0 || consumedY != 0;
1781    }
1782
1783    /**
1784     * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
1785     * range. This value is used to compute the length of the thumb within the scrollbar's track.
1786     * </p>
1787     *
1788     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1789     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
1790     *
1791     * <p>Default implementation returns 0.</p>
1792     *
1793     * <p>If you want to support scroll bars, override
1794     * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
1795     * LayoutManager. </p>
1796     *
1797     * @return The horizontal offset of the scrollbar's thumb
1798     * @see android.support.v7.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset
1799     * (RecyclerView.State)
1800     */
1801    @Override
1802    public int computeHorizontalScrollOffset() {
1803        if (mLayout == null) {
1804            return 0;
1805        }
1806        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
1807    }
1808
1809    /**
1810     * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
1811     * horizontal range. This value is used to compute the length of the thumb within the
1812     * scrollbar's track.</p>
1813     *
1814     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1815     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
1816     *
1817     * <p>Default implementation returns 0.</p>
1818     *
1819     * <p>If you want to support scroll bars, override
1820     * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
1821     * LayoutManager.</p>
1822     *
1823     * @return The horizontal extent of the scrollbar's thumb
1824     * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
1825     */
1826    @Override
1827    public int computeHorizontalScrollExtent() {
1828        if (mLayout == null) {
1829            return 0;
1830        }
1831        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
1832    }
1833
1834    /**
1835     * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
1836     *
1837     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1838     * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
1839     *
1840     * <p>Default implementation returns 0.</p>
1841     *
1842     * <p>If you want to support scroll bars, override
1843     * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
1844     * LayoutManager.</p>
1845     *
1846     * @return The total horizontal range represented by the vertical scrollbar
1847     * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
1848     */
1849    @Override
1850    public int computeHorizontalScrollRange() {
1851        if (mLayout == null) {
1852            return 0;
1853        }
1854        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
1855    }
1856
1857    /**
1858     * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
1859     * This value is used to compute the length of the thumb within the scrollbar's track. </p>
1860     *
1861     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1862     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
1863     *
1864     * <p>Default implementation returns 0.</p>
1865     *
1866     * <p>If you want to support scroll bars, override
1867     * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
1868     * LayoutManager.</p>
1869     *
1870     * @return The vertical offset of the scrollbar's thumb
1871     * @see android.support.v7.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset
1872     * (RecyclerView.State)
1873     */
1874    @Override
1875    public int computeVerticalScrollOffset() {
1876        if (mLayout == null) {
1877            return 0;
1878        }
1879        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
1880    }
1881
1882    /**
1883     * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
1884     * This value is used to compute the length of the thumb within the scrollbar's track.</p>
1885     *
1886     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1887     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
1888     *
1889     * <p>Default implementation returns 0.</p>
1890     *
1891     * <p>If you want to support scroll bars, override
1892     * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
1893     * LayoutManager.</p>
1894     *
1895     * @return The vertical extent of the scrollbar's thumb
1896     * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
1897     */
1898    @Override
1899    public int computeVerticalScrollExtent() {
1900        if (mLayout == null) {
1901            return 0;
1902        }
1903        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
1904    }
1905
1906    /**
1907     * <p>Compute the vertical range that the vertical scrollbar represents.</p>
1908     *
1909     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1910     * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
1911     *
1912     * <p>Default implementation returns 0.</p>
1913     *
1914     * <p>If you want to support scroll bars, override
1915     * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
1916     * LayoutManager.</p>
1917     *
1918     * @return The total vertical range represented by the vertical scrollbar
1919     * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
1920     */
1921    @Override
1922    public int computeVerticalScrollRange() {
1923        if (mLayout == null) {
1924            return 0;
1925        }
1926        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
1927    }
1928
1929
1930    void eatRequestLayout() {
1931        mEatRequestLayout++;
1932        if (mEatRequestLayout == 1 && !mLayoutFrozen) {
1933            mLayoutRequestEaten = false;
1934        }
1935    }
1936
1937    void resumeRequestLayout(boolean performLayoutChildren) {
1938        if (mEatRequestLayout < 1) {
1939            //noinspection PointlessBooleanExpression
1940            if (DEBUG) {
1941                throw new IllegalStateException("invalid eat request layout count");
1942            }
1943            mEatRequestLayout = 1;
1944        }
1945        if (!performLayoutChildren) {
1946            // Reset the layout request eaten counter.
1947            // This is necessary since eatRequest calls can be nested in which case the other
1948            // call will override the inner one.
1949            // for instance:
1950            // eat layout for process adapter updates
1951            //   eat layout for dispatchLayout
1952            //     a bunch of req layout calls arrive
1953
1954            mLayoutRequestEaten = false;
1955        }
1956        if (mEatRequestLayout == 1) {
1957            // when layout is frozen we should delay dispatchLayout()
1958            if (performLayoutChildren && mLayoutRequestEaten && !mLayoutFrozen
1959                    && mLayout != null && mAdapter != null) {
1960                dispatchLayout();
1961            }
1962            if (!mLayoutFrozen) {
1963                mLayoutRequestEaten = false;
1964            }
1965        }
1966        mEatRequestLayout--;
1967    }
1968
1969    /**
1970     * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
1971     * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
1972     * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
1973     * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
1974     * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
1975     * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
1976     * called.
1977     *
1978     * <p>
1979     * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
1980     * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
1981     * RecyclerView, State, int)}.
1982     * <p>
1983     * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
1984     * stop frozen.
1985     * <p>
1986     * Note: Running ItemAnimator is not stopped automatically,  it's caller's
1987     * responsibility to call ItemAnimator.end().
1988     *
1989     * @param frozen   true to freeze layout and scroll, false to re-enable.
1990     */
1991    public void setLayoutFrozen(boolean frozen) {
1992        if (frozen != mLayoutFrozen) {
1993            assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
1994            if (!frozen) {
1995                mLayoutFrozen = false;
1996                if (mLayoutRequestEaten && mLayout != null && mAdapter != null) {
1997                    requestLayout();
1998                }
1999                mLayoutRequestEaten = false;
2000            } else {
2001                final long now = SystemClock.uptimeMillis();
2002                MotionEvent cancelEvent = MotionEvent.obtain(now, now,
2003                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2004                onTouchEvent(cancelEvent);
2005                mLayoutFrozen = true;
2006                mIgnoreMotionEventTillDown = true;
2007                stopScroll();
2008            }
2009        }
2010    }
2011
2012    /**
2013     * Returns true if layout and scroll are frozen.
2014     *
2015     * @return true if layout and scroll are frozen
2016     * @see #setLayoutFrozen(boolean)
2017     */
2018    public boolean isLayoutFrozen() {
2019        return mLayoutFrozen;
2020    }
2021
2022    /**
2023     * Animate a scroll by the given amount of pixels along either axis.
2024     *
2025     * @param dx Pixels to scroll horizontally
2026     * @param dy Pixels to scroll vertically
2027     */
2028    public void smoothScrollBy(int dx, int dy) {
2029        smoothScrollBy(dx, dy, null);
2030    }
2031
2032    /**
2033     * Animate a scroll by the given amount of pixels along either axis.
2034     *
2035     * @param dx Pixels to scroll horizontally
2036     * @param dy Pixels to scroll vertically
2037     * @param interpolator {@link Interpolator} to be used for scrolling. If it is
2038     *                     {@code null}, RecyclerView is going to use the default interpolator.
2039     */
2040    public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
2041        if (mLayout == null) {
2042            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
2043                    + "Call setLayoutManager with a non-null argument.");
2044            return;
2045        }
2046        if (mLayoutFrozen) {
2047            return;
2048        }
2049        if (!mLayout.canScrollHorizontally()) {
2050            dx = 0;
2051        }
2052        if (!mLayout.canScrollVertically()) {
2053            dy = 0;
2054        }
2055        if (dx != 0 || dy != 0) {
2056            mViewFlinger.smoothScrollBy(dx, dy, interpolator);
2057        }
2058    }
2059
2060    /**
2061     * Begin a standard fling with an initial velocity along each axis in pixels per second.
2062     * If the velocity given is below the system-defined minimum this method will return false
2063     * and no fling will occur.
2064     *
2065     * @param velocityX Initial horizontal velocity in pixels per second
2066     * @param velocityY Initial vertical velocity in pixels per second
2067     * @return true if the fling was started, false if the velocity was too low to fling or
2068     * LayoutManager does not support scrolling in the axis fling is issued.
2069     *
2070     * @see LayoutManager#canScrollVertically()
2071     * @see LayoutManager#canScrollHorizontally()
2072     */
2073    public boolean fling(int velocityX, int velocityY) {
2074        if (mLayout == null) {
2075            Log.e(TAG, "Cannot fling without a LayoutManager set. "
2076                    + "Call setLayoutManager with a non-null argument.");
2077            return false;
2078        }
2079        if (mLayoutFrozen) {
2080            return false;
2081        }
2082
2083        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
2084        final boolean canScrollVertical = mLayout.canScrollVertically();
2085
2086        if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
2087            velocityX = 0;
2088        }
2089        if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
2090            velocityY = 0;
2091        }
2092        if (velocityX == 0 && velocityY == 0) {
2093            // If we don't have any velocity, return false
2094            return false;
2095        }
2096
2097        if (!dispatchNestedPreFling(velocityX, velocityY)) {
2098            final boolean canScroll = canScrollHorizontal || canScrollVertical;
2099            dispatchNestedFling(velocityX, velocityY, canScroll);
2100
2101            if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
2102                return true;
2103            }
2104
2105            if (canScroll) {
2106                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2107                if (canScrollHorizontal) {
2108                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2109                }
2110                if (canScrollVertical) {
2111                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2112                }
2113                startNestedScroll(nestedScrollAxis, TYPE_NON_TOUCH);
2114
2115                velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
2116                velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
2117                mViewFlinger.fling(velocityX, velocityY);
2118                return true;
2119            }
2120        }
2121        return false;
2122    }
2123
2124    /**
2125     * Stop any current scroll in progress, such as one started by
2126     * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
2127     */
2128    public void stopScroll() {
2129        setScrollState(SCROLL_STATE_IDLE);
2130        stopScrollersInternal();
2131    }
2132
2133    /**
2134     * Similar to {@link #stopScroll()} but does not set the state.
2135     */
2136    private void stopScrollersInternal() {
2137        mViewFlinger.stop();
2138        if (mLayout != null) {
2139            mLayout.stopSmoothScroller();
2140        }
2141    }
2142
2143    /**
2144     * Returns the minimum velocity to start a fling.
2145     *
2146     * @return The minimum velocity to start a fling
2147     */
2148    public int getMinFlingVelocity() {
2149        return mMinFlingVelocity;
2150    }
2151
2152
2153    /**
2154     * Returns the maximum fling velocity used by this RecyclerView.
2155     *
2156     * @return The maximum fling velocity used by this RecyclerView.
2157     */
2158    public int getMaxFlingVelocity() {
2159        return mMaxFlingVelocity;
2160    }
2161
2162    /**
2163     * Apply a pull to relevant overscroll glow effects
2164     */
2165    private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
2166        boolean invalidate = false;
2167        if (overscrollX < 0) {
2168            ensureLeftGlow();
2169            EdgeEffectCompat.onPull(mLeftGlow, -overscrollX / getWidth(), 1f - y  / getHeight());
2170            invalidate = true;
2171        } else if (overscrollX > 0) {
2172            ensureRightGlow();
2173            EdgeEffectCompat.onPull(mRightGlow, overscrollX / getWidth(), y / getHeight());
2174            invalidate = true;
2175        }
2176
2177        if (overscrollY < 0) {
2178            ensureTopGlow();
2179            EdgeEffectCompat.onPull(mTopGlow, -overscrollY / getHeight(), x / getWidth());
2180            invalidate = true;
2181        } else if (overscrollY > 0) {
2182            ensureBottomGlow();
2183            EdgeEffectCompat.onPull(mBottomGlow, overscrollY / getHeight(), 1f - x / getWidth());
2184            invalidate = true;
2185        }
2186
2187        if (invalidate || overscrollX != 0 || overscrollY != 0) {
2188            ViewCompat.postInvalidateOnAnimation(this);
2189        }
2190    }
2191
2192    private void releaseGlows() {
2193        boolean needsInvalidate = false;
2194        if (mLeftGlow != null) {
2195            mLeftGlow.onRelease();
2196            needsInvalidate = mLeftGlow.isFinished();
2197        }
2198        if (mTopGlow != null) {
2199            mTopGlow.onRelease();
2200            needsInvalidate |= mTopGlow.isFinished();
2201        }
2202        if (mRightGlow != null) {
2203            mRightGlow.onRelease();
2204            needsInvalidate |= mRightGlow.isFinished();
2205        }
2206        if (mBottomGlow != null) {
2207            mBottomGlow.onRelease();
2208            needsInvalidate |= mBottomGlow.isFinished();
2209        }
2210        if (needsInvalidate) {
2211            ViewCompat.postInvalidateOnAnimation(this);
2212        }
2213    }
2214
2215    void considerReleasingGlowsOnScroll(int dx, int dy) {
2216        boolean needsInvalidate = false;
2217        if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
2218            mLeftGlow.onRelease();
2219            needsInvalidate = mLeftGlow.isFinished();
2220        }
2221        if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
2222            mRightGlow.onRelease();
2223            needsInvalidate |= mRightGlow.isFinished();
2224        }
2225        if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
2226            mTopGlow.onRelease();
2227            needsInvalidate |= mTopGlow.isFinished();
2228        }
2229        if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
2230            mBottomGlow.onRelease();
2231            needsInvalidate |= mBottomGlow.isFinished();
2232        }
2233        if (needsInvalidate) {
2234            ViewCompat.postInvalidateOnAnimation(this);
2235        }
2236    }
2237
2238    void absorbGlows(int velocityX, int velocityY) {
2239        if (velocityX < 0) {
2240            ensureLeftGlow();
2241            mLeftGlow.onAbsorb(-velocityX);
2242        } else if (velocityX > 0) {
2243            ensureRightGlow();
2244            mRightGlow.onAbsorb(velocityX);
2245        }
2246
2247        if (velocityY < 0) {
2248            ensureTopGlow();
2249            mTopGlow.onAbsorb(-velocityY);
2250        } else if (velocityY > 0) {
2251            ensureBottomGlow();
2252            mBottomGlow.onAbsorb(velocityY);
2253        }
2254
2255        if (velocityX != 0 || velocityY != 0) {
2256            ViewCompat.postInvalidateOnAnimation(this);
2257        }
2258    }
2259
2260    void ensureLeftGlow() {
2261        if (mLeftGlow != null) {
2262            return;
2263        }
2264        mLeftGlow = new EdgeEffect(getContext());
2265        if (mClipToPadding) {
2266            mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2267                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2268        } else {
2269            mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2270        }
2271    }
2272
2273    void ensureRightGlow() {
2274        if (mRightGlow != null) {
2275            return;
2276        }
2277        mRightGlow = new EdgeEffect(getContext());
2278        if (mClipToPadding) {
2279            mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2280                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2281        } else {
2282            mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2283        }
2284    }
2285
2286    void ensureTopGlow() {
2287        if (mTopGlow != null) {
2288            return;
2289        }
2290        mTopGlow = new EdgeEffect(getContext());
2291        if (mClipToPadding) {
2292            mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2293                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2294        } else {
2295            mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2296        }
2297
2298    }
2299
2300    void ensureBottomGlow() {
2301        if (mBottomGlow != null) {
2302            return;
2303        }
2304        mBottomGlow = new EdgeEffect(getContext());
2305        if (mClipToPadding) {
2306            mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2307                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2308        } else {
2309            mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2310        }
2311    }
2312
2313    void invalidateGlows() {
2314        mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
2315    }
2316
2317    /**
2318     * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
2319     * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
2320     * that differs from other ViewGroups.
2321     * <p>
2322     * It first does a focus search within the RecyclerView. If this search finds a View that is in
2323     * the focus direction with respect to the currently focused View, RecyclerView returns that
2324     * child as the next focus target. When it cannot find such child, it calls
2325     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
2326     * in the focus search direction. If LayoutManager adds a View that matches the
2327     * focus search criteria, it will be returned as the focus search result. Otherwise,
2328     * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
2329     * <p>
2330     * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
2331     * is not in the focus direction is still valid focus target which may not be the desired
2332     * behavior if the Adapter has more children in the focus direction. To handle this case,
2333     * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
2334     * focus search in that direction. If there are no Views to gain focus, it will call
2335     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
2336     * focus search with the original (relative) direction. This allows RecyclerView to provide
2337     * better candidates to the focus search while still allowing the view system to take focus from
2338     * the RecyclerView and give it to a more suitable child if such child exists.
2339     *
2340     * @param focused The view that currently has focus
2341     * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
2342     * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
2343     * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
2344     *
2345     * @return A new View that can be the next focus after the focused View
2346     */
2347    @Override
2348    public View focusSearch(View focused, int direction) {
2349        View result = mLayout.onInterceptFocusSearch(focused, direction);
2350        if (result != null) {
2351            return result;
2352        }
2353        final boolean canRunFocusFailure = mAdapter != null && mLayout != null
2354                && !isComputingLayout() && !mLayoutFrozen;
2355
2356        final FocusFinder ff = FocusFinder.getInstance();
2357        if (canRunFocusFailure
2358                && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
2359            // convert direction to absolute direction and see if we have a view there and if not
2360            // tell LayoutManager to add if it can.
2361            boolean needsFocusFailureLayout = false;
2362            if (mLayout.canScrollVertically()) {
2363                final int absDir =
2364                        direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
2365                final View found = ff.findNextFocus(this, focused, absDir);
2366                needsFocusFailureLayout = found == null;
2367                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2368                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2369                    direction = absDir;
2370                }
2371            }
2372            if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
2373                boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
2374                final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
2375                        ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2376                final View found = ff.findNextFocus(this, focused, absDir);
2377                needsFocusFailureLayout = found == null;
2378                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2379                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2380                    direction = absDir;
2381                }
2382            }
2383            if (needsFocusFailureLayout) {
2384                consumePendingUpdateOperations();
2385                final View focusedItemView = findContainingItemView(focused);
2386                if (focusedItemView == null) {
2387                    // panic, focused view is not a child anymore, cannot call super.
2388                    return null;
2389                }
2390                eatRequestLayout();
2391                mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2392                resumeRequestLayout(false);
2393            }
2394            result = ff.findNextFocus(this, focused, direction);
2395        } else {
2396            result = ff.findNextFocus(this, focused, direction);
2397            if (result == null && canRunFocusFailure) {
2398                consumePendingUpdateOperations();
2399                final View focusedItemView = findContainingItemView(focused);
2400                if (focusedItemView == null) {
2401                    // panic, focused view is not a child anymore, cannot call super.
2402                    return null;
2403                }
2404                eatRequestLayout();
2405                result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2406                resumeRequestLayout(false);
2407            }
2408        }
2409        if (result != null && !result.hasFocusable()) {
2410            if (getFocusedChild() == null) {
2411                // Scrolling to this unfocusable view is not meaningful since there is no currently
2412                // focused view which RV needs to keep visible.
2413                return super.focusSearch(focused, direction);
2414            }
2415            // If the next view returned by onFocusSearchFailed in layout manager has no focusable
2416            // views, we still scroll to that view in order to make it visible on the screen.
2417            // If it's focusable, framework already calls RV's requestChildFocus which handles
2418            // bringing this newly focused item onto the screen.
2419            requestChildOnScreen(result, null);
2420            return focused;
2421        }
2422        return isPreferredNextFocus(focused, result, direction)
2423                ? result : super.focusSearch(focused, direction);
2424    }
2425
2426    /**
2427     * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
2428     * assign it as the next focus View instead of letting view hierarchy decide.
2429     * A good candidate means a View that is aligned in the focus direction wrt the focused View
2430     * and is not the RecyclerView itself.
2431     * When this method returns false, RecyclerView will let the parent make the decision so the
2432     * same View may still get the focus as a result of that search.
2433     */
2434    private boolean isPreferredNextFocus(View focused, View next, int direction) {
2435        if (next == null || next == this) {
2436            return false;
2437        }
2438        if (focused == null) {
2439            return true;
2440        }
2441
2442        if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
2443            final boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
2444            final int absHorizontal = (direction == View.FOCUS_FORWARD) ^ rtl
2445                    ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2446            if (isPreferredNextFocusAbsolute(focused, next, absHorizontal)) {
2447                return true;
2448            }
2449            if (direction == View.FOCUS_FORWARD) {
2450                return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_DOWN);
2451            } else {
2452                return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_UP);
2453            }
2454        } else {
2455            return isPreferredNextFocusAbsolute(focused, next, direction);
2456        }
2457
2458    }
2459
2460    /**
2461     * Logic taken from FocusSearch#isCandidate
2462     */
2463    private boolean isPreferredNextFocusAbsolute(View focused, View next, int direction) {
2464        mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2465        mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
2466        offsetDescendantRectToMyCoords(focused, mTempRect);
2467        offsetDescendantRectToMyCoords(next, mTempRect2);
2468        switch (direction) {
2469            case View.FOCUS_LEFT:
2470                return (mTempRect.right > mTempRect2.right
2471                        || mTempRect.left >= mTempRect2.right)
2472                        && mTempRect.left > mTempRect2.left;
2473            case View.FOCUS_RIGHT:
2474                return (mTempRect.left < mTempRect2.left
2475                        || mTempRect.right <= mTempRect2.left)
2476                        && mTempRect.right < mTempRect2.right;
2477            case View.FOCUS_UP:
2478                return (mTempRect.bottom > mTempRect2.bottom
2479                        || mTempRect.top >= mTempRect2.bottom)
2480                        && mTempRect.top > mTempRect2.top;
2481            case View.FOCUS_DOWN:
2482                return (mTempRect.top < mTempRect2.top
2483                        || mTempRect.bottom <= mTempRect2.top)
2484                        && mTempRect.bottom < mTempRect2.bottom;
2485        }
2486        throw new IllegalArgumentException("direction must be absolute. received:" + direction);
2487    }
2488
2489    @Override
2490    public void requestChildFocus(View child, View focused) {
2491        if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
2492            requestChildOnScreen(child, focused);
2493        }
2494        super.requestChildFocus(child, focused);
2495    }
2496
2497    /**
2498     * Requests that the given child of the RecyclerView be positioned onto the screen. This method
2499     * can be called for both unfocusable and focusable child views. For unfocusable child views,
2500     * the {@param focused} parameter passed is null, whereas for a focusable child, this parameter
2501     * indicates the actual descendant view within this child view that holds the focus.
2502     * @param child The child view of this RecyclerView that wants to come onto the screen.
2503     * @param focused The descendant view that actually has the focus if child is focusable, null
2504     *                otherwise.
2505     */
2506    private void requestChildOnScreen(@NonNull View child, @Nullable View focused) {
2507        View rectView = (focused != null) ? focused : child;
2508        mTempRect.set(0, 0, rectView.getWidth(), rectView.getHeight());
2509
2510        // get item decor offsets w/o refreshing. If they are invalid, there will be another
2511        // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
2512        // View in viewport.
2513        final ViewGroup.LayoutParams focusedLayoutParams = rectView.getLayoutParams();
2514        if (focusedLayoutParams instanceof LayoutParams) {
2515            // if focused child has item decors, use them. Otherwise, ignore.
2516            final LayoutParams lp = (LayoutParams) focusedLayoutParams;
2517            if (!lp.mInsetsDirty) {
2518                final Rect insets = lp.mDecorInsets;
2519                mTempRect.left -= insets.left;
2520                mTempRect.right += insets.right;
2521                mTempRect.top -= insets.top;
2522                mTempRect.bottom += insets.bottom;
2523            }
2524        }
2525
2526        if (focused != null) {
2527            offsetDescendantRectToMyCoords(focused, mTempRect);
2528            offsetRectIntoDescendantCoords(child, mTempRect);
2529        }
2530        mLayout.requestChildRectangleOnScreen(this, child, mTempRect, !mFirstLayoutComplete,
2531                (focused == null));
2532    }
2533
2534    @Override
2535    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
2536        return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
2537    }
2538
2539    @Override
2540    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
2541        if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
2542            super.addFocusables(views, direction, focusableMode);
2543        }
2544    }
2545
2546    @Override
2547    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
2548        if (isComputingLayout()) {
2549            // if we are in the middle of a layout calculation, don't let any child take focus.
2550            // RV will handle it after layout calculation is finished.
2551            return false;
2552        }
2553        return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
2554    }
2555
2556    @Override
2557    protected void onAttachedToWindow() {
2558        super.onAttachedToWindow();
2559        mLayoutOrScrollCounter = 0;
2560        mIsAttached = true;
2561        mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
2562        if (mLayout != null) {
2563            mLayout.dispatchAttachedToWindow(this);
2564        }
2565        mPostedAnimatorRunner = false;
2566
2567        if (ALLOW_THREAD_GAP_WORK) {
2568            // Register with gap worker
2569            mGapWorker = GapWorker.sGapWorker.get();
2570            if (mGapWorker == null) {
2571                mGapWorker = new GapWorker();
2572
2573                // break 60 fps assumption if data from display appears valid
2574                // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
2575                Display display = ViewCompat.getDisplay(this);
2576                float refreshRate = 60.0f;
2577                if (!isInEditMode() && display != null) {
2578                    float displayRefreshRate = display.getRefreshRate();
2579                    if (displayRefreshRate >= 30.0f) {
2580                        refreshRate = displayRefreshRate;
2581                    }
2582                }
2583                mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
2584                GapWorker.sGapWorker.set(mGapWorker);
2585            }
2586            mGapWorker.add(this);
2587        }
2588    }
2589
2590    @Override
2591    protected void onDetachedFromWindow() {
2592        super.onDetachedFromWindow();
2593        if (mItemAnimator != null) {
2594            mItemAnimator.endAnimations();
2595        }
2596        stopScroll();
2597        mIsAttached = false;
2598        if (mLayout != null) {
2599            mLayout.dispatchDetachedFromWindow(this, mRecycler);
2600        }
2601        mPendingAccessibilityImportanceChange.clear();
2602        removeCallbacks(mItemAnimatorRunner);
2603        mViewInfoStore.onDetach();
2604
2605        if (ALLOW_THREAD_GAP_WORK) {
2606            // Unregister with gap worker
2607            mGapWorker.remove(this);
2608            mGapWorker = null;
2609        }
2610    }
2611
2612    /**
2613     * Returns true if RecyclerView is attached to window.
2614     */
2615    @Override
2616    public boolean isAttachedToWindow() {
2617        return mIsAttached;
2618    }
2619
2620    /**
2621     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2622     * {@link IllegalStateException} if it <b>is not</b>.
2623     *
2624     * @param message The message for the exception. Can be null.
2625     * @see #assertNotInLayoutOrScroll(String)
2626     */
2627    void assertInLayoutOrScroll(String message) {
2628        if (!isComputingLayout()) {
2629            if (message == null) {
2630                throw new IllegalStateException("Cannot call this method unless RecyclerView is "
2631                        + "computing a layout or scrolling");
2632            }
2633            throw new IllegalStateException(message);
2634
2635        }
2636    }
2637
2638    /**
2639     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2640     * {@link IllegalStateException} if it <b>is</b>.
2641     *
2642     * @param message The message for the exception. Can be null.
2643     * @see #assertInLayoutOrScroll(String)
2644     */
2645    void assertNotInLayoutOrScroll(String message) {
2646        if (isComputingLayout()) {
2647            if (message == null) {
2648                throw new IllegalStateException("Cannot call this method while RecyclerView is "
2649                        + "computing a layout or scrolling");
2650            }
2651            throw new IllegalStateException(message);
2652        }
2653        if (mDispatchScrollCounter > 0) {
2654            Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might"
2655                            + "be run during a measure & layout pass where you cannot change the"
2656                            + "RecyclerView data. Any method call that might change the structure"
2657                            + "of the RecyclerView or the adapter contents should be postponed to"
2658                            + "the next frame.",
2659                    new IllegalStateException(""));
2660        }
2661    }
2662
2663    /**
2664     * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
2665     * to child views or this view's standard scrolling behavior.
2666     *
2667     * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
2668     * returns true from
2669     * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
2670     * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
2671     * for each incoming MotionEvent until the end of the gesture.</p>
2672     *
2673     * @param listener Listener to add
2674     * @see SimpleOnItemTouchListener
2675     */
2676    public void addOnItemTouchListener(OnItemTouchListener listener) {
2677        mOnItemTouchListeners.add(listener);
2678    }
2679
2680    /**
2681     * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
2682     *
2683     * @param listener Listener to remove
2684     */
2685    public void removeOnItemTouchListener(OnItemTouchListener listener) {
2686        mOnItemTouchListeners.remove(listener);
2687        if (mActiveOnItemTouchListener == listener) {
2688            mActiveOnItemTouchListener = null;
2689        }
2690    }
2691
2692    private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
2693        final int action = e.getAction();
2694        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
2695            mActiveOnItemTouchListener = null;
2696        }
2697
2698        final int listenerCount = mOnItemTouchListeners.size();
2699        for (int i = 0; i < listenerCount; i++) {
2700            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2701            if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
2702                mActiveOnItemTouchListener = listener;
2703                return true;
2704            }
2705        }
2706        return false;
2707    }
2708
2709    private boolean dispatchOnItemTouch(MotionEvent e) {
2710        final int action = e.getAction();
2711        if (mActiveOnItemTouchListener != null) {
2712            if (action == MotionEvent.ACTION_DOWN) {
2713                // Stale state from a previous gesture, we're starting a new one. Clear it.
2714                mActiveOnItemTouchListener = null;
2715            } else {
2716                mActiveOnItemTouchListener.onTouchEvent(this, e);
2717                if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
2718                    // Clean up for the next gesture.
2719                    mActiveOnItemTouchListener = null;
2720                }
2721                return true;
2722            }
2723        }
2724
2725        // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
2726        // as called from onInterceptTouchEvent; skip it.
2727        if (action != MotionEvent.ACTION_DOWN) {
2728            final int listenerCount = mOnItemTouchListeners.size();
2729            for (int i = 0; i < listenerCount; i++) {
2730                final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2731                if (listener.onInterceptTouchEvent(this, e)) {
2732                    mActiveOnItemTouchListener = listener;
2733                    return true;
2734                }
2735            }
2736        }
2737        return false;
2738    }
2739
2740    @Override
2741    public boolean onInterceptTouchEvent(MotionEvent e) {
2742        if (mLayoutFrozen) {
2743            // When layout is frozen,  RV does not intercept the motion event.
2744            // A child view e.g. a button may still get the click.
2745            return false;
2746        }
2747        if (dispatchOnItemTouchIntercept(e)) {
2748            cancelTouch();
2749            return true;
2750        }
2751
2752        if (mLayout == null) {
2753            return false;
2754        }
2755
2756        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2757        final boolean canScrollVertically = mLayout.canScrollVertically();
2758
2759        if (mVelocityTracker == null) {
2760            mVelocityTracker = VelocityTracker.obtain();
2761        }
2762        mVelocityTracker.addMovement(e);
2763
2764        final int action = e.getActionMasked();
2765        final int actionIndex = e.getActionIndex();
2766
2767        switch (action) {
2768            case MotionEvent.ACTION_DOWN:
2769                if (mIgnoreMotionEventTillDown) {
2770                    mIgnoreMotionEventTillDown = false;
2771                }
2772                mScrollPointerId = e.getPointerId(0);
2773                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2774                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2775
2776                if (mScrollState == SCROLL_STATE_SETTLING) {
2777                    getParent().requestDisallowInterceptTouchEvent(true);
2778                    setScrollState(SCROLL_STATE_DRAGGING);
2779                }
2780
2781                // Clear the nested offsets
2782                mNestedOffsets[0] = mNestedOffsets[1] = 0;
2783
2784                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2785                if (canScrollHorizontally) {
2786                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2787                }
2788                if (canScrollVertically) {
2789                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2790                }
2791                startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
2792                break;
2793
2794            case MotionEvent.ACTION_POINTER_DOWN:
2795                mScrollPointerId = e.getPointerId(actionIndex);
2796                mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2797                mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2798                break;
2799
2800            case MotionEvent.ACTION_MOVE: {
2801                final int index = e.findPointerIndex(mScrollPointerId);
2802                if (index < 0) {
2803                    Log.e(TAG, "Error processing scroll; pointer index for id "
2804                            + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2805                    return false;
2806                }
2807
2808                final int x = (int) (e.getX(index) + 0.5f);
2809                final int y = (int) (e.getY(index) + 0.5f);
2810                if (mScrollState != SCROLL_STATE_DRAGGING) {
2811                    final int dx = x - mInitialTouchX;
2812                    final int dy = y - mInitialTouchY;
2813                    boolean startScroll = false;
2814                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2815                        mLastTouchX = x;
2816                        startScroll = true;
2817                    }
2818                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2819                        mLastTouchY = y;
2820                        startScroll = true;
2821                    }
2822                    if (startScroll) {
2823                        setScrollState(SCROLL_STATE_DRAGGING);
2824                    }
2825                }
2826            } break;
2827
2828            case MotionEvent.ACTION_POINTER_UP: {
2829                onPointerUp(e);
2830            } break;
2831
2832            case MotionEvent.ACTION_UP: {
2833                mVelocityTracker.clear();
2834                stopNestedScroll(TYPE_TOUCH);
2835            } break;
2836
2837            case MotionEvent.ACTION_CANCEL: {
2838                cancelTouch();
2839            }
2840        }
2841        return mScrollState == SCROLL_STATE_DRAGGING;
2842    }
2843
2844    @Override
2845    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2846        final int listenerCount = mOnItemTouchListeners.size();
2847        for (int i = 0; i < listenerCount; i++) {
2848            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2849            listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
2850        }
2851        super.requestDisallowInterceptTouchEvent(disallowIntercept);
2852    }
2853
2854    @Override
2855    public boolean onTouchEvent(MotionEvent e) {
2856        if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
2857            return false;
2858        }
2859        if (dispatchOnItemTouch(e)) {
2860            cancelTouch();
2861            return true;
2862        }
2863
2864        if (mLayout == null) {
2865            return false;
2866        }
2867
2868        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2869        final boolean canScrollVertically = mLayout.canScrollVertically();
2870
2871        if (mVelocityTracker == null) {
2872            mVelocityTracker = VelocityTracker.obtain();
2873        }
2874        boolean eventAddedToVelocityTracker = false;
2875
2876        final MotionEvent vtev = MotionEvent.obtain(e);
2877        final int action = e.getActionMasked();
2878        final int actionIndex = e.getActionIndex();
2879
2880        if (action == MotionEvent.ACTION_DOWN) {
2881            mNestedOffsets[0] = mNestedOffsets[1] = 0;
2882        }
2883        vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
2884
2885        switch (action) {
2886            case MotionEvent.ACTION_DOWN: {
2887                mScrollPointerId = e.getPointerId(0);
2888                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2889                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2890
2891                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2892                if (canScrollHorizontally) {
2893                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2894                }
2895                if (canScrollVertically) {
2896                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2897                }
2898                startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
2899            } break;
2900
2901            case MotionEvent.ACTION_POINTER_DOWN: {
2902                mScrollPointerId = e.getPointerId(actionIndex);
2903                mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2904                mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2905            } break;
2906
2907            case MotionEvent.ACTION_MOVE: {
2908                final int index = e.findPointerIndex(mScrollPointerId);
2909                if (index < 0) {
2910                    Log.e(TAG, "Error processing scroll; pointer index for id "
2911                            + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2912                    return false;
2913                }
2914
2915                final int x = (int) (e.getX(index) + 0.5f);
2916                final int y = (int) (e.getY(index) + 0.5f);
2917                int dx = mLastTouchX - x;
2918                int dy = mLastTouchY - y;
2919
2920                if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset, TYPE_TOUCH)) {
2921                    dx -= mScrollConsumed[0];
2922                    dy -= mScrollConsumed[1];
2923                    vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
2924                    // Updated the nested offsets
2925                    mNestedOffsets[0] += mScrollOffset[0];
2926                    mNestedOffsets[1] += mScrollOffset[1];
2927                }
2928
2929                if (mScrollState != SCROLL_STATE_DRAGGING) {
2930                    boolean startScroll = false;
2931                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2932                        if (dx > 0) {
2933                            dx -= mTouchSlop;
2934                        } else {
2935                            dx += mTouchSlop;
2936                        }
2937                        startScroll = true;
2938                    }
2939                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2940                        if (dy > 0) {
2941                            dy -= mTouchSlop;
2942                        } else {
2943                            dy += mTouchSlop;
2944                        }
2945                        startScroll = true;
2946                    }
2947                    if (startScroll) {
2948                        setScrollState(SCROLL_STATE_DRAGGING);
2949                    }
2950                }
2951
2952                if (mScrollState == SCROLL_STATE_DRAGGING) {
2953                    mLastTouchX = x - mScrollOffset[0];
2954                    mLastTouchY = y - mScrollOffset[1];
2955
2956                    if (scrollByInternal(
2957                            canScrollHorizontally ? dx : 0,
2958                            canScrollVertically ? dy : 0,
2959                            vtev)) {
2960                        getParent().requestDisallowInterceptTouchEvent(true);
2961                    }
2962                    if (mGapWorker != null && (dx != 0 || dy != 0)) {
2963                        mGapWorker.postFromTraversal(this, dx, dy);
2964                    }
2965                }
2966            } break;
2967
2968            case MotionEvent.ACTION_POINTER_UP: {
2969                onPointerUp(e);
2970            } break;
2971
2972            case MotionEvent.ACTION_UP: {
2973                mVelocityTracker.addMovement(vtev);
2974                eventAddedToVelocityTracker = true;
2975                mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
2976                final float xvel = canScrollHorizontally
2977                        ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
2978                final float yvel = canScrollVertically
2979                        ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
2980                if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
2981                    setScrollState(SCROLL_STATE_IDLE);
2982                }
2983                resetTouch();
2984            } break;
2985
2986            case MotionEvent.ACTION_CANCEL: {
2987                cancelTouch();
2988            } break;
2989        }
2990
2991        if (!eventAddedToVelocityTracker) {
2992            mVelocityTracker.addMovement(vtev);
2993        }
2994        vtev.recycle();
2995
2996        return true;
2997    }
2998
2999    private void resetTouch() {
3000        if (mVelocityTracker != null) {
3001            mVelocityTracker.clear();
3002        }
3003        stopNestedScroll(TYPE_TOUCH);
3004        releaseGlows();
3005    }
3006
3007    private void cancelTouch() {
3008        resetTouch();
3009        setScrollState(SCROLL_STATE_IDLE);
3010    }
3011
3012    private void onPointerUp(MotionEvent e) {
3013        final int actionIndex = e.getActionIndex();
3014        if (e.getPointerId(actionIndex) == mScrollPointerId) {
3015            // Pick a new pointer to pick up the slack.
3016            final int newIndex = actionIndex == 0 ? 1 : 0;
3017            mScrollPointerId = e.getPointerId(newIndex);
3018            mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
3019            mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
3020        }
3021    }
3022
3023    @Override
3024    public boolean onGenericMotionEvent(MotionEvent event) {
3025        if (mLayout == null) {
3026            return false;
3027        }
3028        if (mLayoutFrozen) {
3029            return false;
3030        }
3031        if (event.getAction() == MotionEventCompat.ACTION_SCROLL) {
3032            final float vScroll, hScroll;
3033            if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
3034                if (mLayout.canScrollVertically()) {
3035                    // Inverse the sign of the vertical scroll to align the scroll orientation
3036                    // with AbsListView.
3037                    vScroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
3038                } else {
3039                    vScroll = 0f;
3040                }
3041                if (mLayout.canScrollHorizontally()) {
3042                    hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
3043                } else {
3044                    hScroll = 0f;
3045                }
3046            } else if ((event.getSource() & InputDeviceCompat.SOURCE_ROTARY_ENCODER) != 0) {
3047                final float axisScroll = event.getAxisValue(MotionEventCompat.AXIS_SCROLL);
3048                if (mLayout.canScrollVertically()) {
3049                    // Invert the sign of the vertical scroll to align the scroll orientation
3050                    // with AbsListView.
3051                    vScroll = -axisScroll;
3052                    hScroll = 0f;
3053                } else if (mLayout.canScrollHorizontally()) {
3054                    vScroll = 0f;
3055                    hScroll = axisScroll;
3056                } else {
3057                    vScroll = 0f;
3058                    hScroll = 0f;
3059                }
3060            } else {
3061                vScroll = 0f;
3062                hScroll = 0f;
3063            }
3064
3065            if (vScroll != 0 || hScroll != 0) {
3066                scrollByInternal((int) (hScroll * mScaledHorizontalScrollFactor),
3067                        (int) (vScroll * mScaledVerticalScrollFactor), event);
3068            }
3069        }
3070        return false;
3071    }
3072
3073    @Override
3074    protected void onMeasure(int widthSpec, int heightSpec) {
3075        if (mLayout == null) {
3076            defaultOnMeasure(widthSpec, heightSpec);
3077            return;
3078        }
3079        if (mLayout.mAutoMeasure) {
3080            final int widthMode = MeasureSpec.getMode(widthSpec);
3081            final int heightMode = MeasureSpec.getMode(heightSpec);
3082            final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
3083                    && heightMode == MeasureSpec.EXACTLY;
3084            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3085            if (skipMeasure || mAdapter == null) {
3086                return;
3087            }
3088            if (mState.mLayoutStep == State.STEP_START) {
3089                dispatchLayoutStep1();
3090            }
3091            // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
3092            // consistency
3093            mLayout.setMeasureSpecs(widthSpec, heightSpec);
3094            mState.mIsMeasuring = true;
3095            dispatchLayoutStep2();
3096
3097            // now we can get the width and height from the children.
3098            mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
3099
3100            // if RecyclerView has non-exact width and height and if there is at least one child
3101            // which also has non-exact width & height, we have to re-measure.
3102            if (mLayout.shouldMeasureTwice()) {
3103                mLayout.setMeasureSpecs(
3104                        MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
3105                        MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
3106                mState.mIsMeasuring = true;
3107                dispatchLayoutStep2();
3108                // now we can get the width and height from the children.
3109                mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
3110            }
3111        } else {
3112            if (mHasFixedSize) {
3113                mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3114                return;
3115            }
3116            // custom onMeasure
3117            if (mAdapterUpdateDuringMeasure) {
3118                eatRequestLayout();
3119                onEnterLayoutOrScroll();
3120                processAdapterUpdatesAndSetAnimationFlags();
3121                onExitLayoutOrScroll();
3122
3123                if (mState.mRunPredictiveAnimations) {
3124                    mState.mInPreLayout = true;
3125                } else {
3126                    // consume remaining updates to provide a consistent state with the layout pass.
3127                    mAdapterHelper.consumeUpdatesInOnePass();
3128                    mState.mInPreLayout = false;
3129                }
3130                mAdapterUpdateDuringMeasure = false;
3131                resumeRequestLayout(false);
3132            }
3133
3134            if (mAdapter != null) {
3135                mState.mItemCount = mAdapter.getItemCount();
3136            } else {
3137                mState.mItemCount = 0;
3138            }
3139            eatRequestLayout();
3140            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3141            resumeRequestLayout(false);
3142            mState.mInPreLayout = false; // clear
3143        }
3144    }
3145
3146    /**
3147     * Used when onMeasure is called before layout manager is set
3148     */
3149    void defaultOnMeasure(int widthSpec, int heightSpec) {
3150        // calling LayoutManager here is not pretty but that API is already public and it is better
3151        // than creating another method since this is internal.
3152        final int width = LayoutManager.chooseSize(widthSpec,
3153                getPaddingLeft() + getPaddingRight(),
3154                ViewCompat.getMinimumWidth(this));
3155        final int height = LayoutManager.chooseSize(heightSpec,
3156                getPaddingTop() + getPaddingBottom(),
3157                ViewCompat.getMinimumHeight(this));
3158
3159        setMeasuredDimension(width, height);
3160    }
3161
3162    @Override
3163    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
3164        super.onSizeChanged(w, h, oldw, oldh);
3165        if (w != oldw || h != oldh) {
3166            invalidateGlows();
3167            // layout's w/h are updated during measure/layout steps.
3168        }
3169    }
3170
3171    /**
3172     * Sets the {@link ItemAnimator} that will handle animations involving changes
3173     * to the items in this RecyclerView. By default, RecyclerView instantiates and
3174     * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
3175     * enabled for the RecyclerView depends on the ItemAnimator and whether
3176     * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
3177     * supports item animations}.
3178     *
3179     * @param animator The ItemAnimator being set. If null, no animations will occur
3180     * when changes occur to the items in this RecyclerView.
3181     */
3182    public void setItemAnimator(ItemAnimator animator) {
3183        if (mItemAnimator != null) {
3184            mItemAnimator.endAnimations();
3185            mItemAnimator.setListener(null);
3186        }
3187        mItemAnimator = animator;
3188        if (mItemAnimator != null) {
3189            mItemAnimator.setListener(mItemAnimatorListener);
3190        }
3191    }
3192
3193    void onEnterLayoutOrScroll() {
3194        mLayoutOrScrollCounter++;
3195    }
3196
3197    void onExitLayoutOrScroll() {
3198        onExitLayoutOrScroll(true);
3199    }
3200
3201    void onExitLayoutOrScroll(boolean enableChangeEvents) {
3202        mLayoutOrScrollCounter--;
3203        if (mLayoutOrScrollCounter < 1) {
3204            if (DEBUG && mLayoutOrScrollCounter < 0) {
3205                throw new IllegalStateException("layout or scroll counter cannot go below zero."
3206                        + "Some calls are not matching");
3207            }
3208            mLayoutOrScrollCounter = 0;
3209            if (enableChangeEvents) {
3210                dispatchContentChangedIfNecessary();
3211                dispatchPendingImportantForAccessibilityChanges();
3212            }
3213        }
3214    }
3215
3216    boolean isAccessibilityEnabled() {
3217        return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
3218    }
3219
3220    private void dispatchContentChangedIfNecessary() {
3221        final int flags = mEatenAccessibilityChangeFlags;
3222        mEatenAccessibilityChangeFlags = 0;
3223        if (flags != 0 && isAccessibilityEnabled()) {
3224            final AccessibilityEvent event = AccessibilityEvent.obtain();
3225            event.setEventType(AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
3226            AccessibilityEventCompat.setContentChangeTypes(event, flags);
3227            sendAccessibilityEventUnchecked(event);
3228        }
3229    }
3230
3231    /**
3232     * Returns whether RecyclerView is currently computing a layout.
3233     * <p>
3234     * If this method returns true, it means that RecyclerView is in a lockdown state and any
3235     * attempt to update adapter contents will result in an exception because adapter contents
3236     * cannot be changed while RecyclerView is trying to compute the layout.
3237     * <p>
3238     * It is very unlikely that your code will be running during this state as it is
3239     * called by the framework when a layout traversal happens or RecyclerView starts to scroll
3240     * in response to system events (touch, accessibility etc).
3241     * <p>
3242     * This case may happen if you have some custom logic to change adapter contents in
3243     * response to a View callback (e.g. focus change callback) which might be triggered during a
3244     * layout calculation. In these cases, you should just postpone the change using a Handler or a
3245     * similar mechanism.
3246     *
3247     * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
3248     *         otherwise
3249     */
3250    public boolean isComputingLayout() {
3251        return mLayoutOrScrollCounter > 0;
3252    }
3253
3254    /**
3255     * Returns true if an accessibility event should not be dispatched now. This happens when an
3256     * accessibility request arrives while RecyclerView does not have a stable state which is very
3257     * hard to handle for a LayoutManager. Instead, this method records necessary information about
3258     * the event and dispatches a window change event after the critical section is finished.
3259     *
3260     * @return True if the accessibility event should be postponed.
3261     */
3262    boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
3263        if (isComputingLayout()) {
3264            int type = 0;
3265            if (event != null) {
3266                type = AccessibilityEventCompat.getContentChangeTypes(event);
3267            }
3268            if (type == 0) {
3269                type = AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED;
3270            }
3271            mEatenAccessibilityChangeFlags |= type;
3272            return true;
3273        }
3274        return false;
3275    }
3276
3277    @Override
3278    public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
3279        if (shouldDeferAccessibilityEvent(event)) {
3280            return;
3281        }
3282        super.sendAccessibilityEventUnchecked(event);
3283    }
3284
3285    /**
3286     * Gets the current ItemAnimator for this RecyclerView. A null return value
3287     * indicates that there is no animator and that item changes will happen without
3288     * any animations. By default, RecyclerView instantiates and
3289     * uses an instance of {@link DefaultItemAnimator}.
3290     *
3291     * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
3292     * when changes occur to the items in this RecyclerView.
3293     */
3294    public ItemAnimator getItemAnimator() {
3295        return mItemAnimator;
3296    }
3297
3298    /**
3299     * Post a runnable to the next frame to run pending item animations. Only the first such
3300     * request will be posted, governed by the mPostedAnimatorRunner flag.
3301     */
3302    void postAnimationRunner() {
3303        if (!mPostedAnimatorRunner && mIsAttached) {
3304            ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
3305            mPostedAnimatorRunner = true;
3306        }
3307    }
3308
3309    private boolean predictiveItemAnimationsEnabled() {
3310        return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
3311    }
3312
3313    /**
3314     * Consumes adapter updates and calculates which type of animations we want to run.
3315     * Called in onMeasure and dispatchLayout.
3316     * <p>
3317     * This method may process only the pre-layout state of updates or all of them.
3318     */
3319    private void processAdapterUpdatesAndSetAnimationFlags() {
3320        if (mDataSetHasChangedAfterLayout) {
3321            // Processing these items have no value since data set changed unexpectedly.
3322            // Instead, we just reset it.
3323            mAdapterHelper.reset();
3324            mLayout.onItemsChanged(this);
3325        }
3326        // simple animations are a subset of advanced animations (which will cause a
3327        // pre-layout step)
3328        // If layout supports predictive animations, pre-process to decide if we want to run them
3329        if (predictiveItemAnimationsEnabled()) {
3330            mAdapterHelper.preProcess();
3331        } else {
3332            mAdapterHelper.consumeUpdatesInOnePass();
3333        }
3334        boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
3335        mState.mRunSimpleAnimations = mFirstLayoutComplete
3336                && mItemAnimator != null
3337                && (mDataSetHasChangedAfterLayout
3338                || animationTypeSupported
3339                || mLayout.mRequestedSimpleAnimations)
3340                && (!mDataSetHasChangedAfterLayout
3341                || mAdapter.hasStableIds());
3342        mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
3343                && animationTypeSupported
3344                && !mDataSetHasChangedAfterLayout
3345                && predictiveItemAnimationsEnabled();
3346    }
3347
3348    /**
3349     * Wrapper around layoutChildren() that handles animating changes caused by layout.
3350     * Animations work on the assumption that there are five different kinds of items
3351     * in play:
3352     * PERSISTENT: items are visible before and after layout
3353     * REMOVED: items were visible before layout and were removed by the app
3354     * ADDED: items did not exist before layout and were added by the app
3355     * DISAPPEARING: items exist in the data set before/after, but changed from
3356     * visible to non-visible in the process of layout (they were moved off
3357     * screen as a side-effect of other changes)
3358     * APPEARING: items exist in the data set before/after, but changed from
3359     * non-visible to visible in the process of layout (they were moved on
3360     * screen as a side-effect of other changes)
3361     * The overall approach figures out what items exist before/after layout and
3362     * infers one of the five above states for each of the items. Then the animations
3363     * are set up accordingly:
3364     * PERSISTENT views are animated via
3365     * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3366     * DISAPPEARING views are animated via
3367     * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3368     * APPEARING views are animated via
3369     * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3370     * and changed views are animated via
3371     * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
3372     */
3373    void dispatchLayout() {
3374        if (mAdapter == null) {
3375            Log.e(TAG, "No adapter attached; skipping layout");
3376            // leave the state in START
3377            return;
3378        }
3379        if (mLayout == null) {
3380            Log.e(TAG, "No layout manager attached; skipping layout");
3381            // leave the state in START
3382            return;
3383        }
3384        mState.mIsMeasuring = false;
3385        if (mState.mLayoutStep == State.STEP_START) {
3386            dispatchLayoutStep1();
3387            mLayout.setExactMeasureSpecsFrom(this);
3388            dispatchLayoutStep2();
3389        } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
3390                || mLayout.getHeight() != getHeight()) {
3391            // First 2 steps are done in onMeasure but looks like we have to run again due to
3392            // changed size.
3393            mLayout.setExactMeasureSpecsFrom(this);
3394            dispatchLayoutStep2();
3395        } else {
3396            // always make sure we sync them (to ensure mode is exact)
3397            mLayout.setExactMeasureSpecsFrom(this);
3398        }
3399        dispatchLayoutStep3();
3400    }
3401
3402    private void saveFocusInfo() {
3403        View child = null;
3404        if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
3405            child = getFocusedChild();
3406        }
3407
3408        final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
3409        if (focusedVh == null) {
3410            resetFocusInfo();
3411        } else {
3412            mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
3413            // mFocusedItemPosition should hold the current adapter position of the previously
3414            // focused item. If the item is removed, we store the previous adapter position of the
3415            // removed item.
3416            mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION
3417                    : (focusedVh.isRemoved() ? focusedVh.mOldPosition
3418                            : focusedVh.getAdapterPosition());
3419            mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
3420        }
3421    }
3422
3423    private void resetFocusInfo() {
3424        mState.mFocusedItemId = NO_ID;
3425        mState.mFocusedItemPosition = NO_POSITION;
3426        mState.mFocusedSubChildId = View.NO_ID;
3427    }
3428
3429    /**
3430     * Finds the best view candidate to request focus on using mFocusedItemPosition index of the
3431     * previously focused item. It first traverses the adapter forward to find a focusable candidate
3432     * and if no such candidate is found, it reverses the focus search direction for the items
3433     * before the mFocusedItemPosition'th index;
3434     * @return The best candidate to request focus on, or null if no such candidate exists. Null
3435     * indicates all the existing adapter items are unfocusable.
3436     */
3437    @Nullable
3438    private View findNextViewToFocus() {
3439        int startFocusSearchIndex = mState.mFocusedItemPosition != -1 ? mState.mFocusedItemPosition
3440                : 0;
3441        ViewHolder nextFocus;
3442        final int itemCount = mState.getItemCount();
3443        for (int i = startFocusSearchIndex; i < itemCount; i++) {
3444            nextFocus = findViewHolderForAdapterPosition(i);
3445            if (nextFocus == null) {
3446                break;
3447            }
3448            if (nextFocus.itemView.hasFocusable()) {
3449                return nextFocus.itemView;
3450            }
3451        }
3452        final int limit = Math.min(itemCount, startFocusSearchIndex);
3453        for (int i = limit - 1; i >= 0; i--) {
3454            nextFocus = findViewHolderForAdapterPosition(i);
3455            if (nextFocus == null) {
3456                return null;
3457            }
3458            if (nextFocus.itemView.hasFocusable()) {
3459                return nextFocus.itemView;
3460            }
3461        }
3462        return null;
3463    }
3464
3465    private void recoverFocusFromState() {
3466        if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()
3467                || getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
3468                || (getDescendantFocusability() == FOCUS_BEFORE_DESCENDANTS && isFocused())) {
3469            // No-op if either of these cases happens:
3470            // 1. RV has no focus, or 2. RV blocks focus to its children, or 3. RV takes focus
3471            // before its children and is focused (i.e. it already stole the focus away from its
3472            // descendants).
3473            return;
3474        }
3475        // only recover focus if RV itself has the focus or the focused view is hidden
3476        if (!isFocused()) {
3477            final View focusedChild = getFocusedChild();
3478            if (IGNORE_DETACHED_FOCUSED_CHILD
3479                    && (focusedChild.getParent() == null || !focusedChild.hasFocus())) {
3480                // Special handling of API 15-. A focused child can be invalid because mFocus is not
3481                // cleared when the child is detached (mParent = null),
3482                // This happens because clearFocus on API 15- does not invalidate mFocus of its
3483                // parent when this child is detached.
3484                // For API 16+, this is not an issue because requestFocus takes care of clearing the
3485                // prior detached focused child. For API 15- the problem happens in 2 cases because
3486                // clearChild does not call clearChildFocus on RV: 1. setFocusable(false) is called
3487                // for the current focused item which calls clearChild or 2. when the prior focused
3488                // child is removed, removeDetachedView called in layout step 3 which calls
3489                // clearChild. We should ignore this invalid focused child in all our calculations
3490                // for the next view to receive focus, and apply the focus recovery logic instead.
3491                if (mChildHelper.getChildCount() == 0) {
3492                    // No children left. Request focus on the RV itself since one of its children
3493                    // was holding focus previously.
3494                    requestFocus();
3495                    return;
3496                }
3497            } else if (!mChildHelper.isHidden(focusedChild)) {
3498                // If the currently focused child is hidden, apply the focus recovery logic.
3499                // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
3500                return;
3501            }
3502        }
3503        ViewHolder focusTarget = null;
3504        // RV first attempts to locate the previously focused item to request focus on using
3505        // mFocusedItemId. If such an item no longer exists, it then makes a best-effort attempt to
3506        // find the next best candidate to request focus on based on mFocusedItemPosition.
3507        if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
3508            focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
3509        }
3510        View viewToFocus = null;
3511        if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView)
3512                || !focusTarget.itemView.hasFocusable()) {
3513            if (mChildHelper.getChildCount() > 0) {
3514                // At this point, RV has focus and either of these conditions are true:
3515                // 1. There's no previously focused item either because RV received focused before
3516                // layout, or the previously focused item was removed, or RV doesn't have stable IDs
3517                // 2. Previous focus child is hidden, or 3. Previous focused child is no longer
3518                // focusable. In either of these cases, we make sure that RV still passes down the
3519                // focus to one of its focusable children using a best-effort algorithm.
3520                viewToFocus = findNextViewToFocus();
3521            }
3522        } else {
3523            // looks like the focused item has been replaced with another view that represents the
3524            // same item in the adapter. Request focus on that.
3525            viewToFocus = focusTarget.itemView;
3526        }
3527
3528        if (viewToFocus != null) {
3529            if (mState.mFocusedSubChildId != NO_ID) {
3530                View child = viewToFocus.findViewById(mState.mFocusedSubChildId);
3531                if (child != null && child.isFocusable()) {
3532                    viewToFocus = child;
3533                }
3534            }
3535            viewToFocus.requestFocus();
3536        }
3537    }
3538
3539    private int getDeepestFocusedViewWithId(View view) {
3540        int lastKnownId = view.getId();
3541        while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
3542            view = ((ViewGroup) view).getFocusedChild();
3543            final int id = view.getId();
3544            if (id != View.NO_ID) {
3545                lastKnownId = view.getId();
3546            }
3547        }
3548        return lastKnownId;
3549    }
3550
3551    /**
3552     * The first step of a layout where we;
3553     * - process adapter updates
3554     * - decide which animation should run
3555     * - save information about current views
3556     * - If necessary, run predictive layout and save its information
3557     */
3558    private void dispatchLayoutStep1() {
3559        mState.assertLayoutStep(State.STEP_START);
3560        mState.mIsMeasuring = false;
3561        eatRequestLayout();
3562        mViewInfoStore.clear();
3563        onEnterLayoutOrScroll();
3564        processAdapterUpdatesAndSetAnimationFlags();
3565        saveFocusInfo();
3566        mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
3567        mItemsAddedOrRemoved = mItemsChanged = false;
3568        mState.mInPreLayout = mState.mRunPredictiveAnimations;
3569        mState.mItemCount = mAdapter.getItemCount();
3570        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3571
3572        if (mState.mRunSimpleAnimations) {
3573            // Step 0: Find out where all non-removed items are, pre-layout
3574            int count = mChildHelper.getChildCount();
3575            for (int i = 0; i < count; ++i) {
3576                final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3577                if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
3578                    continue;
3579                }
3580                final ItemHolderInfo animationInfo = mItemAnimator
3581                        .recordPreLayoutInformation(mState, holder,
3582                                ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
3583                                holder.getUnmodifiedPayloads());
3584                mViewInfoStore.addToPreLayout(holder, animationInfo);
3585                if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
3586                        && !holder.shouldIgnore() && !holder.isInvalid()) {
3587                    long key = getChangedHolderKey(holder);
3588                    // This is NOT the only place where a ViewHolder is added to old change holders
3589                    // list. There is another case where:
3590                    //    * A VH is currently hidden but not deleted
3591                    //    * The hidden item is changed in the adapter
3592                    //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
3593                    // When this case is detected, RV will un-hide that view and add to the old
3594                    // change holders list.
3595                    mViewInfoStore.addToOldChangeHolders(key, holder);
3596                }
3597            }
3598        }
3599        if (mState.mRunPredictiveAnimations) {
3600            // Step 1: run prelayout: This will use the old positions of items. The layout manager
3601            // is expected to layout everything, even removed items (though not to add removed
3602            // items back to the container). This gives the pre-layout position of APPEARING views
3603            // which come into existence as part of the real layout.
3604
3605            // Save old positions so that LayoutManager can run its mapping logic.
3606            saveOldPositions();
3607            final boolean didStructureChange = mState.mStructureChanged;
3608            mState.mStructureChanged = false;
3609            // temporarily disable flag because we are asking for previous layout
3610            mLayout.onLayoutChildren(mRecycler, mState);
3611            mState.mStructureChanged = didStructureChange;
3612
3613            for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
3614                final View child = mChildHelper.getChildAt(i);
3615                final ViewHolder viewHolder = getChildViewHolderInt(child);
3616                if (viewHolder.shouldIgnore()) {
3617                    continue;
3618                }
3619                if (!mViewInfoStore.isInPreLayout(viewHolder)) {
3620                    int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
3621                    boolean wasHidden = viewHolder
3622                            .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3623                    if (!wasHidden) {
3624                        flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
3625                    }
3626                    final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
3627                            mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
3628                    if (wasHidden) {
3629                        recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
3630                    } else {
3631                        mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
3632                    }
3633                }
3634            }
3635            // we don't process disappearing list because they may re-appear in post layout pass.
3636            clearOldPositions();
3637        } else {
3638            clearOldPositions();
3639        }
3640        onExitLayoutOrScroll();
3641        resumeRequestLayout(false);
3642        mState.mLayoutStep = State.STEP_LAYOUT;
3643    }
3644
3645    /**
3646     * The second layout step where we do the actual layout of the views for the final state.
3647     * This step might be run multiple times if necessary (e.g. measure).
3648     */
3649    private void dispatchLayoutStep2() {
3650        eatRequestLayout();
3651        onEnterLayoutOrScroll();
3652        mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
3653        mAdapterHelper.consumeUpdatesInOnePass();
3654        mState.mItemCount = mAdapter.getItemCount();
3655        mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
3656
3657        // Step 2: Run layout
3658        mState.mInPreLayout = false;
3659        mLayout.onLayoutChildren(mRecycler, mState);
3660
3661        mState.mStructureChanged = false;
3662        mPendingSavedState = null;
3663
3664        // onLayoutChildren may have caused client code to disable item animations; re-check
3665        mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
3666        mState.mLayoutStep = State.STEP_ANIMATIONS;
3667        onExitLayoutOrScroll();
3668        resumeRequestLayout(false);
3669    }
3670
3671    /**
3672     * The final step of the layout where we save the information about views for animations,
3673     * trigger animations and do any necessary cleanup.
3674     */
3675    private void dispatchLayoutStep3() {
3676        mState.assertLayoutStep(State.STEP_ANIMATIONS);
3677        eatRequestLayout();
3678        onEnterLayoutOrScroll();
3679        mState.mLayoutStep = State.STEP_START;
3680        if (mState.mRunSimpleAnimations) {
3681            // Step 3: Find out where things are now, and process change animations.
3682            // traverse list in reverse because we may call animateChange in the loop which may
3683            // remove the target view holder.
3684            for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
3685                ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3686                if (holder.shouldIgnore()) {
3687                    continue;
3688                }
3689                long key = getChangedHolderKey(holder);
3690                final ItemHolderInfo animationInfo = mItemAnimator
3691                        .recordPostLayoutInformation(mState, holder);
3692                ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
3693                if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
3694                    // run a change animation
3695
3696                    // If an Item is CHANGED but the updated version is disappearing, it creates
3697                    // a conflicting case.
3698                    // Since a view that is marked as disappearing is likely to be going out of
3699                    // bounds, we run a change animation. Both views will be cleaned automatically
3700                    // once their animations finish.
3701                    // On the other hand, if it is the same view holder instance, we run a
3702                    // disappearing animation instead because we are not going to rebind the updated
3703                    // VH unless it is enforced by the layout manager.
3704                    final boolean oldDisappearing = mViewInfoStore.isDisappearing(
3705                            oldChangeViewHolder);
3706                    final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
3707                    if (oldDisappearing && oldChangeViewHolder == holder) {
3708                        // run disappear animation instead of change
3709                        mViewInfoStore.addToPostLayout(holder, animationInfo);
3710                    } else {
3711                        final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
3712                                oldChangeViewHolder);
3713                        // we add and remove so that any post info is merged.
3714                        mViewInfoStore.addToPostLayout(holder, animationInfo);
3715                        ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
3716                        if (preInfo == null) {
3717                            handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
3718                        } else {
3719                            animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
3720                                    oldDisappearing, newDisappearing);
3721                        }
3722                    }
3723                } else {
3724                    mViewInfoStore.addToPostLayout(holder, animationInfo);
3725                }
3726            }
3727
3728            // Step 4: Process view info lists and trigger animations
3729            mViewInfoStore.process(mViewInfoProcessCallback);
3730        }
3731
3732        mLayout.removeAndRecycleScrapInt(mRecycler);
3733        mState.mPreviousLayoutItemCount = mState.mItemCount;
3734        mDataSetHasChangedAfterLayout = false;
3735        mState.mRunSimpleAnimations = false;
3736
3737        mState.mRunPredictiveAnimations = false;
3738        mLayout.mRequestedSimpleAnimations = false;
3739        if (mRecycler.mChangedScrap != null) {
3740            mRecycler.mChangedScrap.clear();
3741        }
3742        if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
3743            // Initial prefetch has expanded cache, so reset until next prefetch.
3744            // This prevents initial prefetches from expanding the cache permanently.
3745            mLayout.mPrefetchMaxCountObserved = 0;
3746            mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
3747            mRecycler.updateViewCacheSize();
3748        }
3749
3750        mLayout.onLayoutCompleted(mState);
3751        onExitLayoutOrScroll();
3752        resumeRequestLayout(false);
3753        mViewInfoStore.clear();
3754        if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
3755            dispatchOnScrolled(0, 0);
3756        }
3757        recoverFocusFromState();
3758        resetFocusInfo();
3759    }
3760
3761    /**
3762     * This handles the case where there is an unexpected VH missing in the pre-layout map.
3763     * <p>
3764     * We might be able to detect the error in the application which will help the developer to
3765     * resolve the issue.
3766     * <p>
3767     * If it is not an expected error, we at least print an error to notify the developer and ignore
3768     * the animation.
3769     *
3770     * https://code.google.com/p/android/issues/detail?id=193958
3771     *
3772     * @param key The change key
3773     * @param holder Current ViewHolder
3774     * @param oldChangeViewHolder Changed ViewHolder
3775     */
3776    private void handleMissingPreInfoForChangeError(long key,
3777            ViewHolder holder, ViewHolder oldChangeViewHolder) {
3778        // check if two VH have the same key, if so, print that as an error
3779        final int childCount = mChildHelper.getChildCount();
3780        for (int i = 0; i < childCount; i++) {
3781            View view = mChildHelper.getChildAt(i);
3782            ViewHolder other = getChildViewHolderInt(view);
3783            if (other == holder) {
3784                continue;
3785            }
3786            final long otherKey = getChangedHolderKey(other);
3787            if (otherKey == key) {
3788                if (mAdapter != null && mAdapter.hasStableIds()) {
3789                    throw new IllegalStateException("Two different ViewHolders have the same stable"
3790                            + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
3791                            + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
3792                } else {
3793                    throw new IllegalStateException("Two different ViewHolders have the same change"
3794                            + " ID. This might happen due to inconsistent Adapter update events or"
3795                            + " if the LayoutManager lays out the same View multiple times."
3796                            + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
3797                }
3798            }
3799        }
3800        // Very unlikely to happen but if it does, notify the developer.
3801        Log.e(TAG, "Problem while matching changed view holders with the new"
3802                + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
3803                + " cannot be found but it is necessary for " + holder);
3804    }
3805
3806    /**
3807     * Records the animation information for a view holder that was bounced from hidden list. It
3808     * also clears the bounce back flag.
3809     */
3810    void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
3811            ItemHolderInfo animationInfo) {
3812        // looks like this view bounced back from hidden list!
3813        viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3814        if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
3815                && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
3816            long key = getChangedHolderKey(viewHolder);
3817            mViewInfoStore.addToOldChangeHolders(key, viewHolder);
3818        }
3819        mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
3820    }
3821
3822    private void findMinMaxChildLayoutPositions(int[] into) {
3823        final int count = mChildHelper.getChildCount();
3824        if (count == 0) {
3825            into[0] = NO_POSITION;
3826            into[1] = NO_POSITION;
3827            return;
3828        }
3829        int minPositionPreLayout = Integer.MAX_VALUE;
3830        int maxPositionPreLayout = Integer.MIN_VALUE;
3831        for (int i = 0; i < count; ++i) {
3832            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3833            if (holder.shouldIgnore()) {
3834                continue;
3835            }
3836            final int pos = holder.getLayoutPosition();
3837            if (pos < minPositionPreLayout) {
3838                minPositionPreLayout = pos;
3839            }
3840            if (pos > maxPositionPreLayout) {
3841                maxPositionPreLayout = pos;
3842            }
3843        }
3844        into[0] = minPositionPreLayout;
3845        into[1] = maxPositionPreLayout;
3846    }
3847
3848    private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
3849        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3850        return mMinMaxLayoutPositions[0] != minPositionPreLayout
3851                || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
3852    }
3853
3854    @Override
3855    protected void removeDetachedView(View child, boolean animate) {
3856        ViewHolder vh = getChildViewHolderInt(child);
3857        if (vh != null) {
3858            if (vh.isTmpDetached()) {
3859                vh.clearTmpDetachFlag();
3860            } else if (!vh.shouldIgnore()) {
3861                throw new IllegalArgumentException("Called removeDetachedView with a view which"
3862                        + " is not flagged as tmp detached." + vh);
3863            }
3864        }
3865
3866        // Clear any android.view.animation.Animation that may prevent the item from
3867        // detaching when being removed. If a child is re-added before the
3868        // lazy detach occurs, it will receive invalid attach/detach sequencing.
3869        child.clearAnimation();
3870
3871        dispatchChildDetached(child);
3872        super.removeDetachedView(child, animate);
3873    }
3874
3875    /**
3876     * Returns a unique key to be used while handling change animations.
3877     * It might be child's position or stable id depending on the adapter type.
3878     */
3879    long getChangedHolderKey(ViewHolder holder) {
3880        return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
3881    }
3882
3883    void animateAppearance(@NonNull ViewHolder itemHolder,
3884            @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
3885        itemHolder.setIsRecyclable(false);
3886        if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
3887            postAnimationRunner();
3888        }
3889    }
3890
3891    void animateDisappearance(@NonNull ViewHolder holder,
3892            @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
3893        addAnimatingView(holder);
3894        holder.setIsRecyclable(false);
3895        if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
3896            postAnimationRunner();
3897        }
3898    }
3899
3900    private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
3901            @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
3902            boolean oldHolderDisappearing, boolean newHolderDisappearing) {
3903        oldHolder.setIsRecyclable(false);
3904        if (oldHolderDisappearing) {
3905            addAnimatingView(oldHolder);
3906        }
3907        if (oldHolder != newHolder) {
3908            if (newHolderDisappearing) {
3909                addAnimatingView(newHolder);
3910            }
3911            oldHolder.mShadowedHolder = newHolder;
3912            // old holder should disappear after animation ends
3913            addAnimatingView(oldHolder);
3914            mRecycler.unscrapView(oldHolder);
3915            newHolder.setIsRecyclable(false);
3916            newHolder.mShadowingHolder = oldHolder;
3917        }
3918        if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
3919            postAnimationRunner();
3920        }
3921    }
3922
3923    @Override
3924    protected void onLayout(boolean changed, int l, int t, int r, int b) {
3925        TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
3926        dispatchLayout();
3927        TraceCompat.endSection();
3928        mFirstLayoutComplete = true;
3929    }
3930
3931    @Override
3932    public void requestLayout() {
3933        if (mEatRequestLayout == 0 && !mLayoutFrozen) {
3934            super.requestLayout();
3935        } else {
3936            mLayoutRequestEaten = true;
3937        }
3938    }
3939
3940    void markItemDecorInsetsDirty() {
3941        final int childCount = mChildHelper.getUnfilteredChildCount();
3942        for (int i = 0; i < childCount; i++) {
3943            final View child = mChildHelper.getUnfilteredChildAt(i);
3944            ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
3945        }
3946        mRecycler.markItemDecorInsetsDirty();
3947    }
3948
3949    @Override
3950    public void draw(Canvas c) {
3951        super.draw(c);
3952
3953        final int count = mItemDecorations.size();
3954        for (int i = 0; i < count; i++) {
3955            mItemDecorations.get(i).onDrawOver(c, this, mState);
3956        }
3957        // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
3958        // need find children closest to edges. Not sure if it is worth the effort.
3959        boolean needsInvalidate = false;
3960        if (mLeftGlow != null && !mLeftGlow.isFinished()) {
3961            final int restore = c.save();
3962            final int padding = mClipToPadding ? getPaddingBottom() : 0;
3963            c.rotate(270);
3964            c.translate(-getHeight() + padding, 0);
3965            needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
3966            c.restoreToCount(restore);
3967        }
3968        if (mTopGlow != null && !mTopGlow.isFinished()) {
3969            final int restore = c.save();
3970            if (mClipToPadding) {
3971                c.translate(getPaddingLeft(), getPaddingTop());
3972            }
3973            needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
3974            c.restoreToCount(restore);
3975        }
3976        if (mRightGlow != null && !mRightGlow.isFinished()) {
3977            final int restore = c.save();
3978            final int width = getWidth();
3979            final int padding = mClipToPadding ? getPaddingTop() : 0;
3980            c.rotate(90);
3981            c.translate(-padding, -width);
3982            needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
3983            c.restoreToCount(restore);
3984        }
3985        if (mBottomGlow != null && !mBottomGlow.isFinished()) {
3986            final int restore = c.save();
3987            c.rotate(180);
3988            if (mClipToPadding) {
3989                c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
3990            } else {
3991                c.translate(-getWidth(), -getHeight());
3992            }
3993            needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
3994            c.restoreToCount(restore);
3995        }
3996
3997        // If some views are animating, ItemDecorators are likely to move/change with them.
3998        // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
3999        // display lists are not invalidated.
4000        if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
4001                && mItemAnimator.isRunning()) {
4002            needsInvalidate = true;
4003        }
4004
4005        if (needsInvalidate) {
4006            ViewCompat.postInvalidateOnAnimation(this);
4007        }
4008    }
4009
4010    @Override
4011    public void onDraw(Canvas c) {
4012        super.onDraw(c);
4013
4014        final int count = mItemDecorations.size();
4015        for (int i = 0; i < count; i++) {
4016            mItemDecorations.get(i).onDraw(c, this, mState);
4017        }
4018    }
4019
4020    @Override
4021    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
4022        return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
4023    }
4024
4025    @Override
4026    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
4027        if (mLayout == null) {
4028            throw new IllegalStateException("RecyclerView has no LayoutManager");
4029        }
4030        return mLayout.generateDefaultLayoutParams();
4031    }
4032
4033    @Override
4034    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
4035        if (mLayout == null) {
4036            throw new IllegalStateException("RecyclerView has no LayoutManager");
4037        }
4038        return mLayout.generateLayoutParams(getContext(), attrs);
4039    }
4040
4041    @Override
4042    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
4043        if (mLayout == null) {
4044            throw new IllegalStateException("RecyclerView has no LayoutManager");
4045        }
4046        return mLayout.generateLayoutParams(p);
4047    }
4048
4049    /**
4050     * Returns true if RecyclerView is currently running some animations.
4051     * <p>
4052     * If you want to be notified when animations are finished, use
4053     * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
4054     *
4055     * @return True if there are some item animations currently running or waiting to be started.
4056     */
4057    public boolean isAnimating() {
4058        return mItemAnimator != null && mItemAnimator.isRunning();
4059    }
4060
4061    void saveOldPositions() {
4062        final int childCount = mChildHelper.getUnfilteredChildCount();
4063        for (int i = 0; i < childCount; i++) {
4064            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4065            if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
4066                throw new IllegalStateException("view holder cannot have position -1 unless it"
4067                        + " is removed");
4068            }
4069            if (!holder.shouldIgnore()) {
4070                holder.saveOldPosition();
4071            }
4072        }
4073    }
4074
4075    void clearOldPositions() {
4076        final int childCount = mChildHelper.getUnfilteredChildCount();
4077        for (int i = 0; i < childCount; i++) {
4078            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4079            if (!holder.shouldIgnore()) {
4080                holder.clearOldPosition();
4081            }
4082        }
4083        mRecycler.clearOldPositions();
4084    }
4085
4086    void offsetPositionRecordsForMove(int from, int to) {
4087        final int childCount = mChildHelper.getUnfilteredChildCount();
4088        final int start, end, inBetweenOffset;
4089        if (from < to) {
4090            start = from;
4091            end = to;
4092            inBetweenOffset = -1;
4093        } else {
4094            start = to;
4095            end = from;
4096            inBetweenOffset = 1;
4097        }
4098
4099        for (int i = 0; i < childCount; i++) {
4100            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4101            if (holder == null || holder.mPosition < start || holder.mPosition > end) {
4102                continue;
4103            }
4104            if (DEBUG) {
4105                Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
4106                        + holder);
4107            }
4108            if (holder.mPosition == from) {
4109                holder.offsetPosition(to - from, false);
4110            } else {
4111                holder.offsetPosition(inBetweenOffset, false);
4112            }
4113
4114            mState.mStructureChanged = true;
4115        }
4116        mRecycler.offsetPositionRecordsForMove(from, to);
4117        requestLayout();
4118    }
4119
4120    void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
4121        final int childCount = mChildHelper.getUnfilteredChildCount();
4122        for (int i = 0; i < childCount; i++) {
4123            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4124            if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
4125                if (DEBUG) {
4126                    Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
4127                            + holder + " now at position " + (holder.mPosition + itemCount));
4128                }
4129                holder.offsetPosition(itemCount, false);
4130                mState.mStructureChanged = true;
4131            }
4132        }
4133        mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
4134        requestLayout();
4135    }
4136
4137    void offsetPositionRecordsForRemove(int positionStart, int itemCount,
4138            boolean applyToPreLayout) {
4139        final int positionEnd = positionStart + itemCount;
4140        final int childCount = mChildHelper.getUnfilteredChildCount();
4141        for (int i = 0; i < childCount; i++) {
4142            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4143            if (holder != null && !holder.shouldIgnore()) {
4144                if (holder.mPosition >= positionEnd) {
4145                    if (DEBUG) {
4146                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4147                                + " holder " + holder + " now at position "
4148                                + (holder.mPosition - itemCount));
4149                    }
4150                    holder.offsetPosition(-itemCount, applyToPreLayout);
4151                    mState.mStructureChanged = true;
4152                } else if (holder.mPosition >= positionStart) {
4153                    if (DEBUG) {
4154                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4155                                + " holder " + holder + " now REMOVED");
4156                    }
4157                    holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
4158                            applyToPreLayout);
4159                    mState.mStructureChanged = true;
4160                }
4161            }
4162        }
4163        mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
4164        requestLayout();
4165    }
4166
4167    /**
4168     * Rebind existing views for the given range, or create as needed.
4169     *
4170     * @param positionStart Adapter position to start at
4171     * @param itemCount Number of views that must explicitly be rebound
4172     */
4173    void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
4174        final int childCount = mChildHelper.getUnfilteredChildCount();
4175        final int positionEnd = positionStart + itemCount;
4176
4177        for (int i = 0; i < childCount; i++) {
4178            final View child = mChildHelper.getUnfilteredChildAt(i);
4179            final ViewHolder holder = getChildViewHolderInt(child);
4180            if (holder == null || holder.shouldIgnore()) {
4181                continue;
4182            }
4183            if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
4184                // We re-bind these view holders after pre-processing is complete so that
4185                // ViewHolders have their final positions assigned.
4186                holder.addFlags(ViewHolder.FLAG_UPDATE);
4187                holder.addChangePayload(payload);
4188                // lp cannot be null since we get ViewHolder from it.
4189                ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
4190            }
4191        }
4192        mRecycler.viewRangeUpdate(positionStart, itemCount);
4193    }
4194
4195    boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
4196        return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
4197                viewHolder.getUnmodifiedPayloads());
4198    }
4199
4200
4201    /**
4202     * Call this method to signal that *all* adapter content has changed (generally, because of
4203     * swapAdapter, or notifyDataSetChanged), and that once layout occurs, all attached items should
4204     * be discarded or animated.
4205     *
4206     * Attached items are labeled as invalid, and all cached items are discarded.
4207     *
4208     * It is still possible for items to be prefetched while mDataSetHasChangedAfterLayout == true,
4209     * so this method must always discard all cached views so that the only valid items that remain
4210     * in the cache, once layout occurs, are valid prefetched items.
4211     */
4212    void setDataSetChangedAfterLayout() {
4213        mDataSetHasChangedAfterLayout = true;
4214        markKnownViewsInvalid();
4215    }
4216
4217    /**
4218     * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
4219     * data change event.
4220     */
4221    void markKnownViewsInvalid() {
4222        final int childCount = mChildHelper.getUnfilteredChildCount();
4223        for (int i = 0; i < childCount; i++) {
4224            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4225            if (holder != null && !holder.shouldIgnore()) {
4226                holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
4227            }
4228        }
4229        markItemDecorInsetsDirty();
4230        mRecycler.markKnownViewsInvalid();
4231    }
4232
4233    /**
4234     * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
4235     * will trigger a {@link #requestLayout()} call.
4236     */
4237    public void invalidateItemDecorations() {
4238        if (mItemDecorations.size() == 0) {
4239            return;
4240        }
4241        if (mLayout != null) {
4242            mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
4243                    + " or layout");
4244        }
4245        markItemDecorInsetsDirty();
4246        requestLayout();
4247    }
4248
4249    /**
4250     * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
4251     * focus even if the View representing the Item is replaced during a layout calculation.
4252     * <p>
4253     * By default, this value is {@code true}.
4254     *
4255     * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
4256     * focus.
4257     *
4258     * @see #setPreserveFocusAfterLayout(boolean)
4259     */
4260    public boolean getPreserveFocusAfterLayout() {
4261        return mPreserveFocusAfterLayout;
4262    }
4263
4264    /**
4265     * Set whether the RecyclerView should try to keep the same Item focused after a layout
4266     * calculation or not.
4267     * <p>
4268     * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
4269     * views may lose focus during a layout calculation as their state changes or they are replaced
4270     * with another view due to type change or animation. In these cases, RecyclerView can request
4271     * focus on the new view automatically.
4272     *
4273     * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
4274     *                                 layout calculations. Defaults to true.
4275     *
4276     * @see #getPreserveFocusAfterLayout()
4277     */
4278    public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
4279        mPreserveFocusAfterLayout = preserveFocusAfterLayout;
4280    }
4281
4282    /**
4283     * Retrieve the {@link ViewHolder} for the given child view.
4284     *
4285     * @param child Child of this RecyclerView to query for its ViewHolder
4286     * @return The child view's ViewHolder
4287     */
4288    public ViewHolder getChildViewHolder(View child) {
4289        final ViewParent parent = child.getParent();
4290        if (parent != null && parent != this) {
4291            throw new IllegalArgumentException("View " + child + " is not a direct child of "
4292                    + this);
4293        }
4294        return getChildViewHolderInt(child);
4295    }
4296
4297    /**
4298     * Traverses the ancestors of the given view and returns the item view that contains it and
4299     * also a direct child of the RecyclerView. This returned view can be used to get the
4300     * ViewHolder by calling {@link #getChildViewHolder(View)}.
4301     *
4302     * @param view The view that is a descendant of the RecyclerView.
4303     *
4304     * @return The direct child of the RecyclerView which contains the given view or null if the
4305     * provided view is not a descendant of this RecyclerView.
4306     *
4307     * @see #getChildViewHolder(View)
4308     * @see #findContainingViewHolder(View)
4309     */
4310    @Nullable
4311    public View findContainingItemView(View view) {
4312        ViewParent parent = view.getParent();
4313        while (parent != null && parent != this && parent instanceof View) {
4314            view = (View) parent;
4315            parent = view.getParent();
4316        }
4317        return parent == this ? view : null;
4318    }
4319
4320    /**
4321     * Returns the ViewHolder that contains the given view.
4322     *
4323     * @param view The view that is a descendant of the RecyclerView.
4324     *
4325     * @return The ViewHolder that contains the given view or null if the provided view is not a
4326     * descendant of this RecyclerView.
4327     */
4328    @Nullable
4329    public ViewHolder findContainingViewHolder(View view) {
4330        View itemView = findContainingItemView(view);
4331        return itemView == null ? null : getChildViewHolder(itemView);
4332    }
4333
4334
4335    static ViewHolder getChildViewHolderInt(View child) {
4336        if (child == null) {
4337            return null;
4338        }
4339        return ((LayoutParams) child.getLayoutParams()).mViewHolder;
4340    }
4341
4342    /**
4343     * @deprecated use {@link #getChildAdapterPosition(View)} or
4344     * {@link #getChildLayoutPosition(View)}.
4345     */
4346    @Deprecated
4347    public int getChildPosition(View child) {
4348        return getChildAdapterPosition(child);
4349    }
4350
4351    /**
4352     * Return the adapter position that the given child view corresponds to.
4353     *
4354     * @param child Child View to query
4355     * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
4356     */
4357    public int getChildAdapterPosition(View child) {
4358        final ViewHolder holder = getChildViewHolderInt(child);
4359        return holder != null ? holder.getAdapterPosition() : NO_POSITION;
4360    }
4361
4362    /**
4363     * Return the adapter position of the given child view as of the latest completed layout pass.
4364     * <p>
4365     * This position may not be equal to Item's adapter position if there are pending changes
4366     * in the adapter which have not been reflected to the layout yet.
4367     *
4368     * @param child Child View to query
4369     * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
4370     * the View is representing a removed item.
4371     */
4372    public int getChildLayoutPosition(View child) {
4373        final ViewHolder holder = getChildViewHolderInt(child);
4374        return holder != null ? holder.getLayoutPosition() : NO_POSITION;
4375    }
4376
4377    /**
4378     * Return the stable item id that the given child view corresponds to.
4379     *
4380     * @param child Child View to query
4381     * @return Item id corresponding to the given view or {@link #NO_ID}
4382     */
4383    public long getChildItemId(View child) {
4384        if (mAdapter == null || !mAdapter.hasStableIds()) {
4385            return NO_ID;
4386        }
4387        final ViewHolder holder = getChildViewHolderInt(child);
4388        return holder != null ? holder.getItemId() : NO_ID;
4389    }
4390
4391    /**
4392     * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
4393     * {@link #findViewHolderForAdapterPosition(int)}
4394     */
4395    @Deprecated
4396    public ViewHolder findViewHolderForPosition(int position) {
4397        return findViewHolderForPosition(position, false);
4398    }
4399
4400    /**
4401     * Return the ViewHolder for the item in the given position of the data set as of the latest
4402     * layout pass.
4403     * <p>
4404     * This method checks only the children of RecyclerView. If the item at the given
4405     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4406     * <p>
4407     * Note that when Adapter contents change, ViewHolder positions are not updated until the
4408     * next layout calculation. If there are pending adapter updates, the return value of this
4409     * method may not match your adapter contents. You can use
4410     * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
4411     * <p>
4412     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4413     * with the same layout position representing the same Item. In this case, the updated
4414     * ViewHolder will be returned.
4415     *
4416     * @param position The position of the item in the data set of the adapter
4417     * @return The ViewHolder at <code>position</code> or null if there is no such item
4418     */
4419    public ViewHolder findViewHolderForLayoutPosition(int position) {
4420        return findViewHolderForPosition(position, false);
4421    }
4422
4423    /**
4424     * Return the ViewHolder for the item in the given position of the data set. Unlike
4425     * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
4426     * adapter changes that may not be reflected to the layout yet. On the other hand, if
4427     * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
4428     * calculated yet, this method will return <code>null</code> since the new positions of views
4429     * are unknown until the layout is calculated.
4430     * <p>
4431     * This method checks only the children of RecyclerView. If the item at the given
4432     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4433     * <p>
4434     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4435     * representing the same Item. In this case, the updated ViewHolder will be returned.
4436     *
4437     * @param position The position of the item in the data set of the adapter
4438     * @return The ViewHolder at <code>position</code> or null if there is no such item
4439     */
4440    public ViewHolder findViewHolderForAdapterPosition(int position) {
4441        if (mDataSetHasChangedAfterLayout) {
4442            return null;
4443        }
4444        final int childCount = mChildHelper.getUnfilteredChildCount();
4445        // hidden VHs are not preferred but if that is the only one we find, we rather return it
4446        ViewHolder hidden = null;
4447        for (int i = 0; i < childCount; i++) {
4448            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4449            if (holder != null && !holder.isRemoved()
4450                    && getAdapterPositionFor(holder) == position) {
4451                if (mChildHelper.isHidden(holder.itemView)) {
4452                    hidden = holder;
4453                } else {
4454                    return holder;
4455                }
4456            }
4457        }
4458        return hidden;
4459    }
4460
4461    ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
4462        final int childCount = mChildHelper.getUnfilteredChildCount();
4463        ViewHolder hidden = null;
4464        for (int i = 0; i < childCount; i++) {
4465            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4466            if (holder != null && !holder.isRemoved()) {
4467                if (checkNewPosition) {
4468                    if (holder.mPosition != position) {
4469                        continue;
4470                    }
4471                } else if (holder.getLayoutPosition() != position) {
4472                    continue;
4473                }
4474                if (mChildHelper.isHidden(holder.itemView)) {
4475                    hidden = holder;
4476                } else {
4477                    return holder;
4478                }
4479            }
4480        }
4481        // This method should not query cached views. It creates a problem during adapter updates
4482        // when we are dealing with already laid out views. Also, for the public method, it is more
4483        // reasonable to return null if position is not laid out.
4484        return hidden;
4485    }
4486
4487    /**
4488     * Return the ViewHolder for the item with the given id. The RecyclerView must
4489     * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
4490     * return a non-null value.
4491     * <p>
4492     * This method checks only the children of RecyclerView. If the item with the given
4493     * <code>id</code> is not laid out, it <em>will not</em> create a new one.
4494     *
4495     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
4496     * same id. In this case, the updated ViewHolder will be returned.
4497     *
4498     * @param id The id for the requested item
4499     * @return The ViewHolder with the given <code>id</code> or null if there is no such item
4500     */
4501    public ViewHolder findViewHolderForItemId(long id) {
4502        if (mAdapter == null || !mAdapter.hasStableIds()) {
4503            return null;
4504        }
4505        final int childCount = mChildHelper.getUnfilteredChildCount();
4506        ViewHolder hidden = null;
4507        for (int i = 0; i < childCount; i++) {
4508            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4509            if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
4510                if (mChildHelper.isHidden(holder.itemView)) {
4511                    hidden = holder;
4512                } else {
4513                    return holder;
4514                }
4515            }
4516        }
4517        return hidden;
4518    }
4519
4520    /**
4521     * Find the topmost view under the given point.
4522     *
4523     * @param x Horizontal position in pixels to search
4524     * @param y Vertical position in pixels to search
4525     * @return The child view under (x, y) or null if no matching child is found
4526     */
4527    public View findChildViewUnder(float x, float y) {
4528        final int count = mChildHelper.getChildCount();
4529        for (int i = count - 1; i >= 0; i--) {
4530            final View child = mChildHelper.getChildAt(i);
4531            final float translationX = child.getTranslationX();
4532            final float translationY = child.getTranslationY();
4533            if (x >= child.getLeft() + translationX
4534                    && x <= child.getRight() + translationX
4535                    && y >= child.getTop() + translationY
4536                    && y <= child.getBottom() + translationY) {
4537                return child;
4538            }
4539        }
4540        return null;
4541    }
4542
4543    @Override
4544    public boolean drawChild(Canvas canvas, View child, long drawingTime) {
4545        return super.drawChild(canvas, child, drawingTime);
4546    }
4547
4548    /**
4549     * Offset the bounds of all child views by <code>dy</code> pixels.
4550     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4551     *
4552     * @param dy Vertical pixel offset to apply to the bounds of all child views
4553     */
4554    public void offsetChildrenVertical(int dy) {
4555        final int childCount = mChildHelper.getChildCount();
4556        for (int i = 0; i < childCount; i++) {
4557            mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
4558        }
4559    }
4560
4561    /**
4562     * Called when an item view is attached to this RecyclerView.
4563     *
4564     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4565     * of child views as they become attached. This will be called before a
4566     * {@link LayoutManager} measures or lays out the view and is a good time to perform these
4567     * changes.</p>
4568     *
4569     * @param child Child view that is now attached to this RecyclerView and its associated window
4570     */
4571    public void onChildAttachedToWindow(View child) {
4572    }
4573
4574    /**
4575     * Called when an item view is detached from this RecyclerView.
4576     *
4577     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4578     * of child views as they become detached. This will be called as a
4579     * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
4580     *
4581     * @param child Child view that is now detached from this RecyclerView and its associated window
4582     */
4583    public void onChildDetachedFromWindow(View child) {
4584    }
4585
4586    /**
4587     * Offset the bounds of all child views by <code>dx</code> pixels.
4588     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4589     *
4590     * @param dx Horizontal pixel offset to apply to the bounds of all child views
4591     */
4592    public void offsetChildrenHorizontal(int dx) {
4593        final int childCount = mChildHelper.getChildCount();
4594        for (int i = 0; i < childCount; i++) {
4595            mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
4596        }
4597    }
4598
4599    /**
4600     * Returns the bounds of the view including its decoration and margins.
4601     *
4602     * @param view The view element to check
4603     * @param outBounds A rect that will receive the bounds of the element including its
4604     *                  decoration and margins.
4605     */
4606    public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
4607        getDecoratedBoundsWithMarginsInt(view, outBounds);
4608    }
4609
4610    static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
4611        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
4612        final Rect insets = lp.mDecorInsets;
4613        outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
4614                view.getTop() - insets.top - lp.topMargin,
4615                view.getRight() + insets.right + lp.rightMargin,
4616                view.getBottom() + insets.bottom + lp.bottomMargin);
4617    }
4618
4619    Rect getItemDecorInsetsForChild(View child) {
4620        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
4621        if (!lp.mInsetsDirty) {
4622            return lp.mDecorInsets;
4623        }
4624
4625        if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
4626            // changed/invalid items should not be updated until they are rebound.
4627            return lp.mDecorInsets;
4628        }
4629        final Rect insets = lp.mDecorInsets;
4630        insets.set(0, 0, 0, 0);
4631        final int decorCount = mItemDecorations.size();
4632        for (int i = 0; i < decorCount; i++) {
4633            mTempRect.set(0, 0, 0, 0);
4634            mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
4635            insets.left += mTempRect.left;
4636            insets.top += mTempRect.top;
4637            insets.right += mTempRect.right;
4638            insets.bottom += mTempRect.bottom;
4639        }
4640        lp.mInsetsDirty = false;
4641        return insets;
4642    }
4643
4644    /**
4645     * Called when the scroll position of this RecyclerView changes. Subclasses should use
4646     * this method to respond to scrolling within the adapter's data set instead of an explicit
4647     * listener.
4648     *
4649     * <p>This method will always be invoked before listeners. If a subclass needs to perform
4650     * any additional upkeep or bookkeeping after scrolling but before listeners run,
4651     * this is a good place to do so.</p>
4652     *
4653     * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
4654     * the distance scrolled in either direction within the adapter's data set instead of absolute
4655     * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
4656     * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
4657     * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
4658     * do not correspond to the data set scroll position. However, some subclasses may choose
4659     * to use these fields as special offsets.</p>
4660     *
4661     * @param dx horizontal distance scrolled in pixels
4662     * @param dy vertical distance scrolled in pixels
4663     */
4664    public void onScrolled(int dx, int dy) {
4665        // Do nothing
4666    }
4667
4668    void dispatchOnScrolled(int hresult, int vresult) {
4669        mDispatchScrollCounter++;
4670        // Pass the current scrollX/scrollY values; no actual change in these properties occurred
4671        // but some general-purpose code may choose to respond to changes this way.
4672        final int scrollX = getScrollX();
4673        final int scrollY = getScrollY();
4674        onScrollChanged(scrollX, scrollY, scrollX, scrollY);
4675
4676        // Pass the real deltas to onScrolled, the RecyclerView-specific method.
4677        onScrolled(hresult, vresult);
4678
4679        // Invoke listeners last. Subclassed view methods always handle the event first.
4680        // All internal state is consistent by the time listeners are invoked.
4681        if (mScrollListener != null) {
4682            mScrollListener.onScrolled(this, hresult, vresult);
4683        }
4684        if (mScrollListeners != null) {
4685            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4686                mScrollListeners.get(i).onScrolled(this, hresult, vresult);
4687            }
4688        }
4689        mDispatchScrollCounter--;
4690    }
4691
4692    /**
4693     * Called when the scroll state of this RecyclerView changes. Subclasses should use this
4694     * method to respond to state changes instead of an explicit listener.
4695     *
4696     * <p>This method will always be invoked before listeners, but after the LayoutManager
4697     * responds to the scroll state change.</p>
4698     *
4699     * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
4700     *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
4701     */
4702    public void onScrollStateChanged(int state) {
4703        // Do nothing
4704    }
4705
4706    void dispatchOnScrollStateChanged(int state) {
4707        // Let the LayoutManager go first; this allows it to bring any properties into
4708        // a consistent state before the RecyclerView subclass responds.
4709        if (mLayout != null) {
4710            mLayout.onScrollStateChanged(state);
4711        }
4712
4713        // Let the RecyclerView subclass handle this event next; any LayoutManager property
4714        // changes will be reflected by this time.
4715        onScrollStateChanged(state);
4716
4717        // Listeners go last. All other internal state is consistent by this point.
4718        if (mScrollListener != null) {
4719            mScrollListener.onScrollStateChanged(this, state);
4720        }
4721        if (mScrollListeners != null) {
4722            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4723                mScrollListeners.get(i).onScrollStateChanged(this, state);
4724            }
4725        }
4726    }
4727
4728    /**
4729     * Returns whether there are pending adapter updates which are not yet applied to the layout.
4730     * <p>
4731     * If this method returns <code>true</code>, it means that what user is currently seeing may not
4732     * reflect them adapter contents (depending on what has changed).
4733     * You may use this information to defer or cancel some operations.
4734     * <p>
4735     * This method returns true if RecyclerView has not yet calculated the first layout after it is
4736     * attached to the Window or the Adapter has been replaced.
4737     *
4738     * @return True if there are some adapter updates which are not yet reflected to layout or false
4739     * if layout is up to date.
4740     */
4741    public boolean hasPendingAdapterUpdates() {
4742        return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
4743                || mAdapterHelper.hasPendingUpdates();
4744    }
4745
4746    class ViewFlinger implements Runnable {
4747        private int mLastFlingX;
4748        private int mLastFlingY;
4749        private OverScroller mScroller;
4750        Interpolator mInterpolator = sQuinticInterpolator;
4751
4752
4753        // When set to true, postOnAnimation callbacks are delayed until the run method completes
4754        private boolean mEatRunOnAnimationRequest = false;
4755
4756        // Tracks if postAnimationCallback should be re-attached when it is done
4757        private boolean mReSchedulePostAnimationCallback = false;
4758
4759        ViewFlinger() {
4760            mScroller = new OverScroller(getContext(), sQuinticInterpolator);
4761        }
4762
4763        @Override
4764        public void run() {
4765            if (mLayout == null) {
4766                stop();
4767                return; // no layout, cannot scroll.
4768            }
4769            disableRunOnAnimationRequests();
4770            consumePendingUpdateOperations();
4771            // keep a local reference so that if it is changed during onAnimation method, it won't
4772            // cause unexpected behaviors
4773            final OverScroller scroller = mScroller;
4774            final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
4775            if (scroller.computeScrollOffset()) {
4776                final int[] scrollConsumed = mScrollConsumed;
4777                final int x = scroller.getCurrX();
4778                final int y = scroller.getCurrY();
4779                int dx = x - mLastFlingX;
4780                int dy = y - mLastFlingY;
4781                int hresult = 0;
4782                int vresult = 0;
4783                mLastFlingX = x;
4784                mLastFlingY = y;
4785                int overscrollX = 0, overscrollY = 0;
4786
4787                if (dispatchNestedPreScroll(dx, dy, scrollConsumed, null, TYPE_NON_TOUCH)) {
4788                    dx -= scrollConsumed[0];
4789                    dy -= scrollConsumed[1];
4790                }
4791
4792                if (mAdapter != null) {
4793                    eatRequestLayout();
4794                    onEnterLayoutOrScroll();
4795                    TraceCompat.beginSection(TRACE_SCROLL_TAG);
4796                    if (dx != 0) {
4797                        hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
4798                        overscrollX = dx - hresult;
4799                    }
4800                    if (dy != 0) {
4801                        vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
4802                        overscrollY = dy - vresult;
4803                    }
4804                    TraceCompat.endSection();
4805                    repositionShadowingViews();
4806
4807                    onExitLayoutOrScroll();
4808                    resumeRequestLayout(false);
4809
4810                    if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
4811                            && smoothScroller.isRunning()) {
4812                        final int adapterSize = mState.getItemCount();
4813                        if (adapterSize == 0) {
4814                            smoothScroller.stop();
4815                        } else if (smoothScroller.getTargetPosition() >= adapterSize) {
4816                            smoothScroller.setTargetPosition(adapterSize - 1);
4817                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4818                        } else {
4819                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4820                        }
4821                    }
4822                }
4823                if (!mItemDecorations.isEmpty()) {
4824                    invalidate();
4825                }
4826                if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
4827                    considerReleasingGlowsOnScroll(dx, dy);
4828                }
4829
4830                if (!dispatchNestedScroll(hresult, vresult, overscrollX, overscrollY, null,
4831                        TYPE_NON_TOUCH)
4832                        && (overscrollX != 0 || overscrollY != 0)) {
4833                    final int vel = (int) scroller.getCurrVelocity();
4834
4835                    int velX = 0;
4836                    if (overscrollX != x) {
4837                        velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
4838                    }
4839
4840                    int velY = 0;
4841                    if (overscrollY != y) {
4842                        velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
4843                    }
4844
4845                    if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
4846                        absorbGlows(velX, velY);
4847                    }
4848                    if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
4849                            && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
4850                        scroller.abortAnimation();
4851                    }
4852                }
4853                if (hresult != 0 || vresult != 0) {
4854                    dispatchOnScrolled(hresult, vresult);
4855                }
4856
4857                if (!awakenScrollBars()) {
4858                    invalidate();
4859                }
4860
4861                final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
4862                        && vresult == dy;
4863                final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
4864                        && hresult == dx;
4865                final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
4866                        || fullyConsumedVertical;
4867
4868                if (scroller.isFinished() || (!fullyConsumedAny
4869                        && !hasNestedScrollingParent(TYPE_NON_TOUCH))) {
4870                    // setting state to idle will stop this.
4871                    setScrollState(SCROLL_STATE_IDLE);
4872                    if (ALLOW_THREAD_GAP_WORK) {
4873                        mPrefetchRegistry.clearPrefetchPositions();
4874                    }
4875                    stopNestedScroll(TYPE_NON_TOUCH);
4876                } else {
4877                    postOnAnimation();
4878                    if (mGapWorker != null) {
4879                        mGapWorker.postFromTraversal(RecyclerView.this, dx, dy);
4880                    }
4881                }
4882            }
4883            // call this after the onAnimation is complete not to have inconsistent callbacks etc.
4884            if (smoothScroller != null) {
4885                if (smoothScroller.isPendingInitialRun()) {
4886                    smoothScroller.onAnimation(0, 0);
4887                }
4888                if (!mReSchedulePostAnimationCallback) {
4889                    smoothScroller.stop(); //stop if it does not trigger any scroll
4890                }
4891            }
4892            enableRunOnAnimationRequests();
4893        }
4894
4895        private void disableRunOnAnimationRequests() {
4896            mReSchedulePostAnimationCallback = false;
4897            mEatRunOnAnimationRequest = true;
4898        }
4899
4900        private void enableRunOnAnimationRequests() {
4901            mEatRunOnAnimationRequest = false;
4902            if (mReSchedulePostAnimationCallback) {
4903                postOnAnimation();
4904            }
4905        }
4906
4907        void postOnAnimation() {
4908            if (mEatRunOnAnimationRequest) {
4909                mReSchedulePostAnimationCallback = true;
4910            } else {
4911                removeCallbacks(this);
4912                ViewCompat.postOnAnimation(RecyclerView.this, this);
4913            }
4914        }
4915
4916        public void fling(int velocityX, int velocityY) {
4917            setScrollState(SCROLL_STATE_SETTLING);
4918            mLastFlingX = mLastFlingY = 0;
4919            mScroller.fling(0, 0, velocityX, velocityY,
4920                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
4921            postOnAnimation();
4922        }
4923
4924        public void smoothScrollBy(int dx, int dy) {
4925            smoothScrollBy(dx, dy, 0, 0);
4926        }
4927
4928        public void smoothScrollBy(int dx, int dy, int vx, int vy) {
4929            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
4930        }
4931
4932        private float distanceInfluenceForSnapDuration(float f) {
4933            f -= 0.5f; // center the values about 0.
4934            f *= 0.3f * (float) Math.PI / 2.0f;
4935            return (float) Math.sin(f);
4936        }
4937
4938        private int computeScrollDuration(int dx, int dy, int vx, int vy) {
4939            final int absDx = Math.abs(dx);
4940            final int absDy = Math.abs(dy);
4941            final boolean horizontal = absDx > absDy;
4942            final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
4943            final int delta = (int) Math.sqrt(dx * dx + dy * dy);
4944            final int containerSize = horizontal ? getWidth() : getHeight();
4945            final int halfContainerSize = containerSize / 2;
4946            final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
4947            final float distance = halfContainerSize + halfContainerSize
4948                    * distanceInfluenceForSnapDuration(distanceRatio);
4949
4950            final int duration;
4951            if (velocity > 0) {
4952                duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
4953            } else {
4954                float absDelta = (float) (horizontal ? absDx : absDy);
4955                duration = (int) (((absDelta / containerSize) + 1) * 300);
4956            }
4957            return Math.min(duration, MAX_SCROLL_DURATION);
4958        }
4959
4960        public void smoothScrollBy(int dx, int dy, int duration) {
4961            smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
4962        }
4963
4964        public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
4965            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, 0, 0),
4966                    interpolator == null ? sQuinticInterpolator : interpolator);
4967        }
4968
4969        public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
4970            if (mInterpolator != interpolator) {
4971                mInterpolator = interpolator;
4972                mScroller = new OverScroller(getContext(), interpolator);
4973            }
4974            setScrollState(SCROLL_STATE_SETTLING);
4975            mLastFlingX = mLastFlingY = 0;
4976            mScroller.startScroll(0, 0, dx, dy, duration);
4977            postOnAnimation();
4978        }
4979
4980        public void stop() {
4981            removeCallbacks(this);
4982            mScroller.abortAnimation();
4983        }
4984
4985    }
4986
4987    void repositionShadowingViews() {
4988        // Fix up shadow views used by change animations
4989        int count = mChildHelper.getChildCount();
4990        for (int i = 0; i < count; i++) {
4991            View view = mChildHelper.getChildAt(i);
4992            ViewHolder holder = getChildViewHolder(view);
4993            if (holder != null && holder.mShadowingHolder != null) {
4994                View shadowingView = holder.mShadowingHolder.itemView;
4995                int left = view.getLeft();
4996                int top = view.getTop();
4997                if (left != shadowingView.getLeft() ||  top != shadowingView.getTop()) {
4998                    shadowingView.layout(left, top,
4999                            left + shadowingView.getWidth(),
5000                            top + shadowingView.getHeight());
5001                }
5002            }
5003        }
5004    }
5005
5006    private class RecyclerViewDataObserver extends AdapterDataObserver {
5007        RecyclerViewDataObserver() {
5008        }
5009
5010        @Override
5011        public void onChanged() {
5012            assertNotInLayoutOrScroll(null);
5013            mState.mStructureChanged = true;
5014
5015            setDataSetChangedAfterLayout();
5016            if (!mAdapterHelper.hasPendingUpdates()) {
5017                requestLayout();
5018            }
5019        }
5020
5021        @Override
5022        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
5023            assertNotInLayoutOrScroll(null);
5024            if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
5025                triggerUpdateProcessor();
5026            }
5027        }
5028
5029        @Override
5030        public void onItemRangeInserted(int positionStart, int itemCount) {
5031            assertNotInLayoutOrScroll(null);
5032            if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
5033                triggerUpdateProcessor();
5034            }
5035        }
5036
5037        @Override
5038        public void onItemRangeRemoved(int positionStart, int itemCount) {
5039            assertNotInLayoutOrScroll(null);
5040            if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
5041                triggerUpdateProcessor();
5042            }
5043        }
5044
5045        @Override
5046        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
5047            assertNotInLayoutOrScroll(null);
5048            if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
5049                triggerUpdateProcessor();
5050            }
5051        }
5052
5053        void triggerUpdateProcessor() {
5054            if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
5055                ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
5056            } else {
5057                mAdapterUpdateDuringMeasure = true;
5058                requestLayout();
5059            }
5060        }
5061    }
5062
5063    /**
5064     * RecycledViewPool lets you share Views between multiple RecyclerViews.
5065     * <p>
5066     * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
5067     * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
5068     * <p>
5069     * RecyclerView automatically creates a pool for itself if you don't provide one.
5070     *
5071     */
5072    public static class RecycledViewPool {
5073        private static final int DEFAULT_MAX_SCRAP = 5;
5074
5075        /**
5076         * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
5077         *
5078         * Note that this tracks running averages of create/bind time across all RecyclerViews
5079         * (and, indirectly, Adapters) that use this pool.
5080         *
5081         * 1) This enables us to track average create and bind times across multiple adapters. Even
5082         * though create (and especially bind) may behave differently for different Adapter
5083         * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
5084         *
5085         * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
5086         * false for all other views of its type for the same deadline. This prevents items
5087         * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
5088         */
5089        static class ScrapData {
5090            ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
5091            int mMaxScrap = DEFAULT_MAX_SCRAP;
5092            long mCreateRunningAverageNs = 0;
5093            long mBindRunningAverageNs = 0;
5094        }
5095        SparseArray<ScrapData> mScrap = new SparseArray<>();
5096
5097        private int mAttachCount = 0;
5098
5099        public void clear() {
5100            for (int i = 0; i < mScrap.size(); i++) {
5101                ScrapData data = mScrap.valueAt(i);
5102                data.mScrapHeap.clear();
5103            }
5104        }
5105
5106        public void setMaxRecycledViews(int viewType, int max) {
5107            ScrapData scrapData = getScrapDataForType(viewType);
5108            scrapData.mMaxScrap = max;
5109            final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
5110            if (scrapHeap != null) {
5111                while (scrapHeap.size() > max) {
5112                    scrapHeap.remove(scrapHeap.size() - 1);
5113                }
5114            }
5115        }
5116
5117        /**
5118         * Returns the current number of Views held by the RecycledViewPool of the given view type.
5119         */
5120        public int getRecycledViewCount(int viewType) {
5121            return getScrapDataForType(viewType).mScrapHeap.size();
5122        }
5123
5124        public ViewHolder getRecycledView(int viewType) {
5125            final ScrapData scrapData = mScrap.get(viewType);
5126            if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
5127                final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
5128                return scrapHeap.remove(scrapHeap.size() - 1);
5129            }
5130            return null;
5131        }
5132
5133        int size() {
5134            int count = 0;
5135            for (int i = 0; i < mScrap.size(); i++) {
5136                ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
5137                if (viewHolders != null) {
5138                    count += viewHolders.size();
5139                }
5140            }
5141            return count;
5142        }
5143
5144        public void putRecycledView(ViewHolder scrap) {
5145            final int viewType = scrap.getItemViewType();
5146            final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
5147            if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
5148                return;
5149            }
5150            if (DEBUG && scrapHeap.contains(scrap)) {
5151                throw new IllegalArgumentException("this scrap item already exists");
5152            }
5153            scrap.resetInternal();
5154            scrapHeap.add(scrap);
5155        }
5156
5157        long runningAverage(long oldAverage, long newValue) {
5158            if (oldAverage == 0) {
5159                return newValue;
5160            }
5161            return (oldAverage / 4 * 3) + (newValue / 4);
5162        }
5163
5164        void factorInCreateTime(int viewType, long createTimeNs) {
5165            ScrapData scrapData = getScrapDataForType(viewType);
5166            scrapData.mCreateRunningAverageNs = runningAverage(
5167                    scrapData.mCreateRunningAverageNs, createTimeNs);
5168        }
5169
5170        void factorInBindTime(int viewType, long bindTimeNs) {
5171            ScrapData scrapData = getScrapDataForType(viewType);
5172            scrapData.mBindRunningAverageNs = runningAverage(
5173                    scrapData.mBindRunningAverageNs, bindTimeNs);
5174        }
5175
5176        boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5177            long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
5178            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5179        }
5180
5181        boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5182            long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
5183            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5184        }
5185
5186        void attach(Adapter adapter) {
5187            mAttachCount++;
5188        }
5189
5190        void detach() {
5191            mAttachCount--;
5192        }
5193
5194
5195        /**
5196         * Detaches the old adapter and attaches the new one.
5197         * <p>
5198         * RecycledViewPool will clear its cache if it has only one adapter attached and the new
5199         * adapter uses a different ViewHolder than the oldAdapter.
5200         *
5201         * @param oldAdapter The previous adapter instance. Will be detached.
5202         * @param newAdapter The new adapter instance. Will be attached.
5203         * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
5204         *                               ViewHolder and view types.
5205         */
5206        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
5207                boolean compatibleWithPrevious) {
5208            if (oldAdapter != null) {
5209                detach();
5210            }
5211            if (!compatibleWithPrevious && mAttachCount == 0) {
5212                clear();
5213            }
5214            if (newAdapter != null) {
5215                attach(newAdapter);
5216            }
5217        }
5218
5219        private ScrapData getScrapDataForType(int viewType) {
5220            ScrapData scrapData = mScrap.get(viewType);
5221            if (scrapData == null) {
5222                scrapData = new ScrapData();
5223                mScrap.put(viewType, scrapData);
5224            }
5225            return scrapData;
5226        }
5227    }
5228
5229    /**
5230     * Utility method for finding an internal RecyclerView, if present
5231     */
5232    @Nullable
5233    static RecyclerView findNestedRecyclerView(@NonNull View view) {
5234        if (!(view instanceof ViewGroup)) {
5235            return null;
5236        }
5237        if (view instanceof RecyclerView) {
5238            return (RecyclerView) view;
5239        }
5240        final ViewGroup parent = (ViewGroup) view;
5241        final int count = parent.getChildCount();
5242        for (int i = 0; i < count; i++) {
5243            final View child = parent.getChildAt(i);
5244            final RecyclerView descendant = findNestedRecyclerView(child);
5245            if (descendant != null) {
5246                return descendant;
5247            }
5248        }
5249        return null;
5250    }
5251
5252    /**
5253     * Utility method for clearing holder's internal RecyclerView, if present
5254     */
5255    static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
5256        if (holder.mNestedRecyclerView != null) {
5257            View item = holder.mNestedRecyclerView.get();
5258            while (item != null) {
5259                if (item == holder.itemView) {
5260                    return; // match found, don't need to clear
5261                }
5262
5263                ViewParent parent = item.getParent();
5264                if (parent instanceof View) {
5265                    item = (View) parent;
5266                } else {
5267                    item = null;
5268                }
5269            }
5270            holder.mNestedRecyclerView = null; // not nested
5271        }
5272    }
5273
5274    /**
5275     * Time base for deadline-aware work scheduling. Overridable for testing.
5276     *
5277     * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
5278     * isn't relevant.
5279     */
5280    long getNanoTime() {
5281        if (ALLOW_THREAD_GAP_WORK) {
5282            return System.nanoTime();
5283        } else {
5284            return 0;
5285        }
5286    }
5287
5288    /**
5289     * A Recycler is responsible for managing scrapped or detached item views for reuse.
5290     *
5291     * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
5292     * that has been marked for removal or reuse.</p>
5293     *
5294     * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
5295     * an adapter's data set representing the data at a given position or item ID.
5296     * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
5297     * If not, the view can be quickly reused by the LayoutManager with no further work.
5298     * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
5299     * may be repositioned by a LayoutManager without remeasurement.</p>
5300     */
5301    public final class Recycler {
5302        final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
5303        ArrayList<ViewHolder> mChangedScrap = null;
5304
5305        final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
5306
5307        private final List<ViewHolder>
5308                mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
5309
5310        private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
5311        int mViewCacheMax = DEFAULT_CACHE_SIZE;
5312
5313        RecycledViewPool mRecyclerPool;
5314
5315        private ViewCacheExtension mViewCacheExtension;
5316
5317        static final int DEFAULT_CACHE_SIZE = 2;
5318
5319        /**
5320         * Clear scrap views out of this recycler. Detached views contained within a
5321         * recycled view pool will remain.
5322         */
5323        public void clear() {
5324            mAttachedScrap.clear();
5325            recycleAndClearCachedViews();
5326        }
5327
5328        /**
5329         * Set the maximum number of detached, valid views we should retain for later use.
5330         *
5331         * @param viewCount Number of views to keep before sending views to the shared pool
5332         */
5333        public void setViewCacheSize(int viewCount) {
5334            mRequestedCacheMax = viewCount;
5335            updateViewCacheSize();
5336        }
5337
5338        void updateViewCacheSize() {
5339            int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
5340            mViewCacheMax = mRequestedCacheMax + extraCache;
5341
5342            // first, try the views that can be recycled
5343            for (int i = mCachedViews.size() - 1;
5344                    i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
5345                recycleCachedViewAt(i);
5346            }
5347        }
5348
5349        /**
5350         * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
5351         *
5352         * @return List of ViewHolders in the scrap list.
5353         */
5354        public List<ViewHolder> getScrapList() {
5355            return mUnmodifiableAttachedScrap;
5356        }
5357
5358        /**
5359         * Helper method for getViewForPosition.
5360         * <p>
5361         * Checks whether a given view holder can be used for the provided position.
5362         *
5363         * @param holder ViewHolder
5364         * @return true if ViewHolder matches the provided position, false otherwise
5365         */
5366        boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
5367            // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
5368            // if it is not removed, verify the type and id.
5369            if (holder.isRemoved()) {
5370                if (DEBUG && !mState.isPreLayout()) {
5371                    throw new IllegalStateException("should not receive a removed view unless it"
5372                            + " is pre layout");
5373                }
5374                return mState.isPreLayout();
5375            }
5376            if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
5377                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
5378                        + "adapter position" + holder);
5379            }
5380            if (!mState.isPreLayout()) {
5381                // don't check type if it is pre-layout.
5382                final int type = mAdapter.getItemViewType(holder.mPosition);
5383                if (type != holder.getItemViewType()) {
5384                    return false;
5385                }
5386            }
5387            if (mAdapter.hasStableIds()) {
5388                return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
5389            }
5390            return true;
5391        }
5392
5393        /**
5394         * Attempts to bind view, and account for relevant timing information. If
5395         * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
5396         *
5397         * @param holder Holder to be bound.
5398         * @param offsetPosition Position of item to be bound.
5399         * @param position Pre-layout position of item to be bound.
5400         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5401         *                   complete. If FOREVER_NS is passed, this method will not fail to
5402         *                   bind the holder.
5403         * @return
5404         */
5405        private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,
5406                int position, long deadlineNs) {
5407            holder.mOwnerRecyclerView = RecyclerView.this;
5408            final int viewType = holder.getItemViewType();
5409            long startBindNs = getNanoTime();
5410            if (deadlineNs != FOREVER_NS
5411                    && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
5412                // abort - we have a deadline we can't meet
5413                return false;
5414            }
5415            mAdapter.bindViewHolder(holder, offsetPosition);
5416            long endBindNs = getNanoTime();
5417            mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
5418            attachAccessibilityDelegateOnBind(holder);
5419            if (mState.isPreLayout()) {
5420                holder.mPreLayoutPosition = position;
5421            }
5422            return true;
5423        }
5424
5425        /**
5426         * Binds the given View to the position. The View can be a View previously retrieved via
5427         * {@link #getViewForPosition(int)} or created by
5428         * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
5429         * <p>
5430         * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
5431         * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
5432         * wants to handle its own recycling logic.
5433         * <p>
5434         * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
5435         * you don't need to call this method unless you want to bind this View to another position.
5436         *
5437         * @param view The view to update.
5438         * @param position The position of the item to bind to this View.
5439         */
5440        public void bindViewToPosition(View view, int position) {
5441            ViewHolder holder = getChildViewHolderInt(view);
5442            if (holder == null) {
5443                throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
5444                        + " pass arbitrary views to this method, they should be created by the "
5445                        + "Adapter");
5446            }
5447            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5448            if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5449                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5450                        + "position " + position + "(offset:" + offsetPosition + ")."
5451                        + "state:" + mState.getItemCount());
5452            }
5453            tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
5454
5455            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5456            final LayoutParams rvLayoutParams;
5457            if (lp == null) {
5458                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5459                holder.itemView.setLayoutParams(rvLayoutParams);
5460            } else if (!checkLayoutParams(lp)) {
5461                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5462                holder.itemView.setLayoutParams(rvLayoutParams);
5463            } else {
5464                rvLayoutParams = (LayoutParams) lp;
5465            }
5466
5467            rvLayoutParams.mInsetsDirty = true;
5468            rvLayoutParams.mViewHolder = holder;
5469            rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
5470        }
5471
5472        /**
5473         * RecyclerView provides artificial position range (item count) in pre-layout state and
5474         * automatically maps these positions to {@link Adapter} positions when
5475         * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
5476         * <p>
5477         * Usually, LayoutManager does not need to worry about this. However, in some cases, your
5478         * LayoutManager may need to call some custom component with item positions in which
5479         * case you need the actual adapter position instead of the pre layout position. You
5480         * can use this method to convert a pre-layout position to adapter (post layout) position.
5481         * <p>
5482         * Note that if the provided position belongs to a deleted ViewHolder, this method will
5483         * return -1.
5484         * <p>
5485         * Calling this method in post-layout state returns the same value back.
5486         *
5487         * @param position The pre-layout position to convert. Must be greater or equal to 0 and
5488         *                 less than {@link State#getItemCount()}.
5489         */
5490        public int convertPreLayoutPositionToPostLayout(int position) {
5491            if (position < 0 || position >= mState.getItemCount()) {
5492                throw new IndexOutOfBoundsException("invalid position " + position + ". State "
5493                        + "item count is " + mState.getItemCount());
5494            }
5495            if (!mState.isPreLayout()) {
5496                return position;
5497            }
5498            return mAdapterHelper.findPositionOffset(position);
5499        }
5500
5501        /**
5502         * Obtain a view initialized for the given position.
5503         *
5504         * This method should be used by {@link LayoutManager} implementations to obtain
5505         * views to represent data from an {@link Adapter}.
5506         * <p>
5507         * The Recycler may reuse a scrap or detached view from a shared pool if one is
5508         * available for the correct view type. If the adapter has not indicated that the
5509         * data at the given position has changed, the Recycler will attempt to hand back
5510         * a scrap view that was previously initialized for that data without rebinding.
5511         *
5512         * @param position Position to obtain a view for
5513         * @return A view representing the data at <code>position</code> from <code>adapter</code>
5514         */
5515        public View getViewForPosition(int position) {
5516            return getViewForPosition(position, false);
5517        }
5518
5519        View getViewForPosition(int position, boolean dryRun) {
5520            return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
5521        }
5522
5523        /**
5524         * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
5525         * cache, the RecycledViewPool, or creating it directly.
5526         * <p>
5527         * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
5528         * rather than constructing or binding a ViewHolder if it doesn't think it has time.
5529         * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
5530         * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
5531         * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
5532         *
5533         * @param position Position of ViewHolder to be returned.
5534         * @param dryRun True if the ViewHolder should not be removed from scrap/cache/
5535         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5536         *                   complete. If FOREVER_NS is passed, this method will not fail to
5537         *                   create/bind the holder if needed.
5538         *
5539         * @return ViewHolder for requested position
5540         */
5541        @Nullable
5542        ViewHolder tryGetViewHolderForPositionByDeadline(int position,
5543                boolean dryRun, long deadlineNs) {
5544            if (position < 0 || position >= mState.getItemCount()) {
5545                throw new IndexOutOfBoundsException("Invalid item position " + position
5546                        + "(" + position + "). Item count:" + mState.getItemCount());
5547            }
5548            boolean fromScrapOrHiddenOrCache = false;
5549            ViewHolder holder = null;
5550            // 0) If there is a changed scrap, try to find from there
5551            if (mState.isPreLayout()) {
5552                holder = getChangedScrapViewForPosition(position);
5553                fromScrapOrHiddenOrCache = holder != null;
5554            }
5555            // 1) Find by position from scrap/hidden list/cache
5556            if (holder == null) {
5557                holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
5558                if (holder != null) {
5559                    if (!validateViewHolderForOffsetPosition(holder)) {
5560                        // recycle holder (and unscrap if relevant) since it can't be used
5561                        if (!dryRun) {
5562                            // we would like to recycle this but need to make sure it is not used by
5563                            // animation logic etc.
5564                            holder.addFlags(ViewHolder.FLAG_INVALID);
5565                            if (holder.isScrap()) {
5566                                removeDetachedView(holder.itemView, false);
5567                                holder.unScrap();
5568                            } else if (holder.wasReturnedFromScrap()) {
5569                                holder.clearReturnedFromScrapFlag();
5570                            }
5571                            recycleViewHolderInternal(holder);
5572                        }
5573                        holder = null;
5574                    } else {
5575                        fromScrapOrHiddenOrCache = true;
5576                    }
5577                }
5578            }
5579            if (holder == null) {
5580                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5581                if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5582                    throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5583                            + "position " + position + "(offset:" + offsetPosition + ")."
5584                            + "state:" + mState.getItemCount());
5585                }
5586
5587                final int type = mAdapter.getItemViewType(offsetPosition);
5588                // 2) Find from scrap/cache via stable ids, if exists
5589                if (mAdapter.hasStableIds()) {
5590                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
5591                            type, dryRun);
5592                    if (holder != null) {
5593                        // update position
5594                        holder.mPosition = offsetPosition;
5595                        fromScrapOrHiddenOrCache = true;
5596                    }
5597                }
5598                if (holder == null && mViewCacheExtension != null) {
5599                    // We are NOT sending the offsetPosition because LayoutManager does not
5600                    // know it.
5601                    final View view = mViewCacheExtension
5602                            .getViewForPositionAndType(this, position, type);
5603                    if (view != null) {
5604                        holder = getChildViewHolder(view);
5605                        if (holder == null) {
5606                            throw new IllegalArgumentException("getViewForPositionAndType returned"
5607                                    + " a view which does not have a ViewHolder");
5608                        } else if (holder.shouldIgnore()) {
5609                            throw new IllegalArgumentException("getViewForPositionAndType returned"
5610                                    + " a view that is ignored. You must call stopIgnoring before"
5611                                    + " returning this view.");
5612                        }
5613                    }
5614                }
5615                if (holder == null) { // fallback to pool
5616                    if (DEBUG) {
5617                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
5618                                + position + ") fetching from shared pool");
5619                    }
5620                    holder = getRecycledViewPool().getRecycledView(type);
5621                    if (holder != null) {
5622                        holder.resetInternal();
5623                        if (FORCE_INVALIDATE_DISPLAY_LIST) {
5624                            invalidateDisplayListInt(holder);
5625                        }
5626                    }
5627                }
5628                if (holder == null) {
5629                    long start = getNanoTime();
5630                    if (deadlineNs != FOREVER_NS
5631                            && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
5632                        // abort - we have a deadline we can't meet
5633                        return null;
5634                    }
5635                    holder = mAdapter.createViewHolder(RecyclerView.this, type);
5636                    if (ALLOW_THREAD_GAP_WORK) {
5637                        // only bother finding nested RV if prefetching
5638                        RecyclerView innerView = findNestedRecyclerView(holder.itemView);
5639                        if (innerView != null) {
5640                            holder.mNestedRecyclerView = new WeakReference<>(innerView);
5641                        }
5642                    }
5643
5644                    long end = getNanoTime();
5645                    mRecyclerPool.factorInCreateTime(type, end - start);
5646                    if (DEBUG) {
5647                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
5648                    }
5649                }
5650            }
5651
5652            // This is very ugly but the only place we can grab this information
5653            // before the View is rebound and returned to the LayoutManager for post layout ops.
5654            // We don't need this in pre-layout since the VH is not updated by the LM.
5655            if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
5656                    .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
5657                holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5658                if (mState.mRunSimpleAnimations) {
5659                    int changeFlags = ItemAnimator
5660                            .buildAdapterChangeFlagsForAnimations(holder);
5661                    changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
5662                    final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
5663                            holder, changeFlags, holder.getUnmodifiedPayloads());
5664                    recordAnimationInfoIfBouncedHiddenView(holder, info);
5665                }
5666            }
5667
5668            boolean bound = false;
5669            if (mState.isPreLayout() && holder.isBound()) {
5670                // do not update unless we absolutely have to.
5671                holder.mPreLayoutPosition = position;
5672            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
5673                if (DEBUG && holder.isRemoved()) {
5674                    throw new IllegalStateException("Removed holder should be bound and it should"
5675                            + " come here only in pre-layout. Holder: " + holder);
5676                }
5677                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5678                bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
5679            }
5680
5681            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5682            final LayoutParams rvLayoutParams;
5683            if (lp == null) {
5684                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5685                holder.itemView.setLayoutParams(rvLayoutParams);
5686            } else if (!checkLayoutParams(lp)) {
5687                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5688                holder.itemView.setLayoutParams(rvLayoutParams);
5689            } else {
5690                rvLayoutParams = (LayoutParams) lp;
5691            }
5692            rvLayoutParams.mViewHolder = holder;
5693            rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
5694            return holder;
5695        }
5696
5697        private void attachAccessibilityDelegateOnBind(ViewHolder holder) {
5698            if (isAccessibilityEnabled()) {
5699                final View itemView = holder.itemView;
5700                if (ViewCompat.getImportantForAccessibility(itemView)
5701                        == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
5702                    ViewCompat.setImportantForAccessibility(itemView,
5703                            ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
5704                }
5705                if (!ViewCompat.hasAccessibilityDelegate(itemView)) {
5706                    holder.addFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
5707                    ViewCompat.setAccessibilityDelegate(itemView,
5708                            mAccessibilityDelegate.getItemDelegate());
5709                }
5710            }
5711        }
5712
5713        private void invalidateDisplayListInt(ViewHolder holder) {
5714            if (holder.itemView instanceof ViewGroup) {
5715                invalidateDisplayListInt((ViewGroup) holder.itemView, false);
5716            }
5717        }
5718
5719        private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
5720            for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
5721                final View view = viewGroup.getChildAt(i);
5722                if (view instanceof ViewGroup) {
5723                    invalidateDisplayListInt((ViewGroup) view, true);
5724                }
5725            }
5726            if (!invalidateThis) {
5727                return;
5728            }
5729            // we need to force it to become invisible
5730            if (viewGroup.getVisibility() == View.INVISIBLE) {
5731                viewGroup.setVisibility(View.VISIBLE);
5732                viewGroup.setVisibility(View.INVISIBLE);
5733            } else {
5734                final int visibility = viewGroup.getVisibility();
5735                viewGroup.setVisibility(View.INVISIBLE);
5736                viewGroup.setVisibility(visibility);
5737            }
5738        }
5739
5740        /**
5741         * Recycle a detached view. The specified view will be added to a pool of views
5742         * for later rebinding and reuse.
5743         *
5744         * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
5745         * View is scrapped, it will be removed from scrap list.</p>
5746         *
5747         * @param view Removed view for recycling
5748         * @see LayoutManager#removeAndRecycleView(View, Recycler)
5749         */
5750        public void recycleView(View view) {
5751            // This public recycle method tries to make view recycle-able since layout manager
5752            // intended to recycle this view (e.g. even if it is in scrap or change cache)
5753            ViewHolder holder = getChildViewHolderInt(view);
5754            if (holder.isTmpDetached()) {
5755                removeDetachedView(view, false);
5756            }
5757            if (holder.isScrap()) {
5758                holder.unScrap();
5759            } else if (holder.wasReturnedFromScrap()) {
5760                holder.clearReturnedFromScrapFlag();
5761            }
5762            recycleViewHolderInternal(holder);
5763        }
5764
5765        /**
5766         * Internally, use this method instead of {@link #recycleView(android.view.View)} to
5767         * catch potential bugs.
5768         * @param view
5769         */
5770        void recycleViewInternal(View view) {
5771            recycleViewHolderInternal(getChildViewHolderInt(view));
5772        }
5773
5774        void recycleAndClearCachedViews() {
5775            final int count = mCachedViews.size();
5776            for (int i = count - 1; i >= 0; i--) {
5777                recycleCachedViewAt(i);
5778            }
5779            mCachedViews.clear();
5780            if (ALLOW_THREAD_GAP_WORK) {
5781                mPrefetchRegistry.clearPrefetchPositions();
5782            }
5783        }
5784
5785        /**
5786         * Recycles a cached view and removes the view from the list. Views are added to cache
5787         * if and only if they are recyclable, so this method does not check it again.
5788         * <p>
5789         * A small exception to this rule is when the view does not have an animator reference
5790         * but transient state is true (due to animations created outside ItemAnimator). In that
5791         * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
5792         * still recyclable since Adapter wants to do so.
5793         *
5794         * @param cachedViewIndex The index of the view in cached views list
5795         */
5796        void recycleCachedViewAt(int cachedViewIndex) {
5797            if (DEBUG) {
5798                Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
5799            }
5800            ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
5801            if (DEBUG) {
5802                Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
5803            }
5804            addViewHolderToRecycledViewPool(viewHolder, true);
5805            mCachedViews.remove(cachedViewIndex);
5806        }
5807
5808        /**
5809         * internal implementation checks if view is scrapped or attached and throws an exception
5810         * if so.
5811         * Public version un-scraps before calling recycle.
5812         */
5813        void recycleViewHolderInternal(ViewHolder holder) {
5814            if (holder.isScrap() || holder.itemView.getParent() != null) {
5815                throw new IllegalArgumentException(
5816                        "Scrapped or attached views may not be recycled. isScrap:"
5817                                + holder.isScrap() + " isAttached:"
5818                                + (holder.itemView.getParent() != null));
5819            }
5820
5821            if (holder.isTmpDetached()) {
5822                throw new IllegalArgumentException("Tmp detached view should be removed "
5823                        + "from RecyclerView before it can be recycled: " + holder);
5824            }
5825
5826            if (holder.shouldIgnore()) {
5827                throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
5828                        + " should first call stopIgnoringView(view) before calling recycle.");
5829            }
5830            //noinspection unchecked
5831            final boolean transientStatePreventsRecycling = holder
5832                    .doesTransientStatePreventRecycling();
5833            final boolean forceRecycle = mAdapter != null
5834                    && transientStatePreventsRecycling
5835                    && mAdapter.onFailedToRecycleView(holder);
5836            boolean cached = false;
5837            boolean recycled = false;
5838            if (DEBUG && mCachedViews.contains(holder)) {
5839                throw new IllegalArgumentException("cached view received recycle internal? "
5840                        + holder);
5841            }
5842            if (forceRecycle || holder.isRecyclable()) {
5843                if (mViewCacheMax > 0
5844                        && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
5845                        | ViewHolder.FLAG_REMOVED
5846                        | ViewHolder.FLAG_UPDATE
5847                        | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
5848                    // Retire oldest cached view
5849                    int cachedViewSize = mCachedViews.size();
5850                    if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
5851                        recycleCachedViewAt(0);
5852                        cachedViewSize--;
5853                    }
5854
5855                    int targetCacheIndex = cachedViewSize;
5856                    if (ALLOW_THREAD_GAP_WORK
5857                            && cachedViewSize > 0
5858                            && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
5859                        // when adding the view, skip past most recently prefetched views
5860                        int cacheIndex = cachedViewSize - 1;
5861                        while (cacheIndex >= 0) {
5862                            int cachedPos = mCachedViews.get(cacheIndex).mPosition;
5863                            if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
5864                                break;
5865                            }
5866                            cacheIndex--;
5867                        }
5868                        targetCacheIndex = cacheIndex + 1;
5869                    }
5870                    mCachedViews.add(targetCacheIndex, holder);
5871                    cached = true;
5872                }
5873                if (!cached) {
5874                    addViewHolderToRecycledViewPool(holder, true);
5875                    recycled = true;
5876                }
5877            } else {
5878                // NOTE: A view can fail to be recycled when it is scrolled off while an animation
5879                // runs. In this case, the item is eventually recycled by
5880                // ItemAnimatorRestoreListener#onAnimationFinished.
5881
5882                // TODO: consider cancelling an animation when an item is removed scrollBy,
5883                // to return it to the pool faster
5884                if (DEBUG) {
5885                    Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
5886                            + "re-visit here. We are still removing it from animation lists");
5887                }
5888            }
5889            // even if the holder is not removed, we still call this method so that it is removed
5890            // from view holder lists.
5891            mViewInfoStore.removeViewHolder(holder);
5892            if (!cached && !recycled && transientStatePreventsRecycling) {
5893                holder.mOwnerRecyclerView = null;
5894            }
5895        }
5896
5897        /**
5898         * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
5899         *
5900         * Pass false to dispatchRecycled for views that have not been bound.
5901         *
5902         * @param holder Holder to be added to the pool.
5903         * @param dispatchRecycled True to dispatch View recycled callbacks.
5904         */
5905        void addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) {
5906            clearNestedRecyclerViewIfNotNested(holder);
5907            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE)) {
5908                holder.setFlags(0, ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
5909                ViewCompat.setAccessibilityDelegate(holder.itemView, null);
5910            }
5911            if (dispatchRecycled) {
5912                dispatchViewRecycled(holder);
5913            }
5914            holder.mOwnerRecyclerView = null;
5915            getRecycledViewPool().putRecycledView(holder);
5916        }
5917
5918        /**
5919         * Used as a fast path for unscrapping and recycling a view during a bulk operation.
5920         * The caller must call {@link #clearScrap()} when it's done to update the recycler's
5921         * internal bookkeeping.
5922         */
5923        void quickRecycleScrapView(View view) {
5924            final ViewHolder holder = getChildViewHolderInt(view);
5925            holder.mScrapContainer = null;
5926            holder.mInChangeScrap = false;
5927            holder.clearReturnedFromScrapFlag();
5928            recycleViewHolderInternal(holder);
5929        }
5930
5931        /**
5932         * Mark an attached view as scrap.
5933         *
5934         * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
5935         * for rebinding and reuse. Requests for a view for a given position may return a
5936         * reused or rebound scrap view instance.</p>
5937         *
5938         * @param view View to scrap
5939         */
5940        void scrapView(View view) {
5941            final ViewHolder holder = getChildViewHolderInt(view);
5942            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
5943                    || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
5944                if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
5945                    throw new IllegalArgumentException("Called scrap view with an invalid view."
5946                            + " Invalid views cannot be reused from scrap, they should rebound from"
5947                            + " recycler pool.");
5948                }
5949                holder.setScrapContainer(this, false);
5950                mAttachedScrap.add(holder);
5951            } else {
5952                if (mChangedScrap == null) {
5953                    mChangedScrap = new ArrayList<ViewHolder>();
5954                }
5955                holder.setScrapContainer(this, true);
5956                mChangedScrap.add(holder);
5957            }
5958        }
5959
5960        /**
5961         * Remove a previously scrapped view from the pool of eligible scrap.
5962         *
5963         * <p>This view will no longer be eligible for reuse until re-scrapped or
5964         * until it is explicitly removed and recycled.</p>
5965         */
5966        void unscrapView(ViewHolder holder) {
5967            if (holder.mInChangeScrap) {
5968                mChangedScrap.remove(holder);
5969            } else {
5970                mAttachedScrap.remove(holder);
5971            }
5972            holder.mScrapContainer = null;
5973            holder.mInChangeScrap = false;
5974            holder.clearReturnedFromScrapFlag();
5975        }
5976
5977        int getScrapCount() {
5978            return mAttachedScrap.size();
5979        }
5980
5981        View getScrapViewAt(int index) {
5982            return mAttachedScrap.get(index).itemView;
5983        }
5984
5985        void clearScrap() {
5986            mAttachedScrap.clear();
5987            if (mChangedScrap != null) {
5988                mChangedScrap.clear();
5989            }
5990        }
5991
5992        ViewHolder getChangedScrapViewForPosition(int position) {
5993            // If pre-layout, check the changed scrap for an exact match.
5994            final int changedScrapSize;
5995            if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
5996                return null;
5997            }
5998            // find by position
5999            for (int i = 0; i < changedScrapSize; i++) {
6000                final ViewHolder holder = mChangedScrap.get(i);
6001                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
6002                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6003                    return holder;
6004                }
6005            }
6006            // find by id
6007            if (mAdapter.hasStableIds()) {
6008                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
6009                if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
6010                    final long id = mAdapter.getItemId(offsetPosition);
6011                    for (int i = 0; i < changedScrapSize; i++) {
6012                        final ViewHolder holder = mChangedScrap.get(i);
6013                        if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
6014                            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6015                            return holder;
6016                        }
6017                    }
6018                }
6019            }
6020            return null;
6021        }
6022
6023        /**
6024         * Returns a view for the position either from attach scrap, hidden children, or cache.
6025         *
6026         * @param position Item position
6027         * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
6028         * @return a ViewHolder that can be re-used for this position.
6029         */
6030        ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
6031            final int scrapCount = mAttachedScrap.size();
6032
6033            // Try first for an exact, non-invalid match from scrap.
6034            for (int i = 0; i < scrapCount; i++) {
6035                final ViewHolder holder = mAttachedScrap.get(i);
6036                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
6037                        && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
6038                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6039                    return holder;
6040                }
6041            }
6042
6043            if (!dryRun) {
6044                View view = mChildHelper.findHiddenNonRemovedView(position);
6045                if (view != null) {
6046                    // This View is good to be used. We just need to unhide, detach and move to the
6047                    // scrap list.
6048                    final ViewHolder vh = getChildViewHolderInt(view);
6049                    mChildHelper.unhide(view);
6050                    int layoutIndex = mChildHelper.indexOfChild(view);
6051                    if (layoutIndex == RecyclerView.NO_POSITION) {
6052                        throw new IllegalStateException("layout index should not be -1 after "
6053                                + "unhiding a view:" + vh);
6054                    }
6055                    mChildHelper.detachViewFromParent(layoutIndex);
6056                    scrapView(view);
6057                    vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
6058                            | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
6059                    return vh;
6060                }
6061            }
6062
6063            // Search in our first-level recycled view cache.
6064            final int cacheSize = mCachedViews.size();
6065            for (int i = 0; i < cacheSize; i++) {
6066                final ViewHolder holder = mCachedViews.get(i);
6067                // invalid view holders may be in cache if adapter has stable ids as they can be
6068                // retrieved via getScrapOrCachedViewForId
6069                if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
6070                    if (!dryRun) {
6071                        mCachedViews.remove(i);
6072                    }
6073                    if (DEBUG) {
6074                        Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
6075                                + ") found match in cache: " + holder);
6076                    }
6077                    return holder;
6078                }
6079            }
6080            return null;
6081        }
6082
6083        ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
6084            // Look in our attached views first
6085            final int count = mAttachedScrap.size();
6086            for (int i = count - 1; i >= 0; i--) {
6087                final ViewHolder holder = mAttachedScrap.get(i);
6088                if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
6089                    if (type == holder.getItemViewType()) {
6090                        holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
6091                        if (holder.isRemoved()) {
6092                            // this might be valid in two cases:
6093                            // > item is removed but we are in pre-layout pass
6094                            // >> do nothing. return as is. make sure we don't rebind
6095                            // > item is removed then added to another position and we are in
6096                            // post layout.
6097                            // >> remove removed and invalid flags, add update flag to rebind
6098                            // because item was invisible to us and we don't know what happened in
6099                            // between.
6100                            if (!mState.isPreLayout()) {
6101                                holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
6102                                        | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
6103                            }
6104                        }
6105                        return holder;
6106                    } else if (!dryRun) {
6107                        // if we are running animations, it is actually better to keep it in scrap
6108                        // but this would force layout manager to lay it out which would be bad.
6109                        // Recycle this scrap. Type mismatch.
6110                        mAttachedScrap.remove(i);
6111                        removeDetachedView(holder.itemView, false);
6112                        quickRecycleScrapView(holder.itemView);
6113                    }
6114                }
6115            }
6116
6117            // Search the first-level cache
6118            final int cacheSize = mCachedViews.size();
6119            for (int i = cacheSize - 1; i >= 0; i--) {
6120                final ViewHolder holder = mCachedViews.get(i);
6121                if (holder.getItemId() == id) {
6122                    if (type == holder.getItemViewType()) {
6123                        if (!dryRun) {
6124                            mCachedViews.remove(i);
6125                        }
6126                        return holder;
6127                    } else if (!dryRun) {
6128                        recycleCachedViewAt(i);
6129                        return null;
6130                    }
6131                }
6132            }
6133            return null;
6134        }
6135
6136        void dispatchViewRecycled(ViewHolder holder) {
6137            if (mRecyclerListener != null) {
6138                mRecyclerListener.onViewRecycled(holder);
6139            }
6140            if (mAdapter != null) {
6141                mAdapter.onViewRecycled(holder);
6142            }
6143            if (mState != null) {
6144                mViewInfoStore.removeViewHolder(holder);
6145            }
6146            if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
6147        }
6148
6149        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
6150                boolean compatibleWithPrevious) {
6151            clear();
6152            getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
6153        }
6154
6155        void offsetPositionRecordsForMove(int from, int to) {
6156            final int start, end, inBetweenOffset;
6157            if (from < to) {
6158                start = from;
6159                end = to;
6160                inBetweenOffset = -1;
6161            } else {
6162                start = to;
6163                end = from;
6164                inBetweenOffset = 1;
6165            }
6166            final int cachedCount = mCachedViews.size();
6167            for (int i = 0; i < cachedCount; i++) {
6168                final ViewHolder holder = mCachedViews.get(i);
6169                if (holder == null || holder.mPosition < start || holder.mPosition > end) {
6170                    continue;
6171                }
6172                if (holder.mPosition == from) {
6173                    holder.offsetPosition(to - from, false);
6174                } else {
6175                    holder.offsetPosition(inBetweenOffset, false);
6176                }
6177                if (DEBUG) {
6178                    Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
6179                            + holder);
6180                }
6181            }
6182        }
6183
6184        void offsetPositionRecordsForInsert(int insertedAt, int count) {
6185            final int cachedCount = mCachedViews.size();
6186            for (int i = 0; i < cachedCount; i++) {
6187                final ViewHolder holder = mCachedViews.get(i);
6188                if (holder != null && holder.mPosition >= insertedAt) {
6189                    if (DEBUG) {
6190                        Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder "
6191                                + holder + " now at position " + (holder.mPosition + count));
6192                    }
6193                    holder.offsetPosition(count, true);
6194                }
6195            }
6196        }
6197
6198        /**
6199         * @param removedFrom Remove start index
6200         * @param count Remove count
6201         * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
6202         *                         false, they'll be applied before the second layout pass
6203         */
6204        void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
6205            final int removedEnd = removedFrom + count;
6206            final int cachedCount = mCachedViews.size();
6207            for (int i = cachedCount - 1; i >= 0; i--) {
6208                final ViewHolder holder = mCachedViews.get(i);
6209                if (holder != null) {
6210                    if (holder.mPosition >= removedEnd) {
6211                        if (DEBUG) {
6212                            Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
6213                                    + " holder " + holder + " now at position "
6214                                    + (holder.mPosition - count));
6215                        }
6216                        holder.offsetPosition(-count, applyToPreLayout);
6217                    } else if (holder.mPosition >= removedFrom) {
6218                        // Item for this view was removed. Dump it from the cache.
6219                        holder.addFlags(ViewHolder.FLAG_REMOVED);
6220                        recycleCachedViewAt(i);
6221                    }
6222                }
6223            }
6224        }
6225
6226        void setViewCacheExtension(ViewCacheExtension extension) {
6227            mViewCacheExtension = extension;
6228        }
6229
6230        void setRecycledViewPool(RecycledViewPool pool) {
6231            if (mRecyclerPool != null) {
6232                mRecyclerPool.detach();
6233            }
6234            mRecyclerPool = pool;
6235            if (pool != null) {
6236                mRecyclerPool.attach(getAdapter());
6237            }
6238        }
6239
6240        RecycledViewPool getRecycledViewPool() {
6241            if (mRecyclerPool == null) {
6242                mRecyclerPool = new RecycledViewPool();
6243            }
6244            return mRecyclerPool;
6245        }
6246
6247        void viewRangeUpdate(int positionStart, int itemCount) {
6248            final int positionEnd = positionStart + itemCount;
6249            final int cachedCount = mCachedViews.size();
6250            for (int i = cachedCount - 1; i >= 0; i--) {
6251                final ViewHolder holder = mCachedViews.get(i);
6252                if (holder == null) {
6253                    continue;
6254                }
6255
6256                final int pos = holder.mPosition;
6257                if (pos >= positionStart && pos < positionEnd) {
6258                    holder.addFlags(ViewHolder.FLAG_UPDATE);
6259                    recycleCachedViewAt(i);
6260                    // cached views should not be flagged as changed because this will cause them
6261                    // to animate when they are returned from cache.
6262                }
6263            }
6264        }
6265
6266        void markKnownViewsInvalid() {
6267            if (mAdapter != null && mAdapter.hasStableIds()) {
6268                final int cachedCount = mCachedViews.size();
6269                for (int i = 0; i < cachedCount; i++) {
6270                    final ViewHolder holder = mCachedViews.get(i);
6271                    if (holder != null) {
6272                        holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
6273                        holder.addChangePayload(null);
6274                    }
6275                }
6276            } else {
6277                // we cannot re-use cached views in this case. Recycle them all
6278                recycleAndClearCachedViews();
6279            }
6280        }
6281
6282        void clearOldPositions() {
6283            final int cachedCount = mCachedViews.size();
6284            for (int i = 0; i < cachedCount; i++) {
6285                final ViewHolder holder = mCachedViews.get(i);
6286                holder.clearOldPosition();
6287            }
6288            final int scrapCount = mAttachedScrap.size();
6289            for (int i = 0; i < scrapCount; i++) {
6290                mAttachedScrap.get(i).clearOldPosition();
6291            }
6292            if (mChangedScrap != null) {
6293                final int changedScrapCount = mChangedScrap.size();
6294                for (int i = 0; i < changedScrapCount; i++) {
6295                    mChangedScrap.get(i).clearOldPosition();
6296                }
6297            }
6298        }
6299
6300        void markItemDecorInsetsDirty() {
6301            final int cachedCount = mCachedViews.size();
6302            for (int i = 0; i < cachedCount; i++) {
6303                final ViewHolder holder = mCachedViews.get(i);
6304                LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
6305                if (layoutParams != null) {
6306                    layoutParams.mInsetsDirty = true;
6307                }
6308            }
6309        }
6310    }
6311
6312    /**
6313     * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
6314     * be controlled by the developer.
6315     * <p>
6316     * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
6317     * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
6318     * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
6319     * {@link RecycledViewPool}.
6320     * <p>
6321     * Note that, Recycler never sends Views to this method to be cached. It is developers
6322     * responsibility to decide whether they want to keep their Views in this custom cache or let
6323     * the default recycling policy handle it.
6324     */
6325    public abstract static class ViewCacheExtension {
6326
6327        /**
6328         * Returns a View that can be binded to the given Adapter position.
6329         * <p>
6330         * This method should <b>not</b> create a new View. Instead, it is expected to return
6331         * an already created View that can be re-used for the given type and position.
6332         * If the View is marked as ignored, it should first call
6333         * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
6334         * <p>
6335         * RecyclerView will re-bind the returned View to the position if necessary.
6336         *
6337         * @param recycler The Recycler that can be used to bind the View
6338         * @param position The adapter position
6339         * @param type     The type of the View, defined by adapter
6340         * @return A View that is bound to the given position or NULL if there is no View to re-use
6341         * @see LayoutManager#ignoreView(View)
6342         */
6343        public abstract View getViewForPositionAndType(Recycler recycler, int position, int type);
6344    }
6345
6346    /**
6347     * Base class for an Adapter
6348     *
6349     * <p>Adapters provide a binding from an app-specific data set to views that are displayed
6350     * within a {@link RecyclerView}.</p>
6351     *
6352     * @param  A class that extends ViewHolder that will be used by the adapter.
6353     */
6354    public abstract static class Adapter<VH extends ViewHolder> {
6355        private final AdapterDataObservable mObservable = new AdapterDataObservable();
6356        private boolean mHasStableIds = false;
6357
6358        /**
6359         * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
6360         * an item.
6361         * <p>
6362         * This new ViewHolder should be constructed with a new View that can represent the items
6363         * of the given type. You can either create a new View manually or inflate it from an XML
6364         * layout file.
6365         * <p>
6366         * The new ViewHolder will be used to display items of the adapter using
6367         * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
6368         * different items in the data set, it is a good idea to cache references to sub views of
6369         * the View to avoid unnecessary {@link View#findViewById(int)} calls.
6370         *
6371         * @param parent The ViewGroup into which the new View will be added after it is bound to
6372         *               an adapter position.
6373         * @param viewType The view type of the new View.
6374         *
6375         * @return A new ViewHolder that holds a View of the given view type.
6376         * @see #getItemViewType(int)
6377         * @see #onBindViewHolder(ViewHolder, int)
6378         */
6379        public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
6380
6381        /**
6382         * Called by RecyclerView to display the data at the specified position. This method should
6383         * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
6384         * position.
6385         * <p>
6386         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6387         * again if the position of the item changes in the data set unless the item itself is
6388         * invalidated or the new position cannot be determined. For this reason, you should only
6389         * use the <code>position</code> parameter while acquiring the related data item inside
6390         * this method and should not keep a copy of it. If you need the position of an item later
6391         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6392         * have the updated adapter position.
6393         *
6394         * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
6395         * handle efficient partial bind.
6396         *
6397         * @param holder The ViewHolder which should be updated to represent the contents of the
6398         *        item at the given position in the data set.
6399         * @param position The position of the item within the adapter's data set.
6400         */
6401        public abstract void onBindViewHolder(VH holder, int position);
6402
6403        /**
6404         * Called by RecyclerView to display the data at the specified position. This method
6405         * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
6406         * the given position.
6407         * <p>
6408         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6409         * again if the position of the item changes in the data set unless the item itself is
6410         * invalidated or the new position cannot be determined. For this reason, you should only
6411         * use the <code>position</code> parameter while acquiring the related data item inside
6412         * this method and should not keep a copy of it. If you need the position of an item later
6413         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6414         * have the updated adapter position.
6415         * <p>
6416         * Partial bind vs full bind:
6417         * <p>
6418         * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
6419         * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
6420         * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
6421         * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
6422         * Adapter should not assume that the payload passed in notify methods will be received by
6423         * onBindViewHolder().  For example when the view is not attached to the screen, the
6424         * payload in notifyItemChange() will be simply dropped.
6425         *
6426         * @param holder The ViewHolder which should be updated to represent the contents of the
6427         *               item at the given position in the data set.
6428         * @param position The position of the item within the adapter's data set.
6429         * @param payloads A non-null list of merged payloads. Can be empty list if requires full
6430         *                 update.
6431         */
6432        public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
6433            onBindViewHolder(holder, position);
6434        }
6435
6436        /**
6437         * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
6438         * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
6439         *
6440         * @see #onCreateViewHolder(ViewGroup, int)
6441         */
6442        public final VH createViewHolder(ViewGroup parent, int viewType) {
6443            TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
6444            final VH holder = onCreateViewHolder(parent, viewType);
6445            holder.mItemViewType = viewType;
6446            TraceCompat.endSection();
6447            return holder;
6448        }
6449
6450        /**
6451         * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
6452         * {@link ViewHolder} contents with the item at the given position and also sets up some
6453         * private fields to be used by RecyclerView.
6454         *
6455         * @see #onBindViewHolder(ViewHolder, int)
6456         */
6457        public final void bindViewHolder(VH holder, int position) {
6458            holder.mPosition = position;
6459            if (hasStableIds()) {
6460                holder.mItemId = getItemId(position);
6461            }
6462            holder.setFlags(ViewHolder.FLAG_BOUND,
6463                    ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
6464                            | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
6465            TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
6466            onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
6467            holder.clearPayload();
6468            final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
6469            if (layoutParams instanceof RecyclerView.LayoutParams) {
6470                ((LayoutParams) layoutParams).mInsetsDirty = true;
6471            }
6472            TraceCompat.endSection();
6473        }
6474
6475        /**
6476         * Return the view type of the item at <code>position</code> for the purposes
6477         * of view recycling.
6478         *
6479         * <p>The default implementation of this method returns 0, making the assumption of
6480         * a single view type for the adapter. Unlike ListView adapters, types need not
6481         * be contiguous. Consider using id resources to uniquely identify item view types.
6482         *
6483         * @param position position to query
6484         * @return integer value identifying the type of the view needed to represent the item at
6485         *                 <code>position</code>. Type codes need not be contiguous.
6486         */
6487        public int getItemViewType(int position) {
6488            return 0;
6489        }
6490
6491        /**
6492         * Indicates whether each item in the data set can be represented with a unique identifier
6493         * of type {@link java.lang.Long}.
6494         *
6495         * @param hasStableIds Whether items in data set have unique identifiers or not.
6496         * @see #hasStableIds()
6497         * @see #getItemId(int)
6498         */
6499        public void setHasStableIds(boolean hasStableIds) {
6500            if (hasObservers()) {
6501                throw new IllegalStateException("Cannot change whether this adapter has "
6502                        + "stable IDs while the adapter has registered observers.");
6503            }
6504            mHasStableIds = hasStableIds;
6505        }
6506
6507        /**
6508         * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
6509         * would return false this method should return {@link #NO_ID}. The default implementation
6510         * of this method returns {@link #NO_ID}.
6511         *
6512         * @param position Adapter position to query
6513         * @return the stable ID of the item at position
6514         */
6515        public long getItemId(int position) {
6516            return NO_ID;
6517        }
6518
6519        /**
6520         * Returns the total number of items in the data set held by the adapter.
6521         *
6522         * @return The total number of items in this adapter.
6523         */
6524        public abstract int getItemCount();
6525
6526        /**
6527         * Returns true if this adapter publishes a unique <code>long</code> value that can
6528         * act as a key for the item at a given position in the data set. If that item is relocated
6529         * in the data set, the ID returned for that item should be the same.
6530         *
6531         * @return true if this adapter's items have stable IDs
6532         */
6533        public final boolean hasStableIds() {
6534            return mHasStableIds;
6535        }
6536
6537        /**
6538         * Called when a view created by this adapter has been recycled.
6539         *
6540         * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
6541         * needs to be attached to its parent {@link RecyclerView}. This can be because it has
6542         * fallen out of visibility or a set of cached views represented by views still
6543         * attached to the parent RecyclerView. If an item view has large or expensive data
6544         * bound to it such as large bitmaps, this may be a good place to release those
6545         * resources.</p>
6546         * <p>
6547         * RecyclerView calls this method right before clearing ViewHolder's internal data and
6548         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
6549         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
6550         * its adapter position.
6551         *
6552         * @param holder The ViewHolder for the view being recycled
6553         */
6554        public void onViewRecycled(VH holder) {
6555        }
6556
6557        /**
6558         * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
6559         * due to its transient state. Upon receiving this callback, Adapter can clear the
6560         * animation(s) that effect the View's transient state and return <code>true</code> so that
6561         * the View can be recycled. Keep in mind that the View in question is already removed from
6562         * the RecyclerView.
6563         * <p>
6564         * In some cases, it is acceptable to recycle a View although it has transient state. Most
6565         * of the time, this is a case where the transient state will be cleared in
6566         * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
6567         * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
6568         * value of this method to decide whether the View should be recycled or not.
6569         * <p>
6570         * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
6571         * should never receive this callback because RecyclerView keeps those Views as children
6572         * until their animations are complete. This callback is useful when children of the item
6573         * views create animations which may not be easy to implement using an {@link ItemAnimator}.
6574         * <p>
6575         * You should <em>never</em> fix this issue by calling
6576         * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
6577         * <code>holder.itemView.setHasTransientState(true);</code>. Each
6578         * <code>View.setHasTransientState(true)</code> call must be matched by a
6579         * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
6580         * may become inconsistent. You should always prefer to end or cancel animations that are
6581         * triggering the transient state instead of handling it manually.
6582         *
6583         * @param holder The ViewHolder containing the View that could not be recycled due to its
6584         *               transient state.
6585         * @return True if the View should be recycled, false otherwise. Note that if this method
6586         * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
6587         * the View and recycle it regardless. If this method returns <code>false</code>,
6588         * RecyclerView will check the View's transient state again before giving a final decision.
6589         * Default implementation returns false.
6590         */
6591        public boolean onFailedToRecycleView(VH holder) {
6592            return false;
6593        }
6594
6595        /**
6596         * Called when a view created by this adapter has been attached to a window.
6597         *
6598         * <p>This can be used as a reasonable signal that the view is about to be seen
6599         * by the user. If the adapter previously freed any resources in
6600         * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
6601         * those resources should be restored here.</p>
6602         *
6603         * @param holder Holder of the view being attached
6604         */
6605        public void onViewAttachedToWindow(VH holder) {
6606        }
6607
6608        /**
6609         * Called when a view created by this adapter has been detached from its window.
6610         *
6611         * <p>Becoming detached from the window is not necessarily a permanent condition;
6612         * the consumer of an Adapter's views may choose to cache views offscreen while they
6613         * are not visible, attaching and detaching them as appropriate.</p>
6614         *
6615         * @param holder Holder of the view being detached
6616         */
6617        public void onViewDetachedFromWindow(VH holder) {
6618        }
6619
6620        /**
6621         * Returns true if one or more observers are attached to this adapter.
6622         *
6623         * @return true if this adapter has observers
6624         */
6625        public final boolean hasObservers() {
6626            return mObservable.hasObservers();
6627        }
6628
6629        /**
6630         * Register a new observer to listen for data changes.
6631         *
6632         * <p>The adapter may publish a variety of events describing specific changes.
6633         * Not all adapters may support all change types and some may fall back to a generic
6634         * {@link android.support.v7.widget.RecyclerView.AdapterDataObserver#onChanged()
6635         * "something changed"} event if more specific data is not available.</p>
6636         *
6637         * <p>Components registering observers with an adapter are responsible for
6638         * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6639         * unregistering} those observers when finished.</p>
6640         *
6641         * @param observer Observer to register
6642         *
6643         * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6644         */
6645        public void registerAdapterDataObserver(AdapterDataObserver observer) {
6646            mObservable.registerObserver(observer);
6647        }
6648
6649        /**
6650         * Unregister an observer currently listening for data changes.
6651         *
6652         * <p>The unregistered observer will no longer receive events about changes
6653         * to the adapter.</p>
6654         *
6655         * @param observer Observer to unregister
6656         *
6657         * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
6658         */
6659        public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
6660            mObservable.unregisterObserver(observer);
6661        }
6662
6663        /**
6664         * Called by RecyclerView when it starts observing this Adapter.
6665         * <p>
6666         * Keep in mind that same adapter may be observed by multiple RecyclerViews.
6667         *
6668         * @param recyclerView The RecyclerView instance which started observing this adapter.
6669         * @see #onDetachedFromRecyclerView(RecyclerView)
6670         */
6671        public void onAttachedToRecyclerView(RecyclerView recyclerView) {
6672        }
6673
6674        /**
6675         * Called by RecyclerView when it stops observing this Adapter.
6676         *
6677         * @param recyclerView The RecyclerView instance which stopped observing this adapter.
6678         * @see #onAttachedToRecyclerView(RecyclerView)
6679         */
6680        public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
6681        }
6682
6683        /**
6684         * Notify any registered observers that the data set has changed.
6685         *
6686         * <p>There are two different classes of data change events, item changes and structural
6687         * changes. Item changes are when a single item has its data updated but no positional
6688         * changes have occurred. Structural changes are when items are inserted, removed or moved
6689         * within the data set.</p>
6690         *
6691         * <p>This event does not specify what about the data set has changed, forcing
6692         * any observers to assume that all existing items and structure may no longer be valid.
6693         * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
6694         *
6695         * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
6696         * for adapters that report that they have {@link #hasStableIds() stable IDs} when
6697         * this method is used. This can help for the purposes of animation and visual
6698         * object persistence but individual item views will still need to be rebound
6699         * and relaid out.</p>
6700         *
6701         * <p>If you are writing an adapter it will always be more efficient to use the more
6702         * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
6703         * as a last resort.</p>
6704         *
6705         * @see #notifyItemChanged(int)
6706         * @see #notifyItemInserted(int)
6707         * @see #notifyItemRemoved(int)
6708         * @see #notifyItemRangeChanged(int, int)
6709         * @see #notifyItemRangeInserted(int, int)
6710         * @see #notifyItemRangeRemoved(int, int)
6711         */
6712        public final void notifyDataSetChanged() {
6713            mObservable.notifyChanged();
6714        }
6715
6716        /**
6717         * Notify any registered observers that the item at <code>position</code> has changed.
6718         * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
6719         *
6720         * <p>This is an item change event, not a structural change event. It indicates that any
6721         * reflection of the data at <code>position</code> is out of date and should be updated.
6722         * The item at <code>position</code> retains the same identity.</p>
6723         *
6724         * @param position Position of the item that has changed
6725         *
6726         * @see #notifyItemRangeChanged(int, int)
6727         */
6728        public final void notifyItemChanged(int position) {
6729            mObservable.notifyItemRangeChanged(position, 1);
6730        }
6731
6732        /**
6733         * Notify any registered observers that the item at <code>position</code> has changed with
6734         * an optional payload object.
6735         *
6736         * <p>This is an item change event, not a structural change event. It indicates that any
6737         * reflection of the data at <code>position</code> is out of date and should be updated.
6738         * The item at <code>position</code> retains the same identity.
6739         * </p>
6740         *
6741         * <p>
6742         * Client can optionally pass a payload for partial change. These payloads will be merged
6743         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6744         * item is already represented by a ViewHolder and it will be rebound to the same
6745         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6746         * payloads on that item and prevent future payload until
6747         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6748         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6749         * attached, the payload will be simply dropped.
6750         *
6751         * @param position Position of the item that has changed
6752         * @param payload Optional parameter, use null to identify a "full" update
6753         *
6754         * @see #notifyItemRangeChanged(int, int)
6755         */
6756        public final void notifyItemChanged(int position, Object payload) {
6757            mObservable.notifyItemRangeChanged(position, 1, payload);
6758        }
6759
6760        /**
6761         * Notify any registered observers that the <code>itemCount</code> items starting at
6762         * position <code>positionStart</code> have changed.
6763         * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
6764         *
6765         * <p>This is an item change event, not a structural change event. It indicates that
6766         * any reflection of the data in the given position range is out of date and should
6767         * be updated. The items in the given range retain the same identity.</p>
6768         *
6769         * @param positionStart Position of the first item that has changed
6770         * @param itemCount Number of items that have changed
6771         *
6772         * @see #notifyItemChanged(int)
6773         */
6774        public final void notifyItemRangeChanged(int positionStart, int itemCount) {
6775            mObservable.notifyItemRangeChanged(positionStart, itemCount);
6776        }
6777
6778        /**
6779         * Notify any registered observers that the <code>itemCount</code> items starting at
6780         * position <code>positionStart</code> have changed. An optional payload can be
6781         * passed to each changed item.
6782         *
6783         * <p>This is an item change event, not a structural change event. It indicates that any
6784         * reflection of the data in the given position range is out of date and should be updated.
6785         * The items in the given range retain the same identity.
6786         * </p>
6787         *
6788         * <p>
6789         * Client can optionally pass a payload for partial change. These payloads will be merged
6790         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6791         * item is already represented by a ViewHolder and it will be rebound to the same
6792         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6793         * payloads on that item and prevent future payload until
6794         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6795         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6796         * attached, the payload will be simply dropped.
6797         *
6798         * @param positionStart Position of the first item that has changed
6799         * @param itemCount Number of items that have changed
6800         * @param payload  Optional parameter, use null to identify a "full" update
6801         *
6802         * @see #notifyItemChanged(int)
6803         */
6804        public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
6805            mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
6806        }
6807
6808        /**
6809         * Notify any registered observers that the item reflected at <code>position</code>
6810         * has been newly inserted. The item previously at <code>position</code> is now at
6811         * position <code>position + 1</code>.
6812         *
6813         * <p>This is a structural change event. Representations of other existing items in the
6814         * data set are still considered up to date and will not be rebound, though their
6815         * positions may be altered.</p>
6816         *
6817         * @param position Position of the newly inserted item in the data set
6818         *
6819         * @see #notifyItemRangeInserted(int, int)
6820         */
6821        public final void notifyItemInserted(int position) {
6822            mObservable.notifyItemRangeInserted(position, 1);
6823        }
6824
6825        /**
6826         * Notify any registered observers that the item reflected at <code>fromPosition</code>
6827         * has been moved to <code>toPosition</code>.
6828         *
6829         * <p>This is a structural change event. Representations of other existing items in the
6830         * data set are still considered up to date and will not be rebound, though their
6831         * positions may be altered.</p>
6832         *
6833         * @param fromPosition Previous position of the item.
6834         * @param toPosition New position of the item.
6835         */
6836        public final void notifyItemMoved(int fromPosition, int toPosition) {
6837            mObservable.notifyItemMoved(fromPosition, toPosition);
6838        }
6839
6840        /**
6841         * Notify any registered observers that the currently reflected <code>itemCount</code>
6842         * items starting at <code>positionStart</code> have been newly inserted. The items
6843         * previously located at <code>positionStart</code> and beyond can now be found starting
6844         * at position <code>positionStart + itemCount</code>.
6845         *
6846         * <p>This is a structural change event. Representations of other existing items in the
6847         * data set are still considered up to date and will not be rebound, though their positions
6848         * may be altered.</p>
6849         *
6850         * @param positionStart Position of the first item that was inserted
6851         * @param itemCount Number of items inserted
6852         *
6853         * @see #notifyItemInserted(int)
6854         */
6855        public final void notifyItemRangeInserted(int positionStart, int itemCount) {
6856            mObservable.notifyItemRangeInserted(positionStart, itemCount);
6857        }
6858
6859        /**
6860         * Notify any registered observers that the item previously located at <code>position</code>
6861         * has been removed from the data set. The items previously located at and after
6862         * <code>position</code> may now be found at <code>oldPosition - 1</code>.
6863         *
6864         * <p>This is a structural change event. Representations of other existing items in the
6865         * data set are still considered up to date and will not be rebound, though their positions
6866         * may be altered.</p>
6867         *
6868         * @param position Position of the item that has now been removed
6869         *
6870         * @see #notifyItemRangeRemoved(int, int)
6871         */
6872        public final void notifyItemRemoved(int position) {
6873            mObservable.notifyItemRangeRemoved(position, 1);
6874        }
6875
6876        /**
6877         * Notify any registered observers that the <code>itemCount</code> items previously
6878         * located at <code>positionStart</code> have been removed from the data set. The items
6879         * previously located at and after <code>positionStart + itemCount</code> may now be found
6880         * at <code>oldPosition - itemCount</code>.
6881         *
6882         * <p>This is a structural change event. Representations of other existing items in the data
6883         * set are still considered up to date and will not be rebound, though their positions
6884         * may be altered.</p>
6885         *
6886         * @param positionStart Previous position of the first item that was removed
6887         * @param itemCount Number of items removed from the data set
6888         */
6889        public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
6890            mObservable.notifyItemRangeRemoved(positionStart, itemCount);
6891        }
6892    }
6893
6894    void dispatchChildDetached(View child) {
6895        final ViewHolder viewHolder = getChildViewHolderInt(child);
6896        onChildDetachedFromWindow(child);
6897        if (mAdapter != null && viewHolder != null) {
6898            mAdapter.onViewDetachedFromWindow(viewHolder);
6899        }
6900        if (mOnChildAttachStateListeners != null) {
6901            final int cnt = mOnChildAttachStateListeners.size();
6902            for (int i = cnt - 1; i >= 0; i--) {
6903                mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
6904            }
6905        }
6906    }
6907
6908    void dispatchChildAttached(View child) {
6909        final ViewHolder viewHolder = getChildViewHolderInt(child);
6910        onChildAttachedToWindow(child);
6911        if (mAdapter != null && viewHolder != null) {
6912            mAdapter.onViewAttachedToWindow(viewHolder);
6913        }
6914        if (mOnChildAttachStateListeners != null) {
6915            final int cnt = mOnChildAttachStateListeners.size();
6916            for (int i = cnt - 1; i >= 0; i--) {
6917                mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
6918            }
6919        }
6920    }
6921
6922    /**
6923     * A <code>LayoutManager</code> is responsible for measuring and positioning item views
6924     * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
6925     * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
6926     * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
6927     * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
6928     * layout managers are provided for general use.
6929     * <p/>
6930     * If the LayoutManager specifies a default constructor or one with the signature
6931     * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
6932     * instantiate and set the LayoutManager when being inflated. Most used properties can
6933     * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
6934     * a LayoutManager specifies both constructors, the non-default constructor will take
6935     * precedence.
6936     *
6937     */
6938    public abstract static class LayoutManager {
6939        ChildHelper mChildHelper;
6940        RecyclerView mRecyclerView;
6941
6942        /**
6943         * The callback used for retrieving information about a RecyclerView and its children in the
6944         * horizontal direction.
6945         */
6946        private final ViewBoundsCheck.Callback mHorizontalBoundCheckCallback =
6947                new ViewBoundsCheck.Callback() {
6948                    @Override
6949                    public int getChildCount() {
6950                        return LayoutManager.this.getChildCount();
6951                    }
6952
6953                    @Override
6954                    public View getParent() {
6955                        return mRecyclerView;
6956                    }
6957
6958                    @Override
6959                    public View getChildAt(int index) {
6960                        return LayoutManager.this.getChildAt(index);
6961                    }
6962
6963                    @Override
6964                    public int getParentStart() {
6965                        return LayoutManager.this.getPaddingLeft();
6966                    }
6967
6968                    @Override
6969                    public int getParentEnd() {
6970                        return LayoutManager.this.getWidth() - LayoutManager.this.getPaddingRight();
6971                    }
6972
6973                    @Override
6974                    public int getChildStart(View view) {
6975                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
6976                                view.getLayoutParams();
6977                        return LayoutManager.this.getDecoratedLeft(view) - params.leftMargin;
6978                    }
6979
6980                    @Override
6981                    public int getChildEnd(View view) {
6982                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
6983                                view.getLayoutParams();
6984                        return LayoutManager.this.getDecoratedRight(view) + params.rightMargin;
6985                    }
6986                };
6987
6988        /**
6989         * The callback used for retrieving information about a RecyclerView and its children in the
6990         * vertical direction.
6991         */
6992        private final ViewBoundsCheck.Callback mVerticalBoundCheckCallback =
6993                new ViewBoundsCheck.Callback() {
6994                    @Override
6995                    public int getChildCount() {
6996                        return LayoutManager.this.getChildCount();
6997                    }
6998
6999                    @Override
7000                    public View getParent() {
7001                        return mRecyclerView;
7002                    }
7003
7004                    @Override
7005                    public View getChildAt(int index) {
7006                        return LayoutManager.this.getChildAt(index);
7007                    }
7008
7009                    @Override
7010                    public int getParentStart() {
7011                        return LayoutManager.this.getPaddingTop();
7012                    }
7013
7014                    @Override
7015                    public int getParentEnd() {
7016                        return LayoutManager.this.getHeight()
7017                                - LayoutManager.this.getPaddingBottom();
7018                    }
7019
7020                    @Override
7021                    public int getChildStart(View view) {
7022                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7023                                view.getLayoutParams();
7024                        return LayoutManager.this.getDecoratedTop(view) - params.topMargin;
7025                    }
7026
7027                    @Override
7028                    public int getChildEnd(View view) {
7029                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
7030                                view.getLayoutParams();
7031                        return LayoutManager.this.getDecoratedBottom(view) + params.bottomMargin;
7032                    }
7033                };
7034
7035        /**
7036         * Utility objects used to check the boundaries of children against their parent
7037         * RecyclerView.
7038         * @see #isViewPartiallyVisible(View, boolean, boolean),
7039         * {@link LinearLayoutManager#findOneVisibleChild(int, int, boolean, boolean)},
7040         * and {@link LinearLayoutManager#findOnePartiallyOrCompletelyInvisibleChild(int, int)}.
7041         */
7042        ViewBoundsCheck mHorizontalBoundCheck = new ViewBoundsCheck(mHorizontalBoundCheckCallback);
7043        ViewBoundsCheck mVerticalBoundCheck = new ViewBoundsCheck(mVerticalBoundCheckCallback);
7044
7045        @Nullable
7046        SmoothScroller mSmoothScroller;
7047
7048        boolean mRequestedSimpleAnimations = false;
7049
7050        boolean mIsAttachedToWindow = false;
7051
7052        boolean mAutoMeasure = false;
7053
7054        /**
7055         * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
7056         * if the space that will be given to it is already larger than what it has measured before.
7057         */
7058        private boolean mMeasurementCacheEnabled = true;
7059
7060        private boolean mItemPrefetchEnabled = true;
7061
7062        /**
7063         * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
7064         * requested by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)} or
7065         * {@link #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)} call.
7066         *
7067         * If expanded by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)},
7068         * will be reset upon layout to prevent initial prefetches (often large, since they're
7069         * proportional to expected child count) from expanding cache permanently.
7070         */
7071        int mPrefetchMaxCountObserved;
7072
7073        /**
7074         * If true, mPrefetchMaxCountObserved is only valid until next layout, and should be reset.
7075         */
7076        boolean mPrefetchMaxObservedInInitialPrefetch;
7077
7078        /**
7079         * These measure specs might be the measure specs that were passed into RecyclerView's
7080         * onMeasure method OR fake measure specs created by the RecyclerView.
7081         * For example, when a layout is run, RecyclerView always sets these specs to be
7082         * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
7083         * <p>
7084         * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
7085         * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
7086         * corrupt values. Older platforms have no responsibility to provide a size if they set
7087         * mode to unspecified.
7088         */
7089        private int mWidthMode, mHeightMode;
7090        private int mWidth, mHeight;
7091
7092
7093        /**
7094         * Interface for LayoutManagers to request items to be prefetched, based on position, with
7095         * specified distance from viewport, which indicates priority.
7096         *
7097         * @see LayoutManager#collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
7098         * @see LayoutManager#collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
7099         */
7100        public interface LayoutPrefetchRegistry {
7101            /**
7102             * Requests an an item to be prefetched, based on position, with a specified distance,
7103             * indicating priority.
7104             *
7105             * @param layoutPosition Position of the item to prefetch.
7106             * @param pixelDistance Distance from the current viewport to the bounds of the item,
7107             *                      must be non-negative.
7108             */
7109            void addPosition(int layoutPosition, int pixelDistance);
7110        }
7111
7112        void setRecyclerView(RecyclerView recyclerView) {
7113            if (recyclerView == null) {
7114                mRecyclerView = null;
7115                mChildHelper = null;
7116                mWidth = 0;
7117                mHeight = 0;
7118            } else {
7119                mRecyclerView = recyclerView;
7120                mChildHelper = recyclerView.mChildHelper;
7121                mWidth = recyclerView.getWidth();
7122                mHeight = recyclerView.getHeight();
7123            }
7124            mWidthMode = MeasureSpec.EXACTLY;
7125            mHeightMode = MeasureSpec.EXACTLY;
7126        }
7127
7128        void setMeasureSpecs(int wSpec, int hSpec) {
7129            mWidth = MeasureSpec.getSize(wSpec);
7130            mWidthMode = MeasureSpec.getMode(wSpec);
7131            if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
7132                mWidth = 0;
7133            }
7134
7135            mHeight = MeasureSpec.getSize(hSpec);
7136            mHeightMode = MeasureSpec.getMode(hSpec);
7137            if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
7138                mHeight = 0;
7139            }
7140        }
7141
7142        /**
7143         * Called after a layout is calculated during a measure pass when using auto-measure.
7144         * <p>
7145         * It simply traverses all children to calculate a bounding box then calls
7146         * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
7147         * if they need to handle the bounding box differently.
7148         * <p>
7149         * For example, GridLayoutManager override that method to ensure that even if a column is
7150         * empty, the GridLayoutManager still measures wide enough to include it.
7151         *
7152         * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
7153         * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
7154         */
7155        void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
7156            final int count = getChildCount();
7157            if (count == 0) {
7158                mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
7159                return;
7160            }
7161            int minX = Integer.MAX_VALUE;
7162            int minY = Integer.MAX_VALUE;
7163            int maxX = Integer.MIN_VALUE;
7164            int maxY = Integer.MIN_VALUE;
7165
7166            for (int i = 0; i < count; i++) {
7167                View child = getChildAt(i);
7168                final Rect bounds = mRecyclerView.mTempRect;
7169                getDecoratedBoundsWithMargins(child, bounds);
7170                if (bounds.left < minX) {
7171                    minX = bounds.left;
7172                }
7173                if (bounds.right > maxX) {
7174                    maxX = bounds.right;
7175                }
7176                if (bounds.top < minY) {
7177                    minY = bounds.top;
7178                }
7179                if (bounds.bottom > maxY) {
7180                    maxY = bounds.bottom;
7181                }
7182            }
7183            mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
7184            setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
7185        }
7186
7187        /**
7188         * Sets the measured dimensions from the given bounding box of the children and the
7189         * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
7190         * called after the RecyclerView calls
7191         * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a measurement pass.
7192         * <p>
7193         * This method should call {@link #setMeasuredDimension(int, int)}.
7194         * <p>
7195         * The default implementation adds the RecyclerView's padding to the given bounding box
7196         * then caps the value to be within the given measurement specs.
7197         * <p>
7198         * This method is only called if the LayoutManager opted into the auto measurement API.
7199         *
7200         * @param childrenBounds The bounding box of all children
7201         * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
7202         * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
7203         *
7204         * @see #setAutoMeasureEnabled(boolean)
7205         */
7206        public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
7207            int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
7208            int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
7209            int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
7210            int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
7211            setMeasuredDimension(width, height);
7212        }
7213
7214        /**
7215         * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
7216         */
7217        public void requestLayout() {
7218            if (mRecyclerView != null) {
7219                mRecyclerView.requestLayout();
7220            }
7221        }
7222
7223        /**
7224         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7225         * {@link IllegalStateException} if it <b>is not</b>.
7226         *
7227         * @param message The message for the exception. Can be null.
7228         * @see #assertNotInLayoutOrScroll(String)
7229         */
7230        public void assertInLayoutOrScroll(String message) {
7231            if (mRecyclerView != null) {
7232                mRecyclerView.assertInLayoutOrScroll(message);
7233            }
7234        }
7235
7236        /**
7237         * Chooses a size from the given specs and parameters that is closest to the desired size
7238         * and also complies with the spec.
7239         *
7240         * @param spec The measureSpec
7241         * @param desired The preferred measurement
7242         * @param min The minimum value
7243         *
7244         * @return A size that fits to the given specs
7245         */
7246        public static int chooseSize(int spec, int desired, int min) {
7247            final int mode = View.MeasureSpec.getMode(spec);
7248            final int size = View.MeasureSpec.getSize(spec);
7249            switch (mode) {
7250                case View.MeasureSpec.EXACTLY:
7251                    return size;
7252                case View.MeasureSpec.AT_MOST:
7253                    return Math.min(size, Math.max(desired, min));
7254                case View.MeasureSpec.UNSPECIFIED:
7255                default:
7256                    return Math.max(desired, min);
7257            }
7258        }
7259
7260        /**
7261         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7262         * {@link IllegalStateException} if it <b>is</b>.
7263         *
7264         * @param message The message for the exception. Can be null.
7265         * @see #assertInLayoutOrScroll(String)
7266         */
7267        public void assertNotInLayoutOrScroll(String message) {
7268            if (mRecyclerView != null) {
7269                mRecyclerView.assertNotInLayoutOrScroll(message);
7270            }
7271        }
7272
7273        /**
7274         * Defines whether the layout should be measured by the RecyclerView or the LayoutManager
7275         * wants to handle the layout measurements itself.
7276         * <p>
7277         * This method is usually called by the LayoutManager with value {@code true} if it wants
7278         * to support WRAP_CONTENT. If you are using a public LayoutManager but want to customize
7279         * the measurement logic, you can call this method with {@code false} and override
7280         * {@link LayoutManager#onMeasure(int, int)} to implement your custom measurement logic.
7281         * <p>
7282         * AutoMeasure is a convenience mechanism for LayoutManagers to easily wrap their content or
7283         * handle various specs provided by the RecyclerView's parent.
7284         * It works by calling {@link LayoutManager#onLayoutChildren(Recycler, State)} during an
7285         * {@link RecyclerView#onMeasure(int, int)} call, then calculating desired dimensions based
7286         * on children's positions. It does this while supporting all existing animation
7287         * capabilities of the RecyclerView.
7288         * <p>
7289         * AutoMeasure works as follows:
7290         * <ol>
7291         * <li>LayoutManager should call {@code setAutoMeasureEnabled(true)} to enable it. All of
7292         * the framework LayoutManagers use {@code auto-measure}.</li>
7293         * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided specs are
7294         * exact, RecyclerView will only call LayoutManager's {@code onMeasure} and return without
7295         * doing any layout calculation.</li>
7296         * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
7297         * layout process in {@code onMeasure} call. It will process all pending Adapter updates and
7298         * decide whether to run a predictive layout or not. If it decides to do so, it will first
7299         * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
7300         * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
7301         * return the width and height of the RecyclerView as of the last layout calculation.
7302         * <p>
7303         * After handling the predictive case, RecyclerView will call
7304         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7305         * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
7306         * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
7307         * {@link #getWidth()} and {@link #getWidthMode()}.</li>
7308         * <li>After the layout calculation, RecyclerView sets the measured width & height by
7309         * calculating the bounding box for the children (+ RecyclerView's padding). The
7310         * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
7311         * different values. For instance, GridLayoutManager overrides this value to handle the case
7312         * where if it is vertical and has 3 columns but only 2 items, it should still measure its
7313         * width to fit 3 items, not 2.</li>
7314         * <li>Any following on measure call to the RecyclerView will run
7315         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7316         * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
7317         * take care of which views are actually added / removed / moved / changed for animations so
7318         * that the LayoutManager should not worry about them and handle each
7319         * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.
7320         * </li>
7321         * <li>When measure is complete and RecyclerView's
7322         * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
7323         * whether it already did layout calculations during the measure pass and if so, it re-uses
7324         * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
7325         * if the last measure spec was different from the final dimensions or adapter contents
7326         * have changed between the measure call and the layout call.</li>
7327         * <li>Finally, animations are calculated and run as usual.</li>
7328         * </ol>
7329         *
7330         * @param enabled <code>True</code> if the Layout should be measured by the
7331         *                             RecyclerView, <code>false</code> if the LayoutManager wants
7332         *                             to measure itself.
7333         *
7334         * @see #setMeasuredDimension(Rect, int, int)
7335         * @see #isAutoMeasureEnabled()
7336         */
7337        public void setAutoMeasureEnabled(boolean enabled) {
7338            mAutoMeasure = enabled;
7339        }
7340
7341        /**
7342         * Returns whether the LayoutManager uses the automatic measurement API or not.
7343         *
7344         * @return <code>True</code> if the LayoutManager is measured by the RecyclerView or
7345         * <code>false</code> if it measures itself.
7346         *
7347         * @see #setAutoMeasureEnabled(boolean)
7348         */
7349        public boolean isAutoMeasureEnabled() {
7350            return mAutoMeasure;
7351        }
7352
7353        /**
7354         * Returns whether this LayoutManager supports automatic item animations.
7355         * A LayoutManager wishing to support item animations should obey certain
7356         * rules as outlined in {@link #onLayoutChildren(Recycler, State)}.
7357         * The default return value is <code>false</code>, so subclasses of LayoutManager
7358         * will not get predictive item animations by default.
7359         *
7360         * <p>Whether item animations are enabled in a RecyclerView is determined both
7361         * by the return value from this method and the
7362         * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
7363         * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
7364         * method returns false, then simple item animations will be enabled, in which
7365         * views that are moving onto or off of the screen are simply faded in/out. If
7366         * the RecyclerView has a non-null ItemAnimator and this method returns true,
7367         * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to
7368         * setup up the information needed to more intelligently predict where appearing
7369         * and disappearing views should be animated from/to.</p>
7370         *
7371         * @return true if predictive item animations should be enabled, false otherwise
7372         */
7373        public boolean supportsPredictiveItemAnimations() {
7374            return false;
7375        }
7376
7377        /**
7378         * Sets whether the LayoutManager should be queried for views outside of
7379         * its viewport while the UI thread is idle between frames.
7380         *
7381         * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
7382         * view system traversals on devices running API 21 or greater. Default value is true.</p>
7383         *
7384         * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
7385         * to RenderThread and the starting up its next frame at the next VSync pulse. By
7386         * prefetching out of window views in this time period, delays from inflation and view
7387         * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
7388         *
7389         * <p>While prefetch is enabled, it will have the side effect of expanding the effective
7390         * size of the View cache to hold prefetched views.</p>
7391         *
7392         * @param enabled <code>True</code> if items should be prefetched in between traversals.
7393         *
7394         * @see #isItemPrefetchEnabled()
7395         */
7396        public final void setItemPrefetchEnabled(boolean enabled) {
7397            if (enabled != mItemPrefetchEnabled) {
7398                mItemPrefetchEnabled = enabled;
7399                mPrefetchMaxCountObserved = 0;
7400                if (mRecyclerView != null) {
7401                    mRecyclerView.mRecycler.updateViewCacheSize();
7402                }
7403            }
7404        }
7405
7406        /**
7407         * Sets whether the LayoutManager should be queried for views outside of
7408         * its viewport while the UI thread is idle between frames.
7409         *
7410         * @see #setItemPrefetchEnabled(boolean)
7411         *
7412         * @return true if item prefetch is enabled, false otherwise
7413         */
7414        public final boolean isItemPrefetchEnabled() {
7415            return mItemPrefetchEnabled;
7416        }
7417
7418        /**
7419         * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
7420         *
7421         * <p>If item prefetch is enabled, this method is called in between traversals to gather
7422         * which positions the LayoutManager will soon need, given upcoming movement in subsequent
7423         * traversals.</p>
7424         *
7425         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7426         * each item to be prepared, and these positions will have their ViewHolders created and
7427         * bound, if there is sufficient time available, in advance of being needed by a
7428         * scroll or layout.</p>
7429         *
7430         * @param dx X movement component.
7431         * @param dy Y movement component.
7432         * @param state State of RecyclerView
7433         * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7434         *
7435         * @see #isItemPrefetchEnabled()
7436         * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
7437         */
7438        public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
7439                LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7440
7441        /**
7442         * Gather all positions from the LayoutManager to be prefetched in preperation for its
7443         * RecyclerView to come on screen, due to the movement of another, containing RecyclerView.
7444         *
7445         * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
7446         *
7447         * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
7448         * LayoutManager, this method is called in between draw traversals to gather
7449         * which positions this LayoutManager will first need, once it appears on the screen.</p>
7450         *
7451         * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
7452         * vertically scrolling LayoutManager, this method would be called when the horizontal list
7453         * is about to come onscreen.</p>
7454         *
7455         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7456         * each item to be prepared, and these positions will have their ViewHolders created and
7457         * bound, if there is sufficient time available, in advance of being needed by a
7458         * scroll or layout.</p>
7459         *
7460         * @param adapterItemCount number of items in the associated adapter.
7461         * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7462         *
7463         * @see #isItemPrefetchEnabled()
7464         * @see #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
7465         */
7466        public void collectInitialPrefetchPositions(int adapterItemCount,
7467                LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7468
7469        void dispatchAttachedToWindow(RecyclerView view) {
7470            mIsAttachedToWindow = true;
7471            onAttachedToWindow(view);
7472        }
7473
7474        void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
7475            mIsAttachedToWindow = false;
7476            onDetachedFromWindow(view, recycler);
7477        }
7478
7479        /**
7480         * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
7481         * to a window.
7482         *
7483         * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
7484         * is attached to window.
7485         */
7486        public boolean isAttachedToWindow() {
7487            return mIsAttachedToWindow;
7488        }
7489
7490        /**
7491         * Causes the Runnable to execute on the next animation time step.
7492         * The runnable will be run on the user interface thread.
7493         * <p>
7494         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7495         *
7496         * @param action The Runnable that will be executed.
7497         *
7498         * @see #removeCallbacks
7499         */
7500        public void postOnAnimation(Runnable action) {
7501            if (mRecyclerView != null) {
7502                ViewCompat.postOnAnimation(mRecyclerView, action);
7503            }
7504        }
7505
7506        /**
7507         * Removes the specified Runnable from the message queue.
7508         * <p>
7509         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7510         *
7511         * @param action The Runnable to remove from the message handling queue
7512         *
7513         * @return true if RecyclerView could ask the Handler to remove the Runnable,
7514         *         false otherwise. When the returned value is true, the Runnable
7515         *         may or may not have been actually removed from the message queue
7516         *         (for instance, if the Runnable was not in the queue already.)
7517         *
7518         * @see #postOnAnimation
7519         */
7520        public boolean removeCallbacks(Runnable action) {
7521            if (mRecyclerView != null) {
7522                return mRecyclerView.removeCallbacks(action);
7523            }
7524            return false;
7525        }
7526        /**
7527         * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
7528         * is attached to a window.
7529         * <p>
7530         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7531         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7532         * not requested on the RecyclerView while it was detached.
7533         * <p>
7534         * Subclass implementations should always call through to the superclass implementation.
7535         *
7536         * @param view The RecyclerView this LayoutManager is bound to
7537         *
7538         * @see #onDetachedFromWindow(RecyclerView, Recycler)
7539         */
7540        @CallSuper
7541        public void onAttachedToWindow(RecyclerView view) {
7542        }
7543
7544        /**
7545         * @deprecated
7546         * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
7547         */
7548        @Deprecated
7549        public void onDetachedFromWindow(RecyclerView view) {
7550
7551        }
7552
7553        /**
7554         * Called when this LayoutManager is detached from its parent RecyclerView or when
7555         * its parent RecyclerView is detached from its window.
7556         * <p>
7557         * LayoutManager should clear all of its View references as another LayoutManager might be
7558         * assigned to the RecyclerView.
7559         * <p>
7560         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7561         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7562         * not requested on the RecyclerView while it was detached.
7563         * <p>
7564         * If your LayoutManager has View references that it cleans in on-detach, it should also
7565         * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
7566         * RecyclerView is re-attached.
7567         * <p>
7568         * Subclass implementations should always call through to the superclass implementation.
7569         *
7570         * @param view The RecyclerView this LayoutManager is bound to
7571         * @param recycler The recycler to use if you prefer to recycle your children instead of
7572         *                 keeping them around.
7573         *
7574         * @see #onAttachedToWindow(RecyclerView)
7575         */
7576        @CallSuper
7577        public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
7578            onDetachedFromWindow(view);
7579        }
7580
7581        /**
7582         * Check if the RecyclerView is configured to clip child views to its padding.
7583         *
7584         * @return true if this RecyclerView clips children to its padding, false otherwise
7585         */
7586        public boolean getClipToPadding() {
7587            return mRecyclerView != null && mRecyclerView.mClipToPadding;
7588        }
7589
7590        /**
7591         * Lay out all relevant child views from the given adapter.
7592         *
7593         * The LayoutManager is in charge of the behavior of item animations. By default,
7594         * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
7595         * item animations are enabled. This means that add/remove operations on the
7596         * adapter will result in animations to add new or appearing items, removed or
7597         * disappearing items, and moved items. If a LayoutManager returns false from
7598         * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
7599         * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
7600         * RecyclerView will have enough information to run those animations in a simple
7601         * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
7602         * simply fade views in and out, whether they are actually added/removed or whether
7603         * they are moved on or off the screen due to other add/remove operations.
7604         *
7605         * <p>A LayoutManager wanting a better item animation experience, where items can be
7606         * animated onto and off of the screen according to where the items exist when they
7607         * are not on screen, then the LayoutManager should return true from
7608         * {@link #supportsPredictiveItemAnimations()} and add additional logic to
7609         * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
7610         * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
7611         * once as a "pre" layout step to determine where items would have been prior to
7612         * a real layout, and again to do the "real" layout. In the pre-layout phase,
7613         * items will remember their pre-layout positions to allow them to be laid out
7614         * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
7615         * be returned from the scrap to help determine correct placement of other items.
7616         * These removed items should not be added to the child list, but should be used
7617         * to help calculate correct positioning of other views, including views that
7618         * were not previously onscreen (referred to as APPEARING views), but whose
7619         * pre-layout offscreen position can be determined given the extra
7620         * information about the pre-layout removed views.</p>
7621         *
7622         * <p>The second layout pass is the real layout in which only non-removed views
7623         * will be used. The only additional requirement during this pass is, if
7624         * {@link #supportsPredictiveItemAnimations()} returns true, to note which
7625         * views exist in the child list prior to layout and which are not there after
7626         * layout (referred to as DISAPPEARING views), and to position/layout those views
7627         * appropriately, without regard to the actual bounds of the RecyclerView. This allows
7628         * the animation system to know the location to which to animate these disappearing
7629         * views.</p>
7630         *
7631         * <p>The default LayoutManager implementations for RecyclerView handle all of these
7632         * requirements for animations already. Clients of RecyclerView can either use one
7633         * of these layout managers directly or look at their implementations of
7634         * onLayoutChildren() to see how they account for the APPEARING and
7635         * DISAPPEARING views.</p>
7636         *
7637         * @param recycler         Recycler to use for fetching potentially cached views for a
7638         *                         position
7639         * @param state            Transient state of RecyclerView
7640         */
7641        public void onLayoutChildren(Recycler recycler, State state) {
7642            Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
7643        }
7644
7645        /**
7646         * Called after a full layout calculation is finished. The layout calculation may include
7647         * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
7648         * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
7649         * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
7650         * <p>
7651         * This is a good place for the LayoutManager to do some cleanup like pending scroll
7652         * position, saved state etc.
7653         *
7654         * @param state Transient state of RecyclerView
7655         */
7656        public void onLayoutCompleted(State state) {
7657        }
7658
7659        /**
7660         * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
7661         *
7662         * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
7663         * to store extra information specific to the layout. Client code should subclass
7664         * {@link RecyclerView.LayoutParams} for this purpose.</p>
7665         *
7666         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7667         * you must also override
7668         * {@link #checkLayoutParams(LayoutParams)},
7669         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7670         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7671         *
7672         * @return A new LayoutParams for a child view
7673         */
7674        public abstract LayoutParams generateDefaultLayoutParams();
7675
7676        /**
7677         * Determines the validity of the supplied LayoutParams object.
7678         *
7679         * <p>This should check to make sure that the object is of the correct type
7680         * and all values are within acceptable ranges. The default implementation
7681         * returns <code>true</code> for non-null params.</p>
7682         *
7683         * @param lp LayoutParams object to check
7684         * @return true if this LayoutParams object is valid, false otherwise
7685         */
7686        public boolean checkLayoutParams(LayoutParams lp) {
7687            return lp != null;
7688        }
7689
7690        /**
7691         * Create a LayoutParams object suitable for this LayoutManager, copying relevant
7692         * values from the supplied LayoutParams object if possible.
7693         *
7694         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7695         * you must also override
7696         * {@link #checkLayoutParams(LayoutParams)},
7697         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7698         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7699         *
7700         * @param lp Source LayoutParams object to copy values from
7701         * @return a new LayoutParams object
7702         */
7703        public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
7704            if (lp instanceof LayoutParams) {
7705                return new LayoutParams((LayoutParams) lp);
7706            } else if (lp instanceof MarginLayoutParams) {
7707                return new LayoutParams((MarginLayoutParams) lp);
7708            } else {
7709                return new LayoutParams(lp);
7710            }
7711        }
7712
7713        /**
7714         * Create a LayoutParams object suitable for this LayoutManager from
7715         * an inflated layout resource.
7716         *
7717         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7718         * you must also override
7719         * {@link #checkLayoutParams(LayoutParams)},
7720         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7721         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7722         *
7723         * @param c Context for obtaining styled attributes
7724         * @param attrs AttributeSet describing the supplied arguments
7725         * @return a new LayoutParams object
7726         */
7727        public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
7728            return new LayoutParams(c, attrs);
7729        }
7730
7731        /**
7732         * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
7733         * The default implementation does nothing and returns 0.
7734         *
7735         * @param dx            distance to scroll by in pixels. X increases as scroll position
7736         *                      approaches the right.
7737         * @param recycler      Recycler to use for fetching potentially cached views for a
7738         *                      position
7739         * @param state         Transient state of RecyclerView
7740         * @return The actual distance scrolled. The return value will be negative if dx was
7741         * negative and scrolling proceeeded in that direction.
7742         * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
7743         */
7744        public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
7745            return 0;
7746        }
7747
7748        /**
7749         * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
7750         * The default implementation does nothing and returns 0.
7751         *
7752         * @param dy            distance to scroll in pixels. Y increases as scroll position
7753         *                      approaches the bottom.
7754         * @param recycler      Recycler to use for fetching potentially cached views for a
7755         *                      position
7756         * @param state         Transient state of RecyclerView
7757         * @return The actual distance scrolled. The return value will be negative if dy was
7758         * negative and scrolling proceeeded in that direction.
7759         * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
7760         */
7761        public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
7762            return 0;
7763        }
7764
7765        /**
7766         * Query if horizontal scrolling is currently supported. The default implementation
7767         * returns false.
7768         *
7769         * @return True if this LayoutManager can scroll the current contents horizontally
7770         */
7771        public boolean canScrollHorizontally() {
7772            return false;
7773        }
7774
7775        /**
7776         * Query if vertical scrolling is currently supported. The default implementation
7777         * returns false.
7778         *
7779         * @return True if this LayoutManager can scroll the current contents vertically
7780         */
7781        public boolean canScrollVertically() {
7782            return false;
7783        }
7784
7785        /**
7786         * Scroll to the specified adapter position.
7787         *
7788         * Actual position of the item on the screen depends on the LayoutManager implementation.
7789         * @param position Scroll to this adapter position.
7790         */
7791        public void scrollToPosition(int position) {
7792            if (DEBUG) {
7793                Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
7794            }
7795        }
7796
7797        /**
7798         * <p>Smooth scroll to the specified adapter position.</p>
7799         * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
7800         * instance and call {@link #startSmoothScroll(SmoothScroller)}.
7801         * </p>
7802         * @param recyclerView The RecyclerView to which this layout manager is attached
7803         * @param state    Current State of RecyclerView
7804         * @param position Scroll to this adapter position.
7805         */
7806        public void smoothScrollToPosition(RecyclerView recyclerView, State state,
7807                int position) {
7808            Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
7809        }
7810
7811        /**
7812         * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
7813         * <p>Calling this method will cancel any previous smooth scroll request.</p>
7814         * @param smoothScroller Instance which defines how smooth scroll should be animated
7815         */
7816        public void startSmoothScroll(SmoothScroller smoothScroller) {
7817            if (mSmoothScroller != null && smoothScroller != mSmoothScroller
7818                    && mSmoothScroller.isRunning()) {
7819                mSmoothScroller.stop();
7820            }
7821            mSmoothScroller = smoothScroller;
7822            mSmoothScroller.start(mRecyclerView, this);
7823        }
7824
7825        /**
7826         * @return true if RecycylerView is currently in the state of smooth scrolling.
7827         */
7828        public boolean isSmoothScrolling() {
7829            return mSmoothScroller != null && mSmoothScroller.isRunning();
7830        }
7831
7832
7833        /**
7834         * Returns the resolved layout direction for this RecyclerView.
7835         *
7836         * @return {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout
7837         * direction is RTL or returns
7838         * {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction
7839         * is not RTL.
7840         */
7841        public int getLayoutDirection() {
7842            return ViewCompat.getLayoutDirection(mRecyclerView);
7843        }
7844
7845        /**
7846         * Ends all animations on the view created by the {@link ItemAnimator}.
7847         *
7848         * @param view The View for which the animations should be ended.
7849         * @see RecyclerView.ItemAnimator#endAnimations()
7850         */
7851        public void endAnimation(View view) {
7852            if (mRecyclerView.mItemAnimator != null) {
7853                mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
7854            }
7855        }
7856
7857        /**
7858         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
7859         * to the layout that is known to be going away, either because it has been
7860         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
7861         * visible portion of the container but is being laid out in order to inform RecyclerView
7862         * in how to animate the item out of view.
7863         * <p>
7864         * Views added via this method are going to be invisible to LayoutManager after the
7865         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
7866         * or won't be included in {@link #getChildCount()} method.
7867         *
7868         * @param child View to add and then remove with animation.
7869         */
7870        public void addDisappearingView(View child) {
7871            addDisappearingView(child, -1);
7872        }
7873
7874        /**
7875         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
7876         * to the layout that is known to be going away, either because it has been
7877         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
7878         * visible portion of the container but is being laid out in order to inform RecyclerView
7879         * in how to animate the item out of view.
7880         * <p>
7881         * Views added via this method are going to be invisible to LayoutManager after the
7882         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
7883         * or won't be included in {@link #getChildCount()} method.
7884         *
7885         * @param child View to add and then remove with animation.
7886         * @param index Index of the view.
7887         */
7888        public void addDisappearingView(View child, int index) {
7889            addViewInt(child, index, true);
7890        }
7891
7892        /**
7893         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
7894         * use this method to add views obtained from a {@link Recycler} using
7895         * {@link Recycler#getViewForPosition(int)}.
7896         *
7897         * @param child View to add
7898         */
7899        public void addView(View child) {
7900            addView(child, -1);
7901        }
7902
7903        /**
7904         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
7905         * use this method to add views obtained from a {@link Recycler} using
7906         * {@link Recycler#getViewForPosition(int)}.
7907         *
7908         * @param child View to add
7909         * @param index Index to add child at
7910         */
7911        public void addView(View child, int index) {
7912            addViewInt(child, index, false);
7913        }
7914
7915        private void addViewInt(View child, int index, boolean disappearing) {
7916            final ViewHolder holder = getChildViewHolderInt(child);
7917            if (disappearing || holder.isRemoved()) {
7918                // these views will be hidden at the end of the layout pass.
7919                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
7920            } else {
7921                // This may look like unnecessary but may happen if layout manager supports
7922                // predictive layouts and adapter removed then re-added the same item.
7923                // In this case, added version will be visible in the post layout (because add is
7924                // deferred) but RV will still bind it to the same View.
7925                // So if a View re-appears in post layout pass, remove it from disappearing list.
7926                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
7927            }
7928            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
7929            if (holder.wasReturnedFromScrap() || holder.isScrap()) {
7930                if (holder.isScrap()) {
7931                    holder.unScrap();
7932                } else {
7933                    holder.clearReturnedFromScrapFlag();
7934                }
7935                mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
7936                if (DISPATCH_TEMP_DETACH) {
7937                    ViewCompat.dispatchFinishTemporaryDetach(child);
7938                }
7939            } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
7940                // ensure in correct position
7941                int currentIndex = mChildHelper.indexOfChild(child);
7942                if (index == -1) {
7943                    index = mChildHelper.getChildCount();
7944                }
7945                if (currentIndex == -1) {
7946                    throw new IllegalStateException("Added View has RecyclerView as parent but"
7947                            + " view is not a real child. Unfiltered index:"
7948                            + mRecyclerView.indexOfChild(child));
7949                }
7950                if (currentIndex != index) {
7951                    mRecyclerView.mLayout.moveView(currentIndex, index);
7952                }
7953            } else {
7954                mChildHelper.addView(child, index, false);
7955                lp.mInsetsDirty = true;
7956                if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
7957                    mSmoothScroller.onChildAttachedToWindow(child);
7958                }
7959            }
7960            if (lp.mPendingInvalidate) {
7961                if (DEBUG) {
7962                    Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
7963                }
7964                holder.itemView.invalidate();
7965                lp.mPendingInvalidate = false;
7966            }
7967        }
7968
7969        /**
7970         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
7971         * use this method to completely remove a child view that is no longer needed.
7972         * LayoutManagers should strongly consider recycling removed views using
7973         * {@link Recycler#recycleView(android.view.View)}.
7974         *
7975         * @param child View to remove
7976         */
7977        public void removeView(View child) {
7978            mChildHelper.removeView(child);
7979        }
7980
7981        /**
7982         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
7983         * use this method to completely remove a child view that is no longer needed.
7984         * LayoutManagers should strongly consider recycling removed views using
7985         * {@link Recycler#recycleView(android.view.View)}.
7986         *
7987         * @param index Index of the child view to remove
7988         */
7989        public void removeViewAt(int index) {
7990            final View child = getChildAt(index);
7991            if (child != null) {
7992                mChildHelper.removeViewAt(index);
7993            }
7994        }
7995
7996        /**
7997         * Remove all views from the currently attached RecyclerView. This will not recycle
7998         * any of the affected views; the LayoutManager is responsible for doing so if desired.
7999         */
8000        public void removeAllViews() {
8001            // Only remove non-animating views
8002            final int childCount = getChildCount();
8003            for (int i = childCount - 1; i >= 0; i--) {
8004                mChildHelper.removeViewAt(i);
8005            }
8006        }
8007
8008        /**
8009         * Returns offset of the RecyclerView's text baseline from the its top boundary.
8010         *
8011         * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
8012         * there is no baseline.
8013         */
8014        public int getBaseline() {
8015            return -1;
8016        }
8017
8018        /**
8019         * Returns the adapter position of the item represented by the given View. This does not
8020         * contain any adapter changes that might have happened after the last layout.
8021         *
8022         * @param view The view to query
8023         * @return The adapter position of the item which is rendered by this View.
8024         */
8025        public int getPosition(View view) {
8026            return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
8027        }
8028
8029        /**
8030         * Returns the View type defined by the adapter.
8031         *
8032         * @param view The view to query
8033         * @return The type of the view assigned by the adapter.
8034         */
8035        public int getItemViewType(View view) {
8036            return getChildViewHolderInt(view).getItemViewType();
8037        }
8038
8039        /**
8040         * Traverses the ancestors of the given view and returns the item view that contains it
8041         * and also a direct child of the LayoutManager.
8042         * <p>
8043         * Note that this method may return null if the view is a child of the RecyclerView but
8044         * not a child of the LayoutManager (e.g. running a disappear animation).
8045         *
8046         * @param view The view that is a descendant of the LayoutManager.
8047         *
8048         * @return The direct child of the LayoutManager which contains the given view or null if
8049         * the provided view is not a descendant of this LayoutManager.
8050         *
8051         * @see RecyclerView#getChildViewHolder(View)
8052         * @see RecyclerView#findContainingViewHolder(View)
8053         */
8054        @Nullable
8055        public View findContainingItemView(View view) {
8056            if (mRecyclerView == null) {
8057                return null;
8058            }
8059            View found = mRecyclerView.findContainingItemView(view);
8060            if (found == null) {
8061                return null;
8062            }
8063            if (mChildHelper.isHidden(found)) {
8064                return null;
8065            }
8066            return found;
8067        }
8068
8069        /**
8070         * Finds the view which represents the given adapter position.
8071         * <p>
8072         * This method traverses each child since it has no information about child order.
8073         * Override this method to improve performance if your LayoutManager keeps data about
8074         * child views.
8075         * <p>
8076         * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
8077         *
8078         * @param position Position of the item in adapter
8079         * @return The child view that represents the given position or null if the position is not
8080         * laid out
8081         */
8082        public View findViewByPosition(int position) {
8083            final int childCount = getChildCount();
8084            for (int i = 0; i < childCount; i++) {
8085                View child = getChildAt(i);
8086                ViewHolder vh = getChildViewHolderInt(child);
8087                if (vh == null) {
8088                    continue;
8089                }
8090                if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
8091                        && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
8092                    return child;
8093                }
8094            }
8095            return null;
8096        }
8097
8098        /**
8099         * Temporarily detach a child view.
8100         *
8101         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
8102         * views currently attached to the RecyclerView. Generally LayoutManager implementations
8103         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
8104         * so that the detached view may be rebound and reused.</p>
8105         *
8106         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
8107         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
8108         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
8109         * before the LayoutManager entry point method called by RecyclerView returns.</p>
8110         *
8111         * @param child Child to detach
8112         */
8113        public void detachView(View child) {
8114            final int ind = mChildHelper.indexOfChild(child);
8115            if (ind >= 0) {
8116                detachViewInternal(ind, child);
8117            }
8118        }
8119
8120        /**
8121         * Temporarily detach a child view.
8122         *
8123         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
8124         * views currently attached to the RecyclerView. Generally LayoutManager implementations
8125         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
8126         * so that the detached view may be rebound and reused.</p>
8127         *
8128         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
8129         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
8130         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
8131         * before the LayoutManager entry point method called by RecyclerView returns.</p>
8132         *
8133         * @param index Index of the child to detach
8134         */
8135        public void detachViewAt(int index) {
8136            detachViewInternal(index, getChildAt(index));
8137        }
8138
8139        private void detachViewInternal(int index, View view) {
8140            if (DISPATCH_TEMP_DETACH) {
8141                ViewCompat.dispatchStartTemporaryDetach(view);
8142            }
8143            mChildHelper.detachViewFromParent(index);
8144        }
8145
8146        /**
8147         * Reattach a previously {@link #detachView(android.view.View) detached} view.
8148         * This method should not be used to reattach views that were previously
8149         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8150         *
8151         * @param child Child to reattach
8152         * @param index Intended child index for child
8153         * @param lp LayoutParams for child
8154         */
8155        public void attachView(View child, int index, LayoutParams lp) {
8156            ViewHolder vh = getChildViewHolderInt(child);
8157            if (vh.isRemoved()) {
8158                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
8159            } else {
8160                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
8161            }
8162            mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
8163            if (DISPATCH_TEMP_DETACH)  {
8164                ViewCompat.dispatchFinishTemporaryDetach(child);
8165            }
8166        }
8167
8168        /**
8169         * Reattach a previously {@link #detachView(android.view.View) detached} view.
8170         * This method should not be used to reattach views that were previously
8171         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8172         *
8173         * @param child Child to reattach
8174         * @param index Intended child index for child
8175         */
8176        public void attachView(View child, int index) {
8177            attachView(child, index, (LayoutParams) child.getLayoutParams());
8178        }
8179
8180        /**
8181         * Reattach a previously {@link #detachView(android.view.View) detached} view.
8182         * This method should not be used to reattach views that were previously
8183         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
8184         *
8185         * @param child Child to reattach
8186         */
8187        public void attachView(View child) {
8188            attachView(child, -1);
8189        }
8190
8191        /**
8192         * Finish removing a view that was previously temporarily
8193         * {@link #detachView(android.view.View) detached}.
8194         *
8195         * @param child Detached child to remove
8196         */
8197        public void removeDetachedView(View child) {
8198            mRecyclerView.removeDetachedView(child, false);
8199        }
8200
8201        /**
8202         * Moves a View from one position to another.
8203         *
8204         * @param fromIndex The View's initial index
8205         * @param toIndex The View's target index
8206         */
8207        public void moveView(int fromIndex, int toIndex) {
8208            View view = getChildAt(fromIndex);
8209            if (view == null) {
8210                throw new IllegalArgumentException("Cannot move a child from non-existing index:"
8211                        + fromIndex);
8212            }
8213            detachViewAt(fromIndex);
8214            attachView(view, toIndex);
8215        }
8216
8217        /**
8218         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8219         *
8220         * <p>Scrapping a view allows it to be rebound and reused to show updated or
8221         * different data.</p>
8222         *
8223         * @param child Child to detach and scrap
8224         * @param recycler Recycler to deposit the new scrap view into
8225         */
8226        public void detachAndScrapView(View child, Recycler recycler) {
8227            int index = mChildHelper.indexOfChild(child);
8228            scrapOrRecycleView(recycler, index, child);
8229        }
8230
8231        /**
8232         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8233         *
8234         * <p>Scrapping a view allows it to be rebound and reused to show updated or
8235         * different data.</p>
8236         *
8237         * @param index Index of child to detach and scrap
8238         * @param recycler Recycler to deposit the new scrap view into
8239         */
8240        public void detachAndScrapViewAt(int index, Recycler recycler) {
8241            final View child = getChildAt(index);
8242            scrapOrRecycleView(recycler, index, child);
8243        }
8244
8245        /**
8246         * Remove a child view and recycle it using the given Recycler.
8247         *
8248         * @param child Child to remove and recycle
8249         * @param recycler Recycler to use to recycle child
8250         */
8251        public void removeAndRecycleView(View child, Recycler recycler) {
8252            removeView(child);
8253            recycler.recycleView(child);
8254        }
8255
8256        /**
8257         * Remove a child view and recycle it using the given Recycler.
8258         *
8259         * @param index Index of child to remove and recycle
8260         * @param recycler Recycler to use to recycle child
8261         */
8262        public void removeAndRecycleViewAt(int index, Recycler recycler) {
8263            final View view = getChildAt(index);
8264            removeViewAt(index);
8265            recycler.recycleView(view);
8266        }
8267
8268        /**
8269         * Return the current number of child views attached to the parent RecyclerView.
8270         * This does not include child views that were temporarily detached and/or scrapped.
8271         *
8272         * @return Number of attached children
8273         */
8274        public int getChildCount() {
8275            return mChildHelper != null ? mChildHelper.getChildCount() : 0;
8276        }
8277
8278        /**
8279         * Return the child view at the given index
8280         * @param index Index of child to return
8281         * @return Child view at index
8282         */
8283        public View getChildAt(int index) {
8284            return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
8285        }
8286
8287        /**
8288         * Return the width measurement spec mode of the RecyclerView.
8289         * <p>
8290         * This value is set only if the LayoutManager opts into the auto measure api via
8291         * {@link #setAutoMeasureEnabled(boolean)}.
8292         * <p>
8293         * When RecyclerView is running a layout, this value is always set to
8294         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8295         *
8296         * @return Width measure spec mode.
8297         *
8298         * @see View.MeasureSpec#getMode(int)
8299         * @see View#onMeasure(int, int)
8300         */
8301        public int getWidthMode() {
8302            return mWidthMode;
8303        }
8304
8305        /**
8306         * Return the height measurement spec mode of the RecyclerView.
8307         * <p>
8308         * This value is set only if the LayoutManager opts into the auto measure api via
8309         * {@link #setAutoMeasureEnabled(boolean)}.
8310         * <p>
8311         * When RecyclerView is running a layout, this value is always set to
8312         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8313         *
8314         * @return Height measure spec mode.
8315         *
8316         * @see View.MeasureSpec#getMode(int)
8317         * @see View#onMeasure(int, int)
8318         */
8319        public int getHeightMode() {
8320            return mHeightMode;
8321        }
8322
8323        /**
8324         * Return the width of the parent RecyclerView
8325         *
8326         * @return Width in pixels
8327         */
8328        public int getWidth() {
8329            return mWidth;
8330        }
8331
8332        /**
8333         * Return the height of the parent RecyclerView
8334         *
8335         * @return Height in pixels
8336         */
8337        public int getHeight() {
8338            return mHeight;
8339        }
8340
8341        /**
8342         * Return the left padding of the parent RecyclerView
8343         *
8344         * @return Padding in pixels
8345         */
8346        public int getPaddingLeft() {
8347            return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
8348        }
8349
8350        /**
8351         * Return the top padding of the parent RecyclerView
8352         *
8353         * @return Padding in pixels
8354         */
8355        public int getPaddingTop() {
8356            return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
8357        }
8358
8359        /**
8360         * Return the right padding of the parent RecyclerView
8361         *
8362         * @return Padding in pixels
8363         */
8364        public int getPaddingRight() {
8365            return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
8366        }
8367
8368        /**
8369         * Return the bottom padding of the parent RecyclerView
8370         *
8371         * @return Padding in pixels
8372         */
8373        public int getPaddingBottom() {
8374            return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
8375        }
8376
8377        /**
8378         * Return the start padding of the parent RecyclerView
8379         *
8380         * @return Padding in pixels
8381         */
8382        public int getPaddingStart() {
8383            return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;
8384        }
8385
8386        /**
8387         * Return the end padding of the parent RecyclerView
8388         *
8389         * @return Padding in pixels
8390         */
8391        public int getPaddingEnd() {
8392            return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;
8393        }
8394
8395        /**
8396         * Returns true if the RecyclerView this LayoutManager is bound to has focus.
8397         *
8398         * @return True if the RecyclerView has focus, false otherwise.
8399         * @see View#isFocused()
8400         */
8401        public boolean isFocused() {
8402            return mRecyclerView != null && mRecyclerView.isFocused();
8403        }
8404
8405        /**
8406         * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
8407         *
8408         * @return true if the RecyclerView has or contains focus
8409         * @see View#hasFocus()
8410         */
8411        public boolean hasFocus() {
8412            return mRecyclerView != null && mRecyclerView.hasFocus();
8413        }
8414
8415        /**
8416         * Returns the item View which has or contains focus.
8417         *
8418         * @return A direct child of RecyclerView which has focus or contains the focused child.
8419         */
8420        public View getFocusedChild() {
8421            if (mRecyclerView == null) {
8422                return null;
8423            }
8424            final View focused = mRecyclerView.getFocusedChild();
8425            if (focused == null || mChildHelper.isHidden(focused)) {
8426                return null;
8427            }
8428            return focused;
8429        }
8430
8431        /**
8432         * Returns the number of items in the adapter bound to the parent RecyclerView.
8433         * <p>
8434         * Note that this number is not necessarily equal to
8435         * {@link State#getItemCount() State#getItemCount()}. In methods where {@link State} is
8436         * available, you should use {@link State#getItemCount() State#getItemCount()} instead.
8437         * For more details, check the documentation for
8438         * {@link State#getItemCount() State#getItemCount()}.
8439         *
8440         * @return The number of items in the bound adapter
8441         * @see State#getItemCount()
8442         */
8443        public int getItemCount() {
8444            final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
8445            return a != null ? a.getItemCount() : 0;
8446        }
8447
8448        /**
8449         * Offset all child views attached to the parent RecyclerView by dx pixels along
8450         * the horizontal axis.
8451         *
8452         * @param dx Pixels to offset by
8453         */
8454        public void offsetChildrenHorizontal(int dx) {
8455            if (mRecyclerView != null) {
8456                mRecyclerView.offsetChildrenHorizontal(dx);
8457            }
8458        }
8459
8460        /**
8461         * Offset all child views attached to the parent RecyclerView by dy pixels along
8462         * the vertical axis.
8463         *
8464         * @param dy Pixels to offset by
8465         */
8466        public void offsetChildrenVertical(int dy) {
8467            if (mRecyclerView != null) {
8468                mRecyclerView.offsetChildrenVertical(dy);
8469            }
8470        }
8471
8472        /**
8473         * Flags a view so that it will not be scrapped or recycled.
8474         * <p>
8475         * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
8476         * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
8477         * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
8478         * ignore the child.
8479         * <p>
8480         * Before this child can be recycled again, you have to call
8481         * {@link #stopIgnoringView(View)}.
8482         * <p>
8483         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8484         *
8485         * @param view View to ignore.
8486         * @see #stopIgnoringView(View)
8487         */
8488        public void ignoreView(View view) {
8489            if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
8490                // checking this because calling this method on a recycled or detached view may
8491                // cause loss of state.
8492                throw new IllegalArgumentException("View should be fully attached to be ignored");
8493            }
8494            final ViewHolder vh = getChildViewHolderInt(view);
8495            vh.addFlags(ViewHolder.FLAG_IGNORE);
8496            mRecyclerView.mViewInfoStore.removeViewHolder(vh);
8497        }
8498
8499        /**
8500         * View can be scrapped and recycled again.
8501         * <p>
8502         * Note that calling this method removes all information in the view holder.
8503         * <p>
8504         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8505         *
8506         * @param view View to ignore.
8507         */
8508        public void stopIgnoringView(View view) {
8509            final ViewHolder vh = getChildViewHolderInt(view);
8510            vh.stopIgnoring();
8511            vh.resetInternal();
8512            vh.addFlags(ViewHolder.FLAG_INVALID);
8513        }
8514
8515        /**
8516         * Temporarily detach and scrap all currently attached child views. Views will be scrapped
8517         * into the given Recycler. The Recycler may prefer to reuse scrap views before
8518         * other views that were previously recycled.
8519         *
8520         * @param recycler Recycler to scrap views into
8521         */
8522        public void detachAndScrapAttachedViews(Recycler recycler) {
8523            final int childCount = getChildCount();
8524            for (int i = childCount - 1; i >= 0; i--) {
8525                final View v = getChildAt(i);
8526                scrapOrRecycleView(recycler, i, v);
8527            }
8528        }
8529
8530        private void scrapOrRecycleView(Recycler recycler, int index, View view) {
8531            final ViewHolder viewHolder = getChildViewHolderInt(view);
8532            if (viewHolder.shouldIgnore()) {
8533                if (DEBUG) {
8534                    Log.d(TAG, "ignoring view " + viewHolder);
8535                }
8536                return;
8537            }
8538            if (viewHolder.isInvalid() && !viewHolder.isRemoved()
8539                    && !mRecyclerView.mAdapter.hasStableIds()) {
8540                removeViewAt(index);
8541                recycler.recycleViewHolderInternal(viewHolder);
8542            } else {
8543                detachViewAt(index);
8544                recycler.scrapView(view);
8545                mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
8546            }
8547        }
8548
8549        /**
8550         * Recycles the scrapped views.
8551         * <p>
8552         * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
8553         * the expected behavior if scrapped views are used for animations. Otherwise, we need to
8554         * call remove and invalidate RecyclerView to ensure UI update.
8555         *
8556         * @param recycler Recycler
8557         */
8558        void removeAndRecycleScrapInt(Recycler recycler) {
8559            final int scrapCount = recycler.getScrapCount();
8560            // Loop backward, recycler might be changed by removeDetachedView()
8561            for (int i = scrapCount - 1; i >= 0; i--) {
8562                final View scrap = recycler.getScrapViewAt(i);
8563                final ViewHolder vh = getChildViewHolderInt(scrap);
8564                if (vh.shouldIgnore()) {
8565                    continue;
8566                }
8567                // If the scrap view is animating, we need to cancel them first. If we cancel it
8568                // here, ItemAnimator callback may recycle it which will cause double recycling.
8569                // To avoid this, we mark it as not recycleable before calling the item animator.
8570                // Since removeDetachedView calls a user API, a common mistake (ending animations on
8571                // the view) may recycle it too, so we guard it before we call user APIs.
8572                vh.setIsRecyclable(false);
8573                if (vh.isTmpDetached()) {
8574                    mRecyclerView.removeDetachedView(scrap, false);
8575                }
8576                if (mRecyclerView.mItemAnimator != null) {
8577                    mRecyclerView.mItemAnimator.endAnimation(vh);
8578                }
8579                vh.setIsRecyclable(true);
8580                recycler.quickRecycleScrapView(scrap);
8581            }
8582            recycler.clearScrap();
8583            if (scrapCount > 0) {
8584                mRecyclerView.invalidate();
8585            }
8586        }
8587
8588
8589        /**
8590         * Measure a child view using standard measurement policy, taking the padding
8591         * of the parent RecyclerView and any added item decorations into account.
8592         *
8593         * <p>If the RecyclerView can be scrolled in either dimension the caller may
8594         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8595         *
8596         * @param child Child view to measure
8597         * @param widthUsed Width in pixels currently consumed by other views, if relevant
8598         * @param heightUsed Height in pixels currently consumed by other views, if relevant
8599         */
8600        public void measureChild(View child, int widthUsed, int heightUsed) {
8601            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8602
8603            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8604            widthUsed += insets.left + insets.right;
8605            heightUsed += insets.top + insets.bottom;
8606            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8607                    getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
8608                    canScrollHorizontally());
8609            final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8610                    getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
8611                    canScrollVertically());
8612            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8613                child.measure(widthSpec, heightSpec);
8614            }
8615        }
8616
8617        /**
8618         * RecyclerView internally does its own View measurement caching which should help with
8619         * WRAP_CONTENT.
8620         * <p>
8621         * Use this method if the View is already measured once in this layout pass.
8622         */
8623        boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8624            return !mMeasurementCacheEnabled
8625                    || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
8626                    || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
8627        }
8628
8629        // we may consider making this public
8630        /**
8631         * RecyclerView internally does its own View measurement caching which should help with
8632         * WRAP_CONTENT.
8633         * <p>
8634         * Use this method if the View is not yet measured and you need to decide whether to
8635         * measure this View or not.
8636         */
8637        boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8638            return child.isLayoutRequested()
8639                    || !mMeasurementCacheEnabled
8640                    || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
8641                    || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
8642        }
8643
8644        /**
8645         * In addition to the View Framework's measurement cache, RecyclerView uses its own
8646         * additional measurement cache for its children to avoid re-measuring them when not
8647         * necessary. It is on by default but it can be turned off via
8648         * {@link #setMeasurementCacheEnabled(boolean)}.
8649         *
8650         * @return True if measurement cache is enabled, false otherwise.
8651         *
8652         * @see #setMeasurementCacheEnabled(boolean)
8653         */
8654        public boolean isMeasurementCacheEnabled() {
8655            return mMeasurementCacheEnabled;
8656        }
8657
8658        /**
8659         * Sets whether RecyclerView should use its own measurement cache for the children. This is
8660         * a more aggressive cache than the framework uses.
8661         *
8662         * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
8663         *
8664         * @see #isMeasurementCacheEnabled()
8665         */
8666        public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
8667            mMeasurementCacheEnabled = measurementCacheEnabled;
8668        }
8669
8670        private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
8671            final int specMode = MeasureSpec.getMode(spec);
8672            final int specSize = MeasureSpec.getSize(spec);
8673            if (dimension > 0 && childSize != dimension) {
8674                return false;
8675            }
8676            switch (specMode) {
8677                case MeasureSpec.UNSPECIFIED:
8678                    return true;
8679                case MeasureSpec.AT_MOST:
8680                    return specSize >= childSize;
8681                case MeasureSpec.EXACTLY:
8682                    return  specSize == childSize;
8683            }
8684            return false;
8685        }
8686
8687        /**
8688         * Measure a child view using standard measurement policy, taking the padding
8689         * of the parent RecyclerView, any added item decorations and the child margins
8690         * into account.
8691         *
8692         * <p>If the RecyclerView can be scrolled in either dimension the caller may
8693         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8694         *
8695         * @param child Child view to measure
8696         * @param widthUsed Width in pixels currently consumed by other views, if relevant
8697         * @param heightUsed Height in pixels currently consumed by other views, if relevant
8698         */
8699        public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
8700            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8701
8702            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8703            widthUsed += insets.left + insets.right;
8704            heightUsed += insets.top + insets.bottom;
8705
8706            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8707                    getPaddingLeft() + getPaddingRight()
8708                            + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
8709                    canScrollHorizontally());
8710            final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8711                    getPaddingTop() + getPaddingBottom()
8712                            + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
8713                    canScrollVertically());
8714            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8715                child.measure(widthSpec, heightSpec);
8716            }
8717        }
8718
8719        /**
8720         * Calculate a MeasureSpec value for measuring a child view in one dimension.
8721         *
8722         * @param parentSize Size of the parent view where the child will be placed
8723         * @param padding Total space currently consumed by other elements of the parent
8724         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
8725         *                       Generally obtained from the child view's LayoutParams
8726         * @param canScroll true if the parent RecyclerView can scroll in this dimension
8727         *
8728         * @return a MeasureSpec value for the child view
8729         * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
8730         */
8731        @Deprecated
8732        public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
8733                boolean canScroll) {
8734            int size = Math.max(0, parentSize - padding);
8735            int resultSize = 0;
8736            int resultMode = 0;
8737            if (canScroll) {
8738                if (childDimension >= 0) {
8739                    resultSize = childDimension;
8740                    resultMode = MeasureSpec.EXACTLY;
8741                } else {
8742                    // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
8743                    // instead using UNSPECIFIED.
8744                    resultSize = 0;
8745                    resultMode = MeasureSpec.UNSPECIFIED;
8746                }
8747            } else {
8748                if (childDimension >= 0) {
8749                    resultSize = childDimension;
8750                    resultMode = MeasureSpec.EXACTLY;
8751                } else if (childDimension == LayoutParams.MATCH_PARENT) {
8752                    resultSize = size;
8753                    // TODO this should be my spec.
8754                    resultMode = MeasureSpec.EXACTLY;
8755                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8756                    resultSize = size;
8757                    resultMode = MeasureSpec.AT_MOST;
8758                }
8759            }
8760            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
8761        }
8762
8763        /**
8764         * Calculate a MeasureSpec value for measuring a child view in one dimension.
8765         *
8766         * @param parentSize Size of the parent view where the child will be placed
8767         * @param parentMode The measurement spec mode of the parent
8768         * @param padding Total space currently consumed by other elements of parent
8769         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
8770         *                       Generally obtained from the child view's LayoutParams
8771         * @param canScroll true if the parent RecyclerView can scroll in this dimension
8772         *
8773         * @return a MeasureSpec value for the child view
8774         */
8775        public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
8776                int childDimension, boolean canScroll) {
8777            int size = Math.max(0, parentSize - padding);
8778            int resultSize = 0;
8779            int resultMode = 0;
8780            if (canScroll) {
8781                if (childDimension >= 0) {
8782                    resultSize = childDimension;
8783                    resultMode = MeasureSpec.EXACTLY;
8784                } else if (childDimension == LayoutParams.MATCH_PARENT) {
8785                    switch (parentMode) {
8786                        case MeasureSpec.AT_MOST:
8787                        case MeasureSpec.EXACTLY:
8788                            resultSize = size;
8789                            resultMode = parentMode;
8790                            break;
8791                        case MeasureSpec.UNSPECIFIED:
8792                            resultSize = 0;
8793                            resultMode = MeasureSpec.UNSPECIFIED;
8794                            break;
8795                    }
8796                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8797                    resultSize = 0;
8798                    resultMode = MeasureSpec.UNSPECIFIED;
8799                }
8800            } else {
8801                if (childDimension >= 0) {
8802                    resultSize = childDimension;
8803                    resultMode = MeasureSpec.EXACTLY;
8804                } else if (childDimension == LayoutParams.MATCH_PARENT) {
8805                    resultSize = size;
8806                    resultMode = parentMode;
8807                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8808                    resultSize = size;
8809                    if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
8810                        resultMode = MeasureSpec.AT_MOST;
8811                    } else {
8812                        resultMode = MeasureSpec.UNSPECIFIED;
8813                    }
8814
8815                }
8816            }
8817            //noinspection WrongConstant
8818            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
8819        }
8820
8821        /**
8822         * Returns the measured width of the given child, plus the additional size of
8823         * any insets applied by {@link ItemDecoration ItemDecorations}.
8824         *
8825         * @param child Child view to query
8826         * @return child's measured width plus <code>ItemDecoration</code> insets
8827         *
8828         * @see View#getMeasuredWidth()
8829         */
8830        public int getDecoratedMeasuredWidth(View child) {
8831            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8832            return child.getMeasuredWidth() + insets.left + insets.right;
8833        }
8834
8835        /**
8836         * Returns the measured height of the given child, plus the additional size of
8837         * any insets applied by {@link ItemDecoration ItemDecorations}.
8838         *
8839         * @param child Child view to query
8840         * @return child's measured height plus <code>ItemDecoration</code> insets
8841         *
8842         * @see View#getMeasuredHeight()
8843         */
8844        public int getDecoratedMeasuredHeight(View child) {
8845            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8846            return child.getMeasuredHeight() + insets.top + insets.bottom;
8847        }
8848
8849        /**
8850         * Lay out the given child view within the RecyclerView using coordinates that
8851         * include any current {@link ItemDecoration ItemDecorations}.
8852         *
8853         * <p>LayoutManagers should prefer working in sizes and coordinates that include
8854         * item decoration insets whenever possible. This allows the LayoutManager to effectively
8855         * ignore decoration insets within measurement and layout code. See the following
8856         * methods:</p>
8857         * <ul>
8858         *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
8859         *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
8860         *     <li>{@link #measureChild(View, int, int)}</li>
8861         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
8862         *     <li>{@link #getDecoratedLeft(View)}</li>
8863         *     <li>{@link #getDecoratedTop(View)}</li>
8864         *     <li>{@link #getDecoratedRight(View)}</li>
8865         *     <li>{@link #getDecoratedBottom(View)}</li>
8866         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
8867         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
8868         * </ul>
8869         *
8870         * @param child Child to lay out
8871         * @param left Left edge, with item decoration insets included
8872         * @param top Top edge, with item decoration insets included
8873         * @param right Right edge, with item decoration insets included
8874         * @param bottom Bottom edge, with item decoration insets included
8875         *
8876         * @see View#layout(int, int, int, int)
8877         * @see #layoutDecoratedWithMargins(View, int, int, int, int)
8878         */
8879        public void layoutDecorated(View child, int left, int top, int right, int bottom) {
8880            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8881            child.layout(left + insets.left, top + insets.top, right - insets.right,
8882                    bottom - insets.bottom);
8883        }
8884
8885        /**
8886         * Lay out the given child view within the RecyclerView using coordinates that
8887         * include any current {@link ItemDecoration ItemDecorations} and margins.
8888         *
8889         * <p>LayoutManagers should prefer working in sizes and coordinates that include
8890         * item decoration insets whenever possible. This allows the LayoutManager to effectively
8891         * ignore decoration insets within measurement and layout code. See the following
8892         * methods:</p>
8893         * <ul>
8894         *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
8895         *     <li>{@link #measureChild(View, int, int)}</li>
8896         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
8897         *     <li>{@link #getDecoratedLeft(View)}</li>
8898         *     <li>{@link #getDecoratedTop(View)}</li>
8899         *     <li>{@link #getDecoratedRight(View)}</li>
8900         *     <li>{@link #getDecoratedBottom(View)}</li>
8901         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
8902         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
8903         * </ul>
8904         *
8905         * @param child Child to lay out
8906         * @param left Left edge, with item decoration insets and left margin included
8907         * @param top Top edge, with item decoration insets and top margin included
8908         * @param right Right edge, with item decoration insets and right margin included
8909         * @param bottom Bottom edge, with item decoration insets and bottom margin included
8910         *
8911         * @see View#layout(int, int, int, int)
8912         * @see #layoutDecorated(View, int, int, int, int)
8913         */
8914        public void layoutDecoratedWithMargins(View child, int left, int top, int right,
8915                int bottom) {
8916            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8917            final Rect insets = lp.mDecorInsets;
8918            child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
8919                    right - insets.right - lp.rightMargin,
8920                    bottom - insets.bottom - lp.bottomMargin);
8921        }
8922
8923        /**
8924         * Calculates the bounding box of the View while taking into account its matrix changes
8925         * (translation, scale etc) with respect to the RecyclerView.
8926         * <p>
8927         * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
8928         * the View's matrix so that the decor offsets also go through the same transformation.
8929         *
8930         * @param child The ItemView whose bounding box should be calculated.
8931         * @param includeDecorInsets True if the decor insets should be included in the bounding box
8932         * @param out The rectangle into which the output will be written.
8933         */
8934        public void getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out) {
8935            if (includeDecorInsets) {
8936                Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8937                out.set(-insets.left, -insets.top,
8938                        child.getWidth() + insets.right, child.getHeight() + insets.bottom);
8939            } else {
8940                out.set(0, 0, child.getWidth(), child.getHeight());
8941            }
8942
8943            if (mRecyclerView != null) {
8944                final Matrix childMatrix = child.getMatrix();
8945                if (childMatrix != null && !childMatrix.isIdentity()) {
8946                    final RectF tempRectF = mRecyclerView.mTempRectF;
8947                    tempRectF.set(out);
8948                    childMatrix.mapRect(tempRectF);
8949                    out.set(
8950                            (int) Math.floor(tempRectF.left),
8951                            (int) Math.floor(tempRectF.top),
8952                            (int) Math.ceil(tempRectF.right),
8953                            (int) Math.ceil(tempRectF.bottom)
8954                    );
8955                }
8956            }
8957            out.offset(child.getLeft(), child.getTop());
8958        }
8959
8960        /**
8961         * Returns the bounds of the view including its decoration and margins.
8962         *
8963         * @param view The view element to check
8964         * @param outBounds A rect that will receive the bounds of the element including its
8965         *                  decoration and margins.
8966         */
8967        public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
8968            RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
8969        }
8970
8971        /**
8972         * Returns the left edge of the given child view within its parent, offset by any applied
8973         * {@link ItemDecoration ItemDecorations}.
8974         *
8975         * @param child Child to query
8976         * @return Child left edge with offsets applied
8977         * @see #getLeftDecorationWidth(View)
8978         */
8979        public int getDecoratedLeft(View child) {
8980            return child.getLeft() - getLeftDecorationWidth(child);
8981        }
8982
8983        /**
8984         * Returns the top edge of the given child view within its parent, offset by any applied
8985         * {@link ItemDecoration ItemDecorations}.
8986         *
8987         * @param child Child to query
8988         * @return Child top edge with offsets applied
8989         * @see #getTopDecorationHeight(View)
8990         */
8991        public int getDecoratedTop(View child) {
8992            return child.getTop() - getTopDecorationHeight(child);
8993        }
8994
8995        /**
8996         * Returns the right edge of the given child view within its parent, offset by any applied
8997         * {@link ItemDecoration ItemDecorations}.
8998         *
8999         * @param child Child to query
9000         * @return Child right edge with offsets applied
9001         * @see #getRightDecorationWidth(View)
9002         */
9003        public int getDecoratedRight(View child) {
9004            return child.getRight() + getRightDecorationWidth(child);
9005        }
9006
9007        /**
9008         * Returns the bottom edge of the given child view within its parent, offset by any applied
9009         * {@link ItemDecoration ItemDecorations}.
9010         *
9011         * @param child Child to query
9012         * @return Child bottom edge with offsets applied
9013         * @see #getBottomDecorationHeight(View)
9014         */
9015        public int getDecoratedBottom(View child) {
9016            return child.getBottom() + getBottomDecorationHeight(child);
9017        }
9018
9019        /**
9020         * Calculates the item decor insets applied to the given child and updates the provided
9021         * Rect instance with the inset values.
9022         * <ul>
9023         *     <li>The Rect's left is set to the total width of left decorations.</li>
9024         *     <li>The Rect's top is set to the total height of top decorations.</li>
9025         *     <li>The Rect's right is set to the total width of right decorations.</li>
9026         *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
9027         * </ul>
9028         * <p>
9029         * Note that item decorations are automatically calculated when one of the LayoutManager's
9030         * measure child methods is called. If you need to measure the child with custom specs via
9031         * {@link View#measure(int, int)}, you can use this method to get decorations.
9032         *
9033         * @param child The child view whose decorations should be calculated
9034         * @param outRect The Rect to hold result values
9035         */
9036        public void calculateItemDecorationsForChild(View child, Rect outRect) {
9037            if (mRecyclerView == null) {
9038                outRect.set(0, 0, 0, 0);
9039                return;
9040            }
9041            Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
9042            outRect.set(insets);
9043        }
9044
9045        /**
9046         * Returns the total height of item decorations applied to child's top.
9047         * <p>
9048         * Note that this value is not updated until the View is measured or
9049         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9050         *
9051         * @param child Child to query
9052         * @return The total height of item decorations applied to the child's top.
9053         * @see #getDecoratedTop(View)
9054         * @see #calculateItemDecorationsForChild(View, Rect)
9055         */
9056        public int getTopDecorationHeight(View child) {
9057            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
9058        }
9059
9060        /**
9061         * Returns the total height of item decorations applied to child's bottom.
9062         * <p>
9063         * Note that this value is not updated until the View is measured or
9064         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9065         *
9066         * @param child Child to query
9067         * @return The total height of item decorations applied to the child's bottom.
9068         * @see #getDecoratedBottom(View)
9069         * @see #calculateItemDecorationsForChild(View, Rect)
9070         */
9071        public int getBottomDecorationHeight(View child) {
9072            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
9073        }
9074
9075        /**
9076         * Returns the total width of item decorations applied to child's left.
9077         * <p>
9078         * Note that this value is not updated until the View is measured or
9079         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9080         *
9081         * @param child Child to query
9082         * @return The total width of item decorations applied to the child's left.
9083         * @see #getDecoratedLeft(View)
9084         * @see #calculateItemDecorationsForChild(View, Rect)
9085         */
9086        public int getLeftDecorationWidth(View child) {
9087            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
9088        }
9089
9090        /**
9091         * Returns the total width of item decorations applied to child's right.
9092         * <p>
9093         * Note that this value is not updated until the View is measured or
9094         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
9095         *
9096         * @param child Child to query
9097         * @return The total width of item decorations applied to the child's right.
9098         * @see #getDecoratedRight(View)
9099         * @see #calculateItemDecorationsForChild(View, Rect)
9100         */
9101        public int getRightDecorationWidth(View child) {
9102            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
9103        }
9104
9105        /**
9106         * Called when searching for a focusable view in the given direction has failed
9107         * for the current content of the RecyclerView.
9108         *
9109         * <p>This is the LayoutManager's opportunity to populate views in the given direction
9110         * to fulfill the request if it can. The LayoutManager should attach and return
9111         * the view to be focused, if a focusable view in the given direction is found.
9112         * Otherwise, if all the existing (or the newly populated views) are unfocusable, it returns
9113         * the next unfocusable view to become visible on the screen. This unfocusable view is
9114         * typically the first view that's either partially or fully out of RV's padded bounded
9115         * area in the given direction. The default implementation returns null.</p>
9116         *
9117         * @param focused   The currently focused view
9118         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9119         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9120         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9121         *                  or 0 for not applicable
9122         * @param recycler  The recycler to use for obtaining views for currently offscreen items
9123         * @param state     Transient state of RecyclerView
9124         * @return The chosen view to be focused if a focusable view is found, otherwise an
9125         * unfocusable view to become visible onto the screen, else null.
9126         */
9127        @Nullable
9128        public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
9129                State state) {
9130            return null;
9131        }
9132
9133        /**
9134         * This method gives a LayoutManager an opportunity to intercept the initial focus search
9135         * before the default behavior of {@link FocusFinder} is used. If this method returns
9136         * null FocusFinder will attempt to find a focusable child view. If it fails
9137         * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
9138         * will be called to give the LayoutManager an opportunity to add new views for items
9139         * that did not have attached views representing them. The LayoutManager should not add
9140         * or remove views from this method.
9141         *
9142         * @param focused The currently focused view
9143         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9144         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9145         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9146         * @return A descendant view to focus or null to fall back to default behavior.
9147         *         The default implementation returns null.
9148         */
9149        public View onInterceptFocusSearch(View focused, int direction) {
9150            return null;
9151        }
9152
9153        /**
9154         * Returns the scroll amount that brings the given rect in child's coordinate system within
9155         * the padded area of RecyclerView.
9156         * @param parent The parent RecyclerView.
9157         * @param child The direct child making the request.
9158         * @param rect The rectangle in the child's coordinates the child
9159         *             wishes to be on the screen.
9160         * @param immediate True to forbid animated or delayed scrolling,
9161         *                  false otherwise
9162         * @return The array containing the scroll amount in x and y directions that brings the
9163         * given rect into RV's padded area.
9164         */
9165        private int[] getChildRectangleOnScreenScrollAmount(RecyclerView parent, View child,
9166                Rect rect, boolean immediate) {
9167            int[] out = new int[2];
9168            final int parentLeft = getPaddingLeft();
9169            final int parentTop = getPaddingTop();
9170            final int parentRight = getWidth() - getPaddingRight();
9171            final int parentBottom = getHeight() - getPaddingBottom();
9172            final int childLeft = child.getLeft() + rect.left - child.getScrollX();
9173            final int childTop = child.getTop() + rect.top - child.getScrollY();
9174            final int childRight = childLeft + rect.width();
9175            final int childBottom = childTop + rect.height();
9176
9177            final int offScreenLeft = Math.min(0, childLeft - parentLeft);
9178            final int offScreenTop = Math.min(0, childTop - parentTop);
9179            final int offScreenRight = Math.max(0, childRight - parentRight);
9180            final int offScreenBottom = Math.max(0, childBottom - parentBottom);
9181
9182            // Favor the "start" layout direction over the end when bringing one side or the other
9183            // of a large rect into view. If we decide to bring in end because start is already
9184            // visible, limit the scroll such that start won't go out of bounds.
9185            final int dx;
9186            if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) {
9187                dx = offScreenRight != 0 ? offScreenRight
9188                        : Math.max(offScreenLeft, childRight - parentRight);
9189            } else {
9190                dx = offScreenLeft != 0 ? offScreenLeft
9191                        : Math.min(childLeft - parentLeft, offScreenRight);
9192            }
9193
9194            // Favor bringing the top into view over the bottom. If top is already visible and
9195            // we should scroll to make bottom visible, make sure top does not go out of bounds.
9196            final int dy = offScreenTop != 0 ? offScreenTop
9197                    : Math.min(childTop - parentTop, offScreenBottom);
9198            out[0] = dx;
9199            out[1] = dy;
9200            return out;
9201        }
9202        /**
9203         * Called when a child of the RecyclerView wants a particular rectangle to be positioned
9204         * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
9205         * android.graphics.Rect, boolean)} for more details.
9206         *
9207         * <p>The base implementation will attempt to perform a standard programmatic scroll
9208         * to bring the given rect into view, within the padded area of the RecyclerView.</p>
9209         *
9210         * @param child The direct child making the request.
9211         * @param rect  The rectangle in the child's coordinates the child
9212         *              wishes to be on the screen.
9213         * @param immediate True to forbid animated or delayed scrolling,
9214         *                  false otherwise
9215         * @return Whether the group scrolled to handle the operation
9216         */
9217        public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
9218                boolean immediate) {
9219            return requestChildRectangleOnScreen(parent, child, rect, immediate, false);
9220        }
9221
9222        /**
9223         * Requests that the given child of the RecyclerView be positioned onto the screen. This
9224         * method can be called for both unfocusable and focusable child views. For unfocusable
9225         * child views, focusedChildVisible is typically true in which case, layout manager
9226         * makes the child view visible only if the currently focused child stays in-bounds of RV.
9227         * @param parent The parent RecyclerView.
9228         * @param child The direct child making the request.
9229         * @param rect The rectangle in the child's coordinates the child
9230         *              wishes to be on the screen.
9231         * @param immediate True to forbid animated or delayed scrolling,
9232         *                  false otherwise
9233         * @param focusedChildVisible Whether the currently focused view must stay visible.
9234         * @return Whether the group scrolled to handle the operation
9235         */
9236        public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
9237                boolean immediate,
9238                boolean focusedChildVisible) {
9239            int[] scrollAmount = getChildRectangleOnScreenScrollAmount(parent, child, rect,
9240                    immediate);
9241            int dx = scrollAmount[0];
9242            int dy = scrollAmount[1];
9243            if (!focusedChildVisible || isFocusedChildVisibleAfterScrolling(parent, dx, dy)) {
9244                if (dx != 0 || dy != 0) {
9245                    if (immediate) {
9246                        parent.scrollBy(dx, dy);
9247                    } else {
9248                        parent.smoothScrollBy(dx, dy);
9249                    }
9250                    return true;
9251                }
9252            }
9253            return false;
9254        }
9255
9256        /**
9257         * Returns whether the given child view is partially or fully visible within the padded
9258         * bounded area of RecyclerView, depending on the input parameters.
9259         * A view is partially visible if it has non-zero overlap with RV's padded bounded area.
9260         * If acceptEndPointInclusion flag is set to true, it's also considered partially
9261         * visible if it's located outside RV's bounds and it's hitting either RV's start or end
9262         * bounds.
9263         *
9264         * @param child The child view to be examined.
9265         * @param completelyVisible If true, the method returns true if and only if the child is
9266         *                          completely visible. If false, the method returns true if and
9267         *                          only if the child is only partially visible (that is it will
9268         *                          return false if the child is either completely visible or out
9269         *                          of RV's bounds).
9270         * @param acceptEndPointInclusion If the view's endpoint intersection with RV's start of end
9271         *                                bounds is enough to consider it partially visible,
9272         *                                false otherwise.
9273         * @return True if the given child is partially or fully visible, false otherwise.
9274         */
9275        public boolean isViewPartiallyVisible(@NonNull View child, boolean completelyVisible,
9276                boolean acceptEndPointInclusion) {
9277            int boundsFlag = (ViewBoundsCheck.FLAG_CVS_GT_PVS | ViewBoundsCheck.FLAG_CVS_EQ_PVS
9278                    | ViewBoundsCheck.FLAG_CVE_LT_PVE | ViewBoundsCheck.FLAG_CVE_EQ_PVE);
9279            boolean isViewFullyVisible = mHorizontalBoundCheck.isViewWithinBoundFlags(child,
9280                    boundsFlag)
9281                    && mVerticalBoundCheck.isViewWithinBoundFlags(child, boundsFlag);
9282            if (completelyVisible) {
9283                return isViewFullyVisible;
9284            } else {
9285                return !isViewFullyVisible;
9286            }
9287        }
9288
9289        /**
9290         * Returns whether the currently focused child stays within RV's bounds with the given
9291         * amount of scrolling.
9292         * @param parent The parent RecyclerView.
9293         * @param dx The scrolling in x-axis direction to be performed.
9294         * @param dy The scrolling in y-axis direction to be performed.
9295         * @return {@code false} if the focused child is not at least partially visible after
9296         *         scrolling or no focused child exists, {@code true} otherwise.
9297         */
9298        private boolean isFocusedChildVisibleAfterScrolling(RecyclerView parent, int dx, int dy) {
9299            final View focusedChild = parent.getFocusedChild();
9300            if (focusedChild == null) {
9301                return false;
9302            }
9303            final int parentLeft = getPaddingLeft();
9304            final int parentTop = getPaddingTop();
9305            final int parentRight = getWidth() - getPaddingRight();
9306            final int parentBottom = getHeight() - getPaddingBottom();
9307            final Rect bounds = mRecyclerView.mTempRect;
9308            getDecoratedBoundsWithMargins(focusedChild, bounds);
9309
9310            if (bounds.left - dx >= parentRight || bounds.right - dx <= parentLeft
9311                    || bounds.top - dy >= parentBottom || bounds.bottom - dy <= parentTop) {
9312                return false;
9313            }
9314            return true;
9315        }
9316
9317        /**
9318         * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
9319         */
9320        @Deprecated
9321        public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
9322            // eat the request if we are in the middle of a scroll or layout
9323            return isSmoothScrolling() || parent.isComputingLayout();
9324        }
9325
9326        /**
9327         * Called when a descendant view of the RecyclerView requests focus.
9328         *
9329         * <p>A LayoutManager wishing to keep focused views aligned in a specific
9330         * portion of the view may implement that behavior in an override of this method.</p>
9331         *
9332         * <p>If the LayoutManager executes different behavior that should override the default
9333         * behavior of scrolling the focused child on screen instead of running alongside it,
9334         * this method should return true.</p>
9335         *
9336         * @param parent  The RecyclerView hosting this LayoutManager
9337         * @param state   Current state of RecyclerView
9338         * @param child   Direct child of the RecyclerView containing the newly focused view
9339         * @param focused The newly focused view. This may be the same view as child or it may be
9340         *                null
9341         * @return true if the default scroll behavior should be suppressed
9342         */
9343        public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
9344                View focused) {
9345            return onRequestChildFocus(parent, child, focused);
9346        }
9347
9348        /**
9349         * Called if the RecyclerView this LayoutManager is bound to has a different adapter set.
9350         * The LayoutManager may use this opportunity to clear caches and configure state such
9351         * that it can relayout appropriately with the new data and potentially new view types.
9352         *
9353         * <p>The default implementation removes all currently attached views.</p>
9354         *
9355         * @param oldAdapter The previous adapter instance. Will be null if there was previously no
9356         *                   adapter.
9357         * @param newAdapter The new adapter instance. Might be null if
9358         *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
9359         */
9360        public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
9361        }
9362
9363        /**
9364         * Called to populate focusable views within the RecyclerView.
9365         *
9366         * <p>The LayoutManager implementation should return <code>true</code> if the default
9367         * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
9368         * suppressed.</p>
9369         *
9370         * <p>The default implementation returns <code>false</code> to trigger RecyclerView
9371         * to fall back to the default ViewGroup behavior.</p>
9372         *
9373         * @param recyclerView The RecyclerView hosting this LayoutManager
9374         * @param views List of output views. This method should add valid focusable views
9375         *              to this list.
9376         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9377         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9378         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9379         * @param focusableMode The type of focusables to be added.
9380         *
9381         * @return true to suppress the default behavior, false to add default focusables after
9382         *         this method returns.
9383         *
9384         * @see #FOCUSABLES_ALL
9385         * @see #FOCUSABLES_TOUCH_MODE
9386         */
9387        public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,
9388                int direction, int focusableMode) {
9389            return false;
9390        }
9391
9392        /**
9393         * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving
9394         * detailed information on what has actually changed.
9395         *
9396         * @param recyclerView
9397         */
9398        public void onItemsChanged(RecyclerView recyclerView) {
9399        }
9400
9401        /**
9402         * Called when items have been added to the adapter. The LayoutManager may choose to
9403         * requestLayout if the inserted items would require refreshing the currently visible set
9404         * of child views. (e.g. currently empty space would be filled by appended items, etc.)
9405         *
9406         * @param recyclerView
9407         * @param positionStart
9408         * @param itemCount
9409         */
9410        public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
9411        }
9412
9413        /**
9414         * Called when items have been removed from the adapter.
9415         *
9416         * @param recyclerView
9417         * @param positionStart
9418         * @param itemCount
9419         */
9420        public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
9421        }
9422
9423        /**
9424         * Called when items have been changed in the adapter.
9425         * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
9426         * instead, then this callback will not be invoked.
9427         *
9428         * @param recyclerView
9429         * @param positionStart
9430         * @param itemCount
9431         */
9432        public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
9433        }
9434
9435        /**
9436         * Called when items have been changed in the adapter and with optional payload.
9437         * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
9438         *
9439         * @param recyclerView
9440         * @param positionStart
9441         * @param itemCount
9442         * @param payload
9443         */
9444        public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
9445                Object payload) {
9446            onItemsUpdated(recyclerView, positionStart, itemCount);
9447        }
9448
9449        /**
9450         * Called when an item is moved withing the adapter.
9451         * <p>
9452         * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
9453         * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
9454         * is called.
9455         *
9456         * @param recyclerView
9457         * @param from
9458         * @param to
9459         * @param itemCount
9460         */
9461        public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
9462
9463        }
9464
9465
9466        /**
9467         * <p>Override this method if you want to support scroll bars.</p>
9468         *
9469         * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
9470         *
9471         * <p>Default implementation returns 0.</p>
9472         *
9473         * @param state Current state of RecyclerView
9474         * @return The horizontal extent of the scrollbar's thumb
9475         * @see RecyclerView#computeHorizontalScrollExtent()
9476         */
9477        public int computeHorizontalScrollExtent(State state) {
9478            return 0;
9479        }
9480
9481        /**
9482         * <p>Override this method if you want to support scroll bars.</p>
9483         *
9484         * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
9485         *
9486         * <p>Default implementation returns 0.</p>
9487         *
9488         * @param state Current State of RecyclerView where you can find total item count
9489         * @return The horizontal offset of the scrollbar's thumb
9490         * @see RecyclerView#computeHorizontalScrollOffset()
9491         */
9492        public int computeHorizontalScrollOffset(State state) {
9493            return 0;
9494        }
9495
9496        /**
9497         * <p>Override this method if you want to support scroll bars.</p>
9498         *
9499         * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
9500         *
9501         * <p>Default implementation returns 0.</p>
9502         *
9503         * @param state Current State of RecyclerView where you can find total item count
9504         * @return The total horizontal range represented by the vertical scrollbar
9505         * @see RecyclerView#computeHorizontalScrollRange()
9506         */
9507        public int computeHorizontalScrollRange(State state) {
9508            return 0;
9509        }
9510
9511        /**
9512         * <p>Override this method if you want to support scroll bars.</p>
9513         *
9514         * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
9515         *
9516         * <p>Default implementation returns 0.</p>
9517         *
9518         * @param state Current state of RecyclerView
9519         * @return The vertical extent of the scrollbar's thumb
9520         * @see RecyclerView#computeVerticalScrollExtent()
9521         */
9522        public int computeVerticalScrollExtent(State state) {
9523            return 0;
9524        }
9525
9526        /**
9527         * <p>Override this method if you want to support scroll bars.</p>
9528         *
9529         * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
9530         *
9531         * <p>Default implementation returns 0.</p>
9532         *
9533         * @param state Current State of RecyclerView where you can find total item count
9534         * @return The vertical offset of the scrollbar's thumb
9535         * @see RecyclerView#computeVerticalScrollOffset()
9536         */
9537        public int computeVerticalScrollOffset(State state) {
9538            return 0;
9539        }
9540
9541        /**
9542         * <p>Override this method if you want to support scroll bars.</p>
9543         *
9544         * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
9545         *
9546         * <p>Default implementation returns 0.</p>
9547         *
9548         * @param state Current State of RecyclerView where you can find total item count
9549         * @return The total vertical range represented by the vertical scrollbar
9550         * @see RecyclerView#computeVerticalScrollRange()
9551         */
9552        public int computeVerticalScrollRange(State state) {
9553            return 0;
9554        }
9555
9556        /**
9557         * Measure the attached RecyclerView. Implementations must call
9558         * {@link #setMeasuredDimension(int, int)} before returning.
9559         *
9560         * <p>The default implementation will handle EXACTLY measurements and respect
9561         * the minimum width and height properties of the host RecyclerView if measured
9562         * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
9563         * will consume all available space.</p>
9564         *
9565         * @param recycler Recycler
9566         * @param state Transient state of RecyclerView
9567         * @param widthSpec Width {@link android.view.View.MeasureSpec}
9568         * @param heightSpec Height {@link android.view.View.MeasureSpec}
9569         */
9570        public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
9571            mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
9572        }
9573
9574        /**
9575         * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
9576         * host RecyclerView.
9577         *
9578         * @param widthSize Measured width
9579         * @param heightSize Measured height
9580         */
9581        public void setMeasuredDimension(int widthSize, int heightSize) {
9582            mRecyclerView.setMeasuredDimension(widthSize, heightSize);
9583        }
9584
9585        /**
9586         * @return The host RecyclerView's {@link View#getMinimumWidth()}
9587         */
9588        public int getMinimumWidth() {
9589            return ViewCompat.getMinimumWidth(mRecyclerView);
9590        }
9591
9592        /**
9593         * @return The host RecyclerView's {@link View#getMinimumHeight()}
9594         */
9595        public int getMinimumHeight() {
9596            return ViewCompat.getMinimumHeight(mRecyclerView);
9597        }
9598        /**
9599         * <p>Called when the LayoutManager should save its state. This is a good time to save your
9600         * scroll position, configuration and anything else that may be required to restore the same
9601         * layout state if the LayoutManager is recreated.</p>
9602         * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
9603         * restore. This will let you share information between your LayoutManagers but it is also
9604         * your responsibility to make sure they use the same parcelable class.</p>
9605         *
9606         * @return Necessary information for LayoutManager to be able to restore its state
9607         */
9608        public Parcelable onSaveInstanceState() {
9609            return null;
9610        }
9611
9612
9613        public void onRestoreInstanceState(Parcelable state) {
9614
9615        }
9616
9617        void stopSmoothScroller() {
9618            if (mSmoothScroller != null) {
9619                mSmoothScroller.stop();
9620            }
9621        }
9622
9623        private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
9624            if (mSmoothScroller == smoothScroller) {
9625                mSmoothScroller = null;
9626            }
9627        }
9628
9629        /**
9630         * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
9631         *
9632         * @param state The new scroll state for RecyclerView
9633         */
9634        public void onScrollStateChanged(int state) {
9635        }
9636
9637        /**
9638         * Removes all views and recycles them using the given recycler.
9639         * <p>
9640         * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
9641         * <p>
9642         * If a View is marked as "ignored", it is not removed nor recycled.
9643         *
9644         * @param recycler Recycler to use to recycle children
9645         * @see #removeAndRecycleView(View, Recycler)
9646         * @see #removeAndRecycleViewAt(int, Recycler)
9647         * @see #ignoreView(View)
9648         */
9649        public void removeAndRecycleAllViews(Recycler recycler) {
9650            for (int i = getChildCount() - 1; i >= 0; i--) {
9651                final View view = getChildAt(i);
9652                if (!getChildViewHolderInt(view).shouldIgnore()) {
9653                    removeAndRecycleViewAt(i, recycler);
9654                }
9655            }
9656        }
9657
9658        // called by accessibility delegate
9659        void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
9660            onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
9661        }
9662
9663        /**
9664         * Called by the AccessibilityDelegate when the information about the current layout should
9665         * be populated.
9666         * <p>
9667         * Default implementation adds a {@link
9668         * android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}.
9669         * <p>
9670         * You should override
9671         * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
9672         * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
9673         * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
9674         * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
9675         * more accurate accessibility information.
9676         *
9677         * @param recycler The Recycler that can be used to convert view positions into adapter
9678         *                 positions
9679         * @param state    The current state of RecyclerView
9680         * @param info     The info that should be filled by the LayoutManager
9681         * @see View#onInitializeAccessibilityNodeInfo(
9682         *android.view.accessibility.AccessibilityNodeInfo)
9683         * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9684         * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9685         * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
9686         * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9687         */
9688        public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
9689                AccessibilityNodeInfoCompat info) {
9690            if (mRecyclerView.canScrollVertically(-1) || mRecyclerView.canScrollHorizontally(-1)) {
9691                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
9692                info.setScrollable(true);
9693            }
9694            if (mRecyclerView.canScrollVertically(1) || mRecyclerView.canScrollHorizontally(1)) {
9695                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
9696                info.setScrollable(true);
9697            }
9698            final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo =
9699                    AccessibilityNodeInfoCompat.CollectionInfoCompat
9700                            .obtain(getRowCountForAccessibility(recycler, state),
9701                                    getColumnCountForAccessibility(recycler, state),
9702                                    isLayoutHierarchical(recycler, state),
9703                                    getSelectionModeForAccessibility(recycler, state));
9704            info.setCollectionInfo(collectionInfo);
9705        }
9706
9707        // called by accessibility delegate
9708        public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
9709            onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
9710        }
9711
9712        /**
9713         * Called by the accessibility delegate to initialize an accessibility event.
9714         * <p>
9715         * Default implementation adds item count and scroll information to the event.
9716         *
9717         * @param recycler The Recycler that can be used to convert view positions into adapter
9718         *                 positions
9719         * @param state    The current state of RecyclerView
9720         * @param event    The event instance to initialize
9721         * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
9722         */
9723        public void onInitializeAccessibilityEvent(Recycler recycler, State state,
9724                AccessibilityEvent event) {
9725            if (mRecyclerView == null || event == null) {
9726                return;
9727            }
9728            event.setScrollable(mRecyclerView.canScrollVertically(1)
9729                    || mRecyclerView.canScrollVertically(-1)
9730                    || mRecyclerView.canScrollHorizontally(-1)
9731                    || mRecyclerView.canScrollHorizontally(1));
9732
9733            if (mRecyclerView.mAdapter != null) {
9734                event.setItemCount(mRecyclerView.mAdapter.getItemCount());
9735            }
9736        }
9737
9738        // called by accessibility delegate
9739        void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) {
9740            final ViewHolder vh = getChildViewHolderInt(host);
9741            // avoid trying to create accessibility node info for removed children
9742            if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
9743                onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
9744                        mRecyclerView.mState, host, info);
9745            }
9746        }
9747
9748        /**
9749         * Called by the AccessibilityDelegate when the accessibility information for a specific
9750         * item should be populated.
9751         * <p>
9752         * Default implementation adds basic positioning information about the item.
9753         *
9754         * @param recycler The Recycler that can be used to convert view positions into adapter
9755         *                 positions
9756         * @param state    The current state of RecyclerView
9757         * @param host     The child for which accessibility node info should be populated
9758         * @param info     The info to fill out about the item
9759         * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
9760         * android.view.accessibility.AccessibilityNodeInfo)
9761         */
9762        public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
9763                View host, AccessibilityNodeInfoCompat info) {
9764            int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
9765            int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
9766            final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo =
9767                    AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
9768                            columnIndexGuess, 1, false, false);
9769            info.setCollectionItemInfo(itemInfo);
9770        }
9771
9772        /**
9773         * A LayoutManager can call this method to force RecyclerView to run simple animations in
9774         * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
9775         * change).
9776         * <p>
9777         * Note that, calling this method will not guarantee that RecyclerView will run animations
9778         * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
9779         * not run any animations but will still clear this flag after the layout is complete.
9780         *
9781         */
9782        public void requestSimpleAnimationsInNextLayout() {
9783            mRequestedSimpleAnimations = true;
9784        }
9785
9786        /**
9787         * Returns the selection mode for accessibility. Should be
9788         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE},
9789         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or
9790         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}.
9791         * <p>
9792         * Default implementation returns
9793         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
9794         *
9795         * @param recycler The Recycler that can be used to convert view positions into adapter
9796         *                 positions
9797         * @param state    The current state of RecyclerView
9798         * @return Selection mode for accessibility. Default implementation returns
9799         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
9800         */
9801        public int getSelectionModeForAccessibility(Recycler recycler, State state) {
9802            return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
9803        }
9804
9805        /**
9806         * Returns the number of rows for accessibility.
9807         * <p>
9808         * Default implementation returns the number of items in the adapter if LayoutManager
9809         * supports vertical scrolling or 1 if LayoutManager does not support vertical
9810         * scrolling.
9811         *
9812         * @param recycler The Recycler that can be used to convert view positions into adapter
9813         *                 positions
9814         * @param state    The current state of RecyclerView
9815         * @return The number of rows in LayoutManager for accessibility.
9816         */
9817        public int getRowCountForAccessibility(Recycler recycler, State state) {
9818            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
9819                return 1;
9820            }
9821            return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
9822        }
9823
9824        /**
9825         * Returns the number of columns for accessibility.
9826         * <p>
9827         * Default implementation returns the number of items in the adapter if LayoutManager
9828         * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
9829         * scrolling.
9830         *
9831         * @param recycler The Recycler that can be used to convert view positions into adapter
9832         *                 positions
9833         * @param state    The current state of RecyclerView
9834         * @return The number of rows in LayoutManager for accessibility.
9835         */
9836        public int getColumnCountForAccessibility(Recycler recycler, State state) {
9837            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
9838                return 1;
9839            }
9840            return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
9841        }
9842
9843        /**
9844         * Returns whether layout is hierarchical or not to be used for accessibility.
9845         * <p>
9846         * Default implementation returns false.
9847         *
9848         * @param recycler The Recycler that can be used to convert view positions into adapter
9849         *                 positions
9850         * @param state    The current state of RecyclerView
9851         * @return True if layout is hierarchical.
9852         */
9853        public boolean isLayoutHierarchical(Recycler recycler, State state) {
9854            return false;
9855        }
9856
9857        // called by accessibility delegate
9858        boolean performAccessibilityAction(int action, Bundle args) {
9859            return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
9860                    action, args);
9861        }
9862
9863        /**
9864         * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
9865         *
9866         * @param recycler  The Recycler that can be used to convert view positions into adapter
9867         *                  positions
9868         * @param state     The current state of RecyclerView
9869         * @param action    The action to perform
9870         * @param args      Optional action arguments
9871         * @see View#performAccessibilityAction(int, android.os.Bundle)
9872         */
9873        public boolean performAccessibilityAction(Recycler recycler, State state, int action,
9874                Bundle args) {
9875            if (mRecyclerView == null) {
9876                return false;
9877            }
9878            int vScroll = 0, hScroll = 0;
9879            switch (action) {
9880                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
9881                    if (mRecyclerView.canScrollVertically(-1)) {
9882                        vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
9883                    }
9884                    if (mRecyclerView.canScrollHorizontally(-1)) {
9885                        hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
9886                    }
9887                    break;
9888                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
9889                    if (mRecyclerView.canScrollVertically(1)) {
9890                        vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
9891                    }
9892                    if (mRecyclerView.canScrollHorizontally(1)) {
9893                        hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
9894                    }
9895                    break;
9896            }
9897            if (vScroll == 0 && hScroll == 0) {
9898                return false;
9899            }
9900            mRecyclerView.scrollBy(hScroll, vScroll);
9901            return true;
9902        }
9903
9904        // called by accessibility delegate
9905        boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
9906            return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
9907                    view, action, args);
9908        }
9909
9910        /**
9911         * Called by AccessibilityDelegate when an accessibility action is requested on one of the
9912         * children of LayoutManager.
9913         * <p>
9914         * Default implementation does not do anything.
9915         *
9916         * @param recycler The Recycler that can be used to convert view positions into adapter
9917         *                 positions
9918         * @param state    The current state of RecyclerView
9919         * @param view     The child view on which the action is performed
9920         * @param action   The action to perform
9921         * @param args     Optional action arguments
9922         * @return true if action is handled
9923         * @see View#performAccessibilityAction(int, android.os.Bundle)
9924         */
9925        public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
9926                int action, Bundle args) {
9927            return false;
9928        }
9929
9930        /**
9931         * Parse the xml attributes to get the most common properties used by layout managers.
9932         *
9933         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
9934         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
9935         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
9936         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
9937         *
9938         * @return an object containing the properties as specified in the attrs.
9939         */
9940        public static Properties getProperties(Context context, AttributeSet attrs,
9941                int defStyleAttr, int defStyleRes) {
9942            Properties properties = new Properties();
9943            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
9944                    defStyleAttr, defStyleRes);
9945            properties.orientation = a.getInt(R.styleable.RecyclerView_android_orientation,
9946                    VERTICAL);
9947            properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
9948            properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
9949            properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
9950            a.recycle();
9951            return properties;
9952        }
9953
9954        void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
9955            setMeasureSpecs(
9956                    MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
9957                    MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
9958            );
9959        }
9960
9961        /**
9962         * Internal API to allow LayoutManagers to be measured twice.
9963         * <p>
9964         * This is not public because LayoutManagers should be able to handle their layouts in one
9965         * pass but it is very convenient to make existing LayoutManagers support wrapping content
9966         * when both orientations are undefined.
9967         * <p>
9968         * This API will be removed after default LayoutManagers properly implement wrap content in
9969         * non-scroll orientation.
9970         */
9971        boolean shouldMeasureTwice() {
9972            return false;
9973        }
9974
9975        boolean hasFlexibleChildInBothOrientations() {
9976            final int childCount = getChildCount();
9977            for (int i = 0; i < childCount; i++) {
9978                final View child = getChildAt(i);
9979                final ViewGroup.LayoutParams lp = child.getLayoutParams();
9980                if (lp.width < 0 && lp.height < 0) {
9981                    return true;
9982                }
9983            }
9984            return false;
9985        }
9986
9987        /**
9988         * Some general properties that a LayoutManager may want to use.
9989         */
9990        public static class Properties {
9991            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation */
9992            public int orientation;
9993            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount */
9994            public int spanCount;
9995            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout */
9996            public boolean reverseLayout;
9997            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd */
9998            public boolean stackFromEnd;
9999        }
10000    }
10001
10002    /**
10003     * An ItemDecoration allows the application to add a special drawing and layout offset
10004     * to specific item views from the adapter's data set. This can be useful for drawing dividers
10005     * between items, highlights, visual grouping boundaries and more.
10006     *
10007     * <p>All ItemDecorations are drawn in the order they were added, before the item
10008     * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
10009     * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
10010     * RecyclerView.State)}.</p>
10011     */
10012    public abstract static class ItemDecoration {
10013        /**
10014         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
10015         * Any content drawn by this method will be drawn before the item views are drawn,
10016         * and will thus appear underneath the views.
10017         *
10018         * @param c Canvas to draw into
10019         * @param parent RecyclerView this ItemDecoration is drawing into
10020         * @param state The current state of RecyclerView
10021         */
10022        public void onDraw(Canvas c, RecyclerView parent, State state) {
10023            onDraw(c, parent);
10024        }
10025
10026        /**
10027         * @deprecated
10028         * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
10029         */
10030        @Deprecated
10031        public void onDraw(Canvas c, RecyclerView parent) {
10032        }
10033
10034        /**
10035         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
10036         * Any content drawn by this method will be drawn after the item views are drawn
10037         * and will thus appear over the views.
10038         *
10039         * @param c Canvas to draw into
10040         * @param parent RecyclerView this ItemDecoration is drawing into
10041         * @param state The current state of RecyclerView.
10042         */
10043        public void onDrawOver(Canvas c, RecyclerView parent, State state) {
10044            onDrawOver(c, parent);
10045        }
10046
10047        /**
10048         * @deprecated
10049         * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
10050         */
10051        @Deprecated
10052        public void onDrawOver(Canvas c, RecyclerView parent) {
10053        }
10054
10055
10056        /**
10057         * @deprecated
10058         * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
10059         */
10060        @Deprecated
10061        public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
10062            outRect.set(0, 0, 0, 0);
10063        }
10064
10065        /**
10066         * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
10067         * the number of pixels that the item view should be inset by, similar to padding or margin.
10068         * The default implementation sets the bounds of outRect to 0 and returns.
10069         *
10070         * <p>
10071         * If this ItemDecoration does not affect the positioning of item views, it should set
10072         * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
10073         * before returning.
10074         *
10075         * <p>
10076         * If you need to access Adapter for additional data, you can call
10077         * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
10078         * View.
10079         *
10080         * @param outRect Rect to receive the output.
10081         * @param view    The child view to decorate
10082         * @param parent  RecyclerView this ItemDecoration is decorating
10083         * @param state   The current state of RecyclerView.
10084         */
10085        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
10086            getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
10087                    parent);
10088        }
10089    }
10090
10091    /**
10092     * An OnItemTouchListener allows the application to intercept touch events in progress at the
10093     * view hierarchy level of the RecyclerView before those touch events are considered for
10094     * RecyclerView's own scrolling behavior.
10095     *
10096     * <p>This can be useful for applications that wish to implement various forms of gestural
10097     * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
10098     * a touch interaction already in progress even if the RecyclerView is already handling that
10099     * gesture stream itself for the purposes of scrolling.</p>
10100     *
10101     * @see SimpleOnItemTouchListener
10102     */
10103    public interface OnItemTouchListener {
10104        /**
10105         * Silently observe and/or take over touch events sent to the RecyclerView
10106         * before they are handled by either the RecyclerView itself or its child views.
10107         *
10108         * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
10109         * in the order in which each listener was added, before any other touch processing
10110         * by the RecyclerView itself or child views occurs.</p>
10111         *
10112         * @param e MotionEvent describing the touch event. All coordinates are in
10113         *          the RecyclerView's coordinate system.
10114         * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
10115         *         to continue with the current behavior and continue observing future events in
10116         *         the gesture.
10117         */
10118        boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
10119
10120        /**
10121         * Process a touch event as part of a gesture that was claimed by returning true from
10122         * a previous call to {@link #onInterceptTouchEvent}.
10123         *
10124         * @param e MotionEvent describing the touch event. All coordinates are in
10125         *          the RecyclerView's coordinate system.
10126         */
10127        void onTouchEvent(RecyclerView rv, MotionEvent e);
10128
10129        /**
10130         * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
10131         * intercept touch events with
10132         * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
10133         *
10134         * @param disallowIntercept True if the child does not want the parent to
10135         *            intercept touch events.
10136         * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
10137         */
10138        void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
10139    }
10140
10141    /**
10142     * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies
10143     * and default return values.
10144     * <p>
10145     * You may prefer to extend this class if you don't need to override all methods. Another
10146     * benefit of using this class is future compatibility. As the interface may change, we'll
10147     * always provide a default implementation on this class so that your code won't break when
10148     * you update to a new version of the support library.
10149     */
10150    public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
10151        @Override
10152        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
10153            return false;
10154        }
10155
10156        @Override
10157        public void onTouchEvent(RecyclerView rv, MotionEvent e) {
10158        }
10159
10160        @Override
10161        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
10162        }
10163    }
10164
10165
10166    /**
10167     * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
10168     * has occurred on that RecyclerView.
10169     * <p>
10170     * @see RecyclerView#addOnScrollListener(OnScrollListener)
10171     * @see RecyclerView#clearOnChildAttachStateChangeListeners()
10172     *
10173     */
10174    public abstract static class OnScrollListener {
10175        /**
10176         * Callback method to be invoked when RecyclerView's scroll state changes.
10177         *
10178         * @param recyclerView The RecyclerView whose scroll state has changed.
10179         * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
10180         *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
10181         */
10182        public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
10183
10184        /**
10185         * Callback method to be invoked when the RecyclerView has been scrolled. This will be
10186         * called after the scroll has completed.
10187         * <p>
10188         * This callback will also be called if visible item range changes after a layout
10189         * calculation. In that case, dx and dy will be 0.
10190         *
10191         * @param recyclerView The RecyclerView which scrolled.
10192         * @param dx The amount of horizontal scroll.
10193         * @param dy The amount of vertical scroll.
10194         */
10195        public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
10196    }
10197
10198    /**
10199     * A RecyclerListener can be set on a RecyclerView to receive messages whenever
10200     * a view is recycled.
10201     *
10202     * @see RecyclerView#setRecyclerListener(RecyclerListener)
10203     */
10204    public interface RecyclerListener {
10205
10206        /**
10207         * This method is called whenever the view in the ViewHolder is recycled.
10208         *
10209         * RecyclerView calls this method right before clearing ViewHolder's internal data and
10210         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
10211         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
10212         * its adapter position.
10213         *
10214         * @param holder The ViewHolder containing the view that was recycled
10215         */
10216        void onViewRecycled(ViewHolder holder);
10217    }
10218
10219    /**
10220     * A Listener interface that can be attached to a RecylcerView to get notified
10221     * whenever a ViewHolder is attached to or detached from RecyclerView.
10222     */
10223    public interface OnChildAttachStateChangeListener {
10224
10225        /**
10226         * Called when a view is attached to the RecyclerView.
10227         *
10228         * @param view The View which is attached to the RecyclerView
10229         */
10230        void onChildViewAttachedToWindow(View view);
10231
10232        /**
10233         * Called when a view is detached from RecyclerView.
10234         *
10235         * @param view The View which is being detached from the RecyclerView
10236         */
10237        void onChildViewDetachedFromWindow(View view);
10238    }
10239
10240    /**
10241     * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
10242     *
10243     * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
10244     * potentially expensive {@link View#findViewById(int)} results.</p>
10245     *
10246     * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
10247     * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
10248     * their own custom ViewHolder implementations to store data that makes binding view contents
10249     * easier. Implementations should assume that individual item views will hold strong references
10250     * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
10251     * strong references to extra off-screen item views for caching purposes</p>
10252     */
10253    public abstract static class ViewHolder {
10254        public final View itemView;
10255        WeakReference<RecyclerView> mNestedRecyclerView;
10256        int mPosition = NO_POSITION;
10257        int mOldPosition = NO_POSITION;
10258        long mItemId = NO_ID;
10259        int mItemViewType = INVALID_TYPE;
10260        int mPreLayoutPosition = NO_POSITION;
10261
10262        // The item that this holder is shadowing during an item change event/animation
10263        ViewHolder mShadowedHolder = null;
10264        // The item that is shadowing this holder during an item change event/animation
10265        ViewHolder mShadowingHolder = null;
10266
10267        /**
10268         * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
10269         * are all valid.
10270         */
10271        static final int FLAG_BOUND = 1 << 0;
10272
10273        /**
10274         * The data this ViewHolder's view reflects is stale and needs to be rebound
10275         * by the adapter. mPosition and mItemId are consistent.
10276         */
10277        static final int FLAG_UPDATE = 1 << 1;
10278
10279        /**
10280         * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
10281         * are not to be trusted and may no longer match the item view type.
10282         * This ViewHolder must be fully rebound to different data.
10283         */
10284        static final int FLAG_INVALID = 1 << 2;
10285
10286        /**
10287         * This ViewHolder points at data that represents an item previously removed from the
10288         * data set. Its view may still be used for things like outgoing animations.
10289         */
10290        static final int FLAG_REMOVED = 1 << 3;
10291
10292        /**
10293         * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
10294         * and is intended to keep views around during animations.
10295         */
10296        static final int FLAG_NOT_RECYCLABLE = 1 << 4;
10297
10298        /**
10299         * This ViewHolder is returned from scrap which means we are expecting an addView call
10300         * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
10301         * the end of the layout pass and then recycled by RecyclerView if it is not added back to
10302         * the RecyclerView.
10303         */
10304        static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
10305
10306        /**
10307         * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
10308         * it unless LayoutManager is replaced.
10309         * It is still fully visible to the LayoutManager.
10310         */
10311        static final int FLAG_IGNORE = 1 << 7;
10312
10313        /**
10314         * When the View is detached form the parent, we set this flag so that we can take correct
10315         * action when we need to remove it or add it back.
10316         */
10317        static final int FLAG_TMP_DETACHED = 1 << 8;
10318
10319        /**
10320         * Set when we can no longer determine the adapter position of this ViewHolder until it is
10321         * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
10322         * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
10323         * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
10324         * re-calculated.
10325         */
10326        static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
10327
10328        /**
10329         * Set when a addChangePayload(null) is called
10330         */
10331        static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
10332
10333        /**
10334         * Used by ItemAnimator when a ViewHolder's position changes
10335         */
10336        static final int FLAG_MOVED = 1 << 11;
10337
10338        /**
10339         * Used by ItemAnimator when a ViewHolder appears in pre-layout
10340         */
10341        static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
10342
10343        static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
10344
10345        /**
10346         * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
10347         * hidden list (as if it was scrap) without being recycled in between.
10348         *
10349         * When a ViewHolder is hidden, there are 2 paths it can be re-used:
10350         *   a) Animation ends, view is recycled and used from the recycle pool.
10351         *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
10352         *
10353         * This flag is used to represent "case b" where the ViewHolder is reused without being
10354         * recycled (thus "bounced" from the hidden list). This state requires special handling
10355         * because the ViewHolder must be added to pre layout maps for animations as if it was
10356         * already there.
10357         */
10358        static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
10359
10360        /**
10361         * Flags that RecyclerView assigned {@link RecyclerViewAccessibilityDelegate
10362         * #getItemDelegate()} in onBindView when app does not provide a delegate.
10363         */
10364        static final int FLAG_SET_A11Y_ITEM_DELEGATE = 1 << 14;
10365
10366        private int mFlags;
10367
10368        private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
10369
10370        List<Object> mPayloads = null;
10371        List<Object> mUnmodifiedPayloads = null;
10372
10373        private int mIsRecyclableCount = 0;
10374
10375        // If non-null, view is currently considered scrap and may be reused for other data by the
10376        // scrap container.
10377        private Recycler mScrapContainer = null;
10378        // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
10379        private boolean mInChangeScrap = false;
10380
10381        // Saves isImportantForAccessibility value for the view item while it's in hidden state and
10382        // marked as unimportant for accessibility.
10383        private int mWasImportantForAccessibilityBeforeHidden =
10384                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10385        // set if we defer the accessibility state change of the view holder
10386        @VisibleForTesting
10387        int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
10388
10389        /**
10390         * Is set when VH is bound from the adapter and cleaned right before it is sent to
10391         * {@link RecycledViewPool}.
10392         */
10393        RecyclerView mOwnerRecyclerView;
10394
10395        public ViewHolder(View itemView) {
10396            if (itemView == null) {
10397                throw new IllegalArgumentException("itemView may not be null");
10398            }
10399            this.itemView = itemView;
10400        }
10401
10402        void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
10403            addFlags(ViewHolder.FLAG_REMOVED);
10404            offsetPosition(offset, applyToPreLayout);
10405            mPosition = mNewPosition;
10406        }
10407
10408        void offsetPosition(int offset, boolean applyToPreLayout) {
10409            if (mOldPosition == NO_POSITION) {
10410                mOldPosition = mPosition;
10411            }
10412            if (mPreLayoutPosition == NO_POSITION) {
10413                mPreLayoutPosition = mPosition;
10414            }
10415            if (applyToPreLayout) {
10416                mPreLayoutPosition += offset;
10417            }
10418            mPosition += offset;
10419            if (itemView.getLayoutParams() != null) {
10420                ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
10421            }
10422        }
10423
10424        void clearOldPosition() {
10425            mOldPosition = NO_POSITION;
10426            mPreLayoutPosition = NO_POSITION;
10427        }
10428
10429        void saveOldPosition() {
10430            if (mOldPosition == NO_POSITION) {
10431                mOldPosition = mPosition;
10432            }
10433        }
10434
10435        boolean shouldIgnore() {
10436            return (mFlags & FLAG_IGNORE) != 0;
10437        }
10438
10439        /**
10440         * @deprecated This method is deprecated because its meaning is ambiguous due to the async
10441         * handling of adapter updates. Please use {@link #getLayoutPosition()} or
10442         * {@link #getAdapterPosition()} depending on your use case.
10443         *
10444         * @see #getLayoutPosition()
10445         * @see #getAdapterPosition()
10446         */
10447        @Deprecated
10448        public final int getPosition() {
10449            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10450        }
10451
10452        /**
10453         * Returns the position of the ViewHolder in terms of the latest layout pass.
10454         * <p>
10455         * This position is mostly used by RecyclerView components to be consistent while
10456         * RecyclerView lazily processes adapter updates.
10457         * <p>
10458         * For performance and animation reasons, RecyclerView batches all adapter updates until the
10459         * next layout pass. This may cause mismatches between the Adapter position of the item and
10460         * the position it had in the latest layout calculations.
10461         * <p>
10462         * LayoutManagers should always call this method while doing calculations based on item
10463         * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
10464         * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
10465         * of the item.
10466         * <p>
10467         * If LayoutManager needs to call an external method that requires the adapter position of
10468         * the item, it can use {@link #getAdapterPosition()} or
10469         * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
10470         *
10471         * @return Returns the adapter position of the ViewHolder in the latest layout pass.
10472         * @see #getAdapterPosition()
10473         */
10474        public final int getLayoutPosition() {
10475            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10476        }
10477
10478        /**
10479         * Returns the Adapter position of the item represented by this ViewHolder.
10480         * <p>
10481         * Note that this might be different than the {@link #getLayoutPosition()} if there are
10482         * pending adapter updates but a new layout pass has not happened yet.
10483         * <p>
10484         * RecyclerView does not handle any adapter updates until the next layout traversal. This
10485         * may create temporary inconsistencies between what user sees on the screen and what
10486         * adapter contents have. This inconsistency is not important since it will be less than
10487         * 16ms but it might be a problem if you want to use ViewHolder position to access the
10488         * adapter. Sometimes, you may need to get the exact adapter position to do
10489         * some actions in response to user events. In that case, you should use this method which
10490         * will calculate the Adapter position of the ViewHolder.
10491         * <p>
10492         * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
10493         * next layout pass, the return value of this method will be {@link #NO_POSITION}.
10494         *
10495         * @return The adapter position of the item if it still exists in the adapter.
10496         * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
10497         * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
10498         * layout pass or the ViewHolder has already been recycled.
10499         */
10500        public final int getAdapterPosition() {
10501            if (mOwnerRecyclerView == null) {
10502                return NO_POSITION;
10503            }
10504            return mOwnerRecyclerView.getAdapterPositionFor(this);
10505        }
10506
10507        /**
10508         * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
10509         * to perform animations.
10510         * <p>
10511         * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
10512         * adapter index in the previous layout.
10513         *
10514         * @return The previous adapter index of the Item represented by this ViewHolder or
10515         * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
10516         * complete).
10517         */
10518        public final int getOldPosition() {
10519            return mOldPosition;
10520        }
10521
10522        /**
10523         * Returns The itemId represented by this ViewHolder.
10524         *
10525         * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
10526         * otherwise
10527         */
10528        public final long getItemId() {
10529            return mItemId;
10530        }
10531
10532        /**
10533         * @return The view type of this ViewHolder.
10534         */
10535        public final int getItemViewType() {
10536            return mItemViewType;
10537        }
10538
10539        boolean isScrap() {
10540            return mScrapContainer != null;
10541        }
10542
10543        void unScrap() {
10544            mScrapContainer.unscrapView(this);
10545        }
10546
10547        boolean wasReturnedFromScrap() {
10548            return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
10549        }
10550
10551        void clearReturnedFromScrapFlag() {
10552            mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
10553        }
10554
10555        void clearTmpDetachFlag() {
10556            mFlags = mFlags & ~FLAG_TMP_DETACHED;
10557        }
10558
10559        void stopIgnoring() {
10560            mFlags = mFlags & ~FLAG_IGNORE;
10561        }
10562
10563        void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
10564            mScrapContainer = recycler;
10565            mInChangeScrap = isChangeScrap;
10566        }
10567
10568        boolean isInvalid() {
10569            return (mFlags & FLAG_INVALID) != 0;
10570        }
10571
10572        boolean needsUpdate() {
10573            return (mFlags & FLAG_UPDATE) != 0;
10574        }
10575
10576        boolean isBound() {
10577            return (mFlags & FLAG_BOUND) != 0;
10578        }
10579
10580        boolean isRemoved() {
10581            return (mFlags & FLAG_REMOVED) != 0;
10582        }
10583
10584        boolean hasAnyOfTheFlags(int flags) {
10585            return (mFlags & flags) != 0;
10586        }
10587
10588        boolean isTmpDetached() {
10589            return (mFlags & FLAG_TMP_DETACHED) != 0;
10590        }
10591
10592        boolean isAdapterPositionUnknown() {
10593            return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
10594        }
10595
10596        void setFlags(int flags, int mask) {
10597            mFlags = (mFlags & ~mask) | (flags & mask);
10598        }
10599
10600        void addFlags(int flags) {
10601            mFlags |= flags;
10602        }
10603
10604        void addChangePayload(Object payload) {
10605            if (payload == null) {
10606                addFlags(FLAG_ADAPTER_FULLUPDATE);
10607            } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10608                createPayloadsIfNeeded();
10609                mPayloads.add(payload);
10610            }
10611        }
10612
10613        private void createPayloadsIfNeeded() {
10614            if (mPayloads == null) {
10615                mPayloads = new ArrayList<Object>();
10616                mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
10617            }
10618        }
10619
10620        void clearPayload() {
10621            if (mPayloads != null) {
10622                mPayloads.clear();
10623            }
10624            mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
10625        }
10626
10627        List<Object> getUnmodifiedPayloads() {
10628            if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10629                if (mPayloads == null || mPayloads.size() == 0) {
10630                    // Initial state,  no update being called.
10631                    return FULLUPDATE_PAYLOADS;
10632                }
10633                // there are none-null payloads
10634                return mUnmodifiedPayloads;
10635            } else {
10636                // a full update has been called.
10637                return FULLUPDATE_PAYLOADS;
10638            }
10639        }
10640
10641        void resetInternal() {
10642            mFlags = 0;
10643            mPosition = NO_POSITION;
10644            mOldPosition = NO_POSITION;
10645            mItemId = NO_ID;
10646            mPreLayoutPosition = NO_POSITION;
10647            mIsRecyclableCount = 0;
10648            mShadowedHolder = null;
10649            mShadowingHolder = null;
10650            clearPayload();
10651            mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10652            mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
10653            clearNestedRecyclerViewIfNotNested(this);
10654        }
10655
10656        /**
10657         * Called when the child view enters the hidden state
10658         */
10659        private void onEnteredHiddenState(RecyclerView parent) {
10660            // While the view item is in hidden state, make it invisible for the accessibility.
10661            mWasImportantForAccessibilityBeforeHidden =
10662                    ViewCompat.getImportantForAccessibility(itemView);
10663            parent.setChildImportantForAccessibilityInternal(this,
10664                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
10665        }
10666
10667        /**
10668         * Called when the child view leaves the hidden state
10669         */
10670        private void onLeftHiddenState(RecyclerView parent) {
10671            parent.setChildImportantForAccessibilityInternal(this,
10672                    mWasImportantForAccessibilityBeforeHidden);
10673            mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10674        }
10675
10676        @Override
10677        public String toString() {
10678            final StringBuilder sb = new StringBuilder("ViewHolder{"
10679                    + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
10680                    + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
10681            if (isScrap()) {
10682                sb.append(" scrap ")
10683                        .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
10684            }
10685            if (isInvalid()) sb.append(" invalid");
10686            if (!isBound()) sb.append(" unbound");
10687            if (needsUpdate()) sb.append(" update");
10688            if (isRemoved()) sb.append(" removed");
10689            if (shouldIgnore()) sb.append(" ignored");
10690            if (isTmpDetached()) sb.append(" tmpDetached");
10691            if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
10692            if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
10693
10694            if (itemView.getParent() == null) sb.append(" no parent");
10695            sb.append("}");
10696            return sb.toString();
10697        }
10698
10699        /**
10700         * Informs the recycler whether this item can be recycled. Views which are not
10701         * recyclable will not be reused for other items until setIsRecyclable() is
10702         * later set to true. Calls to setIsRecyclable() should always be paired (one
10703         * call to setIsRecyclabe(false) should always be matched with a later call to
10704         * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
10705         * reference-counted.
10706         *
10707         * @param recyclable Whether this item is available to be recycled. Default value
10708         * is true.
10709         *
10710         * @see #isRecyclable()
10711         */
10712        public final void setIsRecyclable(boolean recyclable) {
10713            mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
10714            if (mIsRecyclableCount < 0) {
10715                mIsRecyclableCount = 0;
10716                if (DEBUG) {
10717                    throw new RuntimeException("isRecyclable decremented below 0: "
10718                            + "unmatched pair of setIsRecyable() calls for " + this);
10719                }
10720                Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: "
10721                        + "unmatched pair of setIsRecyable() calls for " + this);
10722            } else if (!recyclable && mIsRecyclableCount == 1) {
10723                mFlags |= FLAG_NOT_RECYCLABLE;
10724            } else if (recyclable && mIsRecyclableCount == 0) {
10725                mFlags &= ~FLAG_NOT_RECYCLABLE;
10726            }
10727            if (DEBUG) {
10728                Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
10729            }
10730        }
10731
10732        /**
10733         * @return true if this item is available to be recycled, false otherwise.
10734         *
10735         * @see #setIsRecyclable(boolean)
10736         */
10737        public final boolean isRecyclable() {
10738            return (mFlags & FLAG_NOT_RECYCLABLE) == 0
10739                    && !ViewCompat.hasTransientState(itemView);
10740        }
10741
10742        /**
10743         * Returns whether we have animations referring to this view holder or not.
10744         * This is similar to isRecyclable flag but does not check transient state.
10745         */
10746        private boolean shouldBeKeptAsChild() {
10747            return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
10748        }
10749
10750        /**
10751         * @return True if ViewHolder is not referenced by RecyclerView animations but has
10752         * transient state which will prevent it from being recycled.
10753         */
10754        private boolean doesTransientStatePreventRecycling() {
10755            return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView);
10756        }
10757
10758        boolean isUpdated() {
10759            return (mFlags & FLAG_UPDATE) != 0;
10760        }
10761    }
10762
10763    /**
10764     * This method is here so that we can control the important for a11y changes and test it.
10765     */
10766    @VisibleForTesting
10767    boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
10768            int importantForAccessibility) {
10769        if (isComputingLayout()) {
10770            viewHolder.mPendingAccessibilityState = importantForAccessibility;
10771            mPendingAccessibilityImportanceChange.add(viewHolder);
10772            return false;
10773        }
10774        ViewCompat.setImportantForAccessibility(viewHolder.itemView, importantForAccessibility);
10775        return true;
10776    }
10777
10778    void dispatchPendingImportantForAccessibilityChanges() {
10779        for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
10780            ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
10781            if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
10782                continue;
10783            }
10784            int state = viewHolder.mPendingAccessibilityState;
10785            if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
10786                //noinspection WrongConstant
10787                ViewCompat.setImportantForAccessibility(viewHolder.itemView, state);
10788                viewHolder.mPendingAccessibilityState =
10789                        ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
10790            }
10791        }
10792        mPendingAccessibilityImportanceChange.clear();
10793    }
10794
10795    int getAdapterPositionFor(ViewHolder viewHolder) {
10796        if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
10797                | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
10798                || !viewHolder.isBound()) {
10799            return RecyclerView.NO_POSITION;
10800        }
10801        return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
10802    }
10803
10804    @VisibleForTesting
10805    void initFastScroller(StateListDrawable verticalThumbDrawable,
10806            Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable,
10807            Drawable horizontalTrackDrawable) {
10808        if (verticalThumbDrawable == null || verticalTrackDrawable == null
10809                || horizontalThumbDrawable == null || horizontalTrackDrawable == null) {
10810            throw new IllegalArgumentException(
10811                "Trying to set fast scroller without both required drawables.");
10812        }
10813
10814        Resources resources = getContext().getResources();
10815        new FastScroller(this, verticalThumbDrawable, verticalTrackDrawable,
10816                horizontalThumbDrawable, horizontalTrackDrawable,
10817                resources.getDimensionPixelSize(R.dimen.fastscroll_default_thickness),
10818                resources.getDimensionPixelSize(R.dimen.fastscroll_minimum_range),
10819                resources.getDimensionPixelOffset(R.dimen.fastscroll_margin));
10820    }
10821
10822    // NestedScrollingChild
10823
10824    @Override
10825    public void setNestedScrollingEnabled(boolean enabled) {
10826        getScrollingChildHelper().setNestedScrollingEnabled(enabled);
10827    }
10828
10829    @Override
10830    public boolean isNestedScrollingEnabled() {
10831        return getScrollingChildHelper().isNestedScrollingEnabled();
10832    }
10833
10834    @Override
10835    public boolean startNestedScroll(int axes) {
10836        return getScrollingChildHelper().startNestedScroll(axes);
10837    }
10838
10839    @Override
10840    public boolean startNestedScroll(int axes, int type) {
10841        return getScrollingChildHelper().startNestedScroll(axes, type);
10842    }
10843
10844    @Override
10845    public void stopNestedScroll() {
10846        getScrollingChildHelper().stopNestedScroll();
10847    }
10848
10849    @Override
10850    public void stopNestedScroll(int type) {
10851        getScrollingChildHelper().stopNestedScroll(type);
10852    }
10853
10854    @Override
10855    public boolean hasNestedScrollingParent() {
10856        return getScrollingChildHelper().hasNestedScrollingParent();
10857    }
10858
10859    @Override
10860    public boolean hasNestedScrollingParent(int type) {
10861        return getScrollingChildHelper().hasNestedScrollingParent(type);
10862    }
10863
10864    @Override
10865    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
10866            int dyUnconsumed, int[] offsetInWindow) {
10867        return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
10868                dxUnconsumed, dyUnconsumed, offsetInWindow);
10869    }
10870
10871    @Override
10872    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
10873            int dyUnconsumed, int[] offsetInWindow, int type) {
10874        return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
10875                dxUnconsumed, dyUnconsumed, offsetInWindow, type);
10876    }
10877
10878    @Override
10879    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
10880        return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
10881    }
10882
10883    @Override
10884    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
10885            int type) {
10886        return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow,
10887                type);
10888    }
10889
10890    @Override
10891    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
10892        return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
10893    }
10894
10895    @Override
10896    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
10897        return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
10898    }
10899
10900    /**
10901     * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
10902     * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
10903     * to create their own subclass of this <code>LayoutParams</code> class
10904     * to store any additional required per-child view metadata about the layout.
10905     */
10906    public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
10907        ViewHolder mViewHolder;
10908        final Rect mDecorInsets = new Rect();
10909        boolean mInsetsDirty = true;
10910        // Flag is set to true if the view is bound while it is detached from RV.
10911        // In this case, we need to manually call invalidate after view is added to guarantee that
10912        // invalidation is populated through the View hierarchy
10913        boolean mPendingInvalidate = false;
10914
10915        public LayoutParams(Context c, AttributeSet attrs) {
10916            super(c, attrs);
10917        }
10918
10919        public LayoutParams(int width, int height) {
10920            super(width, height);
10921        }
10922
10923        public LayoutParams(MarginLayoutParams source) {
10924            super(source);
10925        }
10926
10927        public LayoutParams(ViewGroup.LayoutParams source) {
10928            super(source);
10929        }
10930
10931        public LayoutParams(LayoutParams source) {
10932            super((ViewGroup.LayoutParams) source);
10933        }
10934
10935        /**
10936         * Returns true if the view this LayoutParams is attached to needs to have its content
10937         * updated from the corresponding adapter.
10938         *
10939         * @return true if the view should have its content updated
10940         */
10941        public boolean viewNeedsUpdate() {
10942            return mViewHolder.needsUpdate();
10943        }
10944
10945        /**
10946         * Returns true if the view this LayoutParams is attached to is now representing
10947         * potentially invalid data. A LayoutManager should scrap/recycle it.
10948         *
10949         * @return true if the view is invalid
10950         */
10951        public boolean isViewInvalid() {
10952            return mViewHolder.isInvalid();
10953        }
10954
10955        /**
10956         * Returns true if the adapter data item corresponding to the view this LayoutParams
10957         * is attached to has been removed from the data set. A LayoutManager may choose to
10958         * treat it differently in order to animate its outgoing or disappearing state.
10959         *
10960         * @return true if the item the view corresponds to was removed from the data set
10961         */
10962        public boolean isItemRemoved() {
10963            return mViewHolder.isRemoved();
10964        }
10965
10966        /**
10967         * Returns true if the adapter data item corresponding to the view this LayoutParams
10968         * is attached to has been changed in the data set. A LayoutManager may choose to
10969         * treat it differently in order to animate its changing state.
10970         *
10971         * @return true if the item the view corresponds to was changed in the data set
10972         */
10973        public boolean isItemChanged() {
10974            return mViewHolder.isUpdated();
10975        }
10976
10977        /**
10978         * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
10979         */
10980        @Deprecated
10981        public int getViewPosition() {
10982            return mViewHolder.getPosition();
10983        }
10984
10985        /**
10986         * Returns the adapter position that the view this LayoutParams is attached to corresponds
10987         * to as of latest layout calculation.
10988         *
10989         * @return the adapter position this view as of latest layout pass
10990         */
10991        public int getViewLayoutPosition() {
10992            return mViewHolder.getLayoutPosition();
10993        }
10994
10995        /**
10996         * Returns the up-to-date adapter position that the view this LayoutParams is attached to
10997         * corresponds to.
10998         *
10999         * @return the up-to-date adapter position this view. It may return
11000         * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
11001         * its up-to-date position cannot be calculated.
11002         */
11003        public int getViewAdapterPosition() {
11004            return mViewHolder.getAdapterPosition();
11005        }
11006    }
11007
11008    /**
11009     * Observer base class for watching changes to an {@link Adapter}.
11010     * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
11011     */
11012    public abstract static class AdapterDataObserver {
11013        public void onChanged() {
11014            // Do nothing
11015        }
11016
11017        public void onItemRangeChanged(int positionStart, int itemCount) {
11018            // do nothing
11019        }
11020
11021        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
11022            // fallback to onItemRangeChanged(positionStart, itemCount) if app
11023            // does not override this method.
11024            onItemRangeChanged(positionStart, itemCount);
11025        }
11026
11027        public void onItemRangeInserted(int positionStart, int itemCount) {
11028            // do nothing
11029        }
11030
11031        public void onItemRangeRemoved(int positionStart, int itemCount) {
11032            // do nothing
11033        }
11034
11035        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
11036            // do nothing
11037        }
11038    }
11039
11040    /**
11041     * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
11042     * provides methods to trigger a programmatic scroll.</p>
11043     *
11044     * @see LinearSmoothScroller
11045     */
11046    public abstract static class SmoothScroller {
11047
11048        private int mTargetPosition = RecyclerView.NO_POSITION;
11049
11050        private RecyclerView mRecyclerView;
11051
11052        private LayoutManager mLayoutManager;
11053
11054        private boolean mPendingInitialRun;
11055
11056        private boolean mRunning;
11057
11058        private View mTargetView;
11059
11060        private final Action mRecyclingAction;
11061
11062        public SmoothScroller() {
11063            mRecyclingAction = new Action(0, 0);
11064        }
11065
11066        /**
11067         * Starts a smooth scroll for the given target position.
11068         * <p>In each animation step, {@link RecyclerView} will check
11069         * for the target view and call either
11070         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
11071         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
11072         * SmoothScroller is stopped.</p>
11073         *
11074         * <p>Note that if RecyclerView finds the target view, it will automatically stop the
11075         * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
11076         * stop calling SmoothScroller in each animation step.</p>
11077         */
11078        void start(RecyclerView recyclerView, LayoutManager layoutManager) {
11079            mRecyclerView = recyclerView;
11080            mLayoutManager = layoutManager;
11081            if (mTargetPosition == RecyclerView.NO_POSITION) {
11082                throw new IllegalArgumentException("Invalid target position");
11083            }
11084            mRecyclerView.mState.mTargetPosition = mTargetPosition;
11085            mRunning = true;
11086            mPendingInitialRun = true;
11087            mTargetView = findViewByPosition(getTargetPosition());
11088            onStart();
11089            mRecyclerView.mViewFlinger.postOnAnimation();
11090        }
11091
11092        public void setTargetPosition(int targetPosition) {
11093            mTargetPosition = targetPosition;
11094        }
11095
11096        /**
11097         * @return The LayoutManager to which this SmoothScroller is attached. Will return
11098         * <code>null</code> after the SmoothScroller is stopped.
11099         */
11100        @Nullable
11101        public LayoutManager getLayoutManager() {
11102            return mLayoutManager;
11103        }
11104
11105        /**
11106         * Stops running the SmoothScroller in each animation callback. Note that this does not
11107         * cancel any existing {@link Action} updated by
11108         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
11109         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
11110         */
11111        protected final void stop() {
11112            if (!mRunning) {
11113                return;
11114            }
11115            onStop();
11116            mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
11117            mTargetView = null;
11118            mTargetPosition = RecyclerView.NO_POSITION;
11119            mPendingInitialRun = false;
11120            mRunning = false;
11121            // trigger a cleanup
11122            mLayoutManager.onSmoothScrollerStopped(this);
11123            // clear references to avoid any potential leak by a custom smooth scroller
11124            mLayoutManager = null;
11125            mRecyclerView = null;
11126        }
11127
11128        /**
11129         * Returns true if SmoothScroller has been started but has not received the first
11130         * animation
11131         * callback yet.
11132         *
11133         * @return True if this SmoothScroller is waiting to start
11134         */
11135        public boolean isPendingInitialRun() {
11136            return mPendingInitialRun;
11137        }
11138
11139
11140        /**
11141         * @return True if SmoothScroller is currently active
11142         */
11143        public boolean isRunning() {
11144            return mRunning;
11145        }
11146
11147        /**
11148         * Returns the adapter position of the target item
11149         *
11150         * @return Adapter position of the target item or
11151         * {@link RecyclerView#NO_POSITION} if no target view is set.
11152         */
11153        public int getTargetPosition() {
11154            return mTargetPosition;
11155        }
11156
11157        private void onAnimation(int dx, int dy) {
11158            final RecyclerView recyclerView = mRecyclerView;
11159            if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
11160                stop();
11161            }
11162            mPendingInitialRun = false;
11163            if (mTargetView != null) {
11164                // verify target position
11165                if (getChildPosition(mTargetView) == mTargetPosition) {
11166                    onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
11167                    mRecyclingAction.runIfNecessary(recyclerView);
11168                    stop();
11169                } else {
11170                    Log.e(TAG, "Passed over target position while smooth scrolling.");
11171                    mTargetView = null;
11172                }
11173            }
11174            if (mRunning) {
11175                onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
11176                boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
11177                mRecyclingAction.runIfNecessary(recyclerView);
11178                if (hadJumpTarget) {
11179                    // It is not stopped so needs to be restarted
11180                    if (mRunning) {
11181                        mPendingInitialRun = true;
11182                        recyclerView.mViewFlinger.postOnAnimation();
11183                    } else {
11184                        stop(); // done
11185                    }
11186                }
11187            }
11188        }
11189
11190        /**
11191         * @see RecyclerView#getChildLayoutPosition(android.view.View)
11192         */
11193        public int getChildPosition(View view) {
11194            return mRecyclerView.getChildLayoutPosition(view);
11195        }
11196
11197        /**
11198         * @see RecyclerView.LayoutManager#getChildCount()
11199         */
11200        public int getChildCount() {
11201            return mRecyclerView.mLayout.getChildCount();
11202        }
11203
11204        /**
11205         * @see RecyclerView.LayoutManager#findViewByPosition(int)
11206         */
11207        public View findViewByPosition(int position) {
11208            return mRecyclerView.mLayout.findViewByPosition(position);
11209        }
11210
11211        /**
11212         * @see RecyclerView#scrollToPosition(int)
11213         * @deprecated Use {@link Action#jumpTo(int)}.
11214         */
11215        @Deprecated
11216        public void instantScrollToPosition(int position) {
11217            mRecyclerView.scrollToPosition(position);
11218        }
11219
11220        protected void onChildAttachedToWindow(View child) {
11221            if (getChildPosition(child) == getTargetPosition()) {
11222                mTargetView = child;
11223                if (DEBUG) {
11224                    Log.d(TAG, "smooth scroll target view has been attached");
11225                }
11226            }
11227        }
11228
11229        /**
11230         * Normalizes the vector.
11231         * @param scrollVector The vector that points to the target scroll position
11232         */
11233        protected void normalize(PointF scrollVector) {
11234            final float magnitude = (float) Math.sqrt(scrollVector.x * scrollVector.x
11235                    + scrollVector.y * scrollVector.y);
11236            scrollVector.x /= magnitude;
11237            scrollVector.y /= magnitude;
11238        }
11239
11240        /**
11241         * Called when smooth scroll is started. This might be a good time to do setup.
11242         */
11243        protected abstract void onStart();
11244
11245        /**
11246         * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
11247         * @see #stop()
11248         */
11249        protected abstract void onStop();
11250
11251        /**
11252         * <p>RecyclerView will call this method each time it scrolls until it can find the target
11253         * position in the layout.</p>
11254         * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
11255         * provided {@link Action} to define the next scroll.</p>
11256         *
11257         * @param dx        Last scroll amount horizontally
11258         * @param dy        Last scroll amount vertically
11259         * @param state     Transient state of RecyclerView
11260         * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
11261         *                  update this object.
11262         */
11263        protected abstract void onSeekTargetStep(int dx, int dy, State state, Action action);
11264
11265        /**
11266         * Called when the target position is laid out. This is the last callback SmoothScroller
11267         * will receive and it should update the provided {@link Action} to define the scroll
11268         * details towards the target view.
11269         * @param targetView    The view element which render the target position.
11270         * @param state         Transient state of RecyclerView
11271         * @param action        Action instance that you should update to define final scroll action
11272         *                      towards the targetView
11273         */
11274        protected abstract void onTargetFound(View targetView, State state, Action action);
11275
11276        /**
11277         * Holds information about a smooth scroll request by a {@link SmoothScroller}.
11278         */
11279        public static class Action {
11280
11281            public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
11282
11283            private int mDx;
11284
11285            private int mDy;
11286
11287            private int mDuration;
11288
11289            private int mJumpToPosition = NO_POSITION;
11290
11291            private Interpolator mInterpolator;
11292
11293            private boolean mChanged = false;
11294
11295            // we track this variable to inform custom implementer if they are updating the action
11296            // in every animation callback
11297            private int mConsecutiveUpdates = 0;
11298
11299            /**
11300             * @param dx Pixels to scroll horizontally
11301             * @param dy Pixels to scroll vertically
11302             */
11303            public Action(int dx, int dy) {
11304                this(dx, dy, UNDEFINED_DURATION, null);
11305            }
11306
11307            /**
11308             * @param dx       Pixels to scroll horizontally
11309             * @param dy       Pixels to scroll vertically
11310             * @param duration Duration of the animation in milliseconds
11311             */
11312            public Action(int dx, int dy, int duration) {
11313                this(dx, dy, duration, null);
11314            }
11315
11316            /**
11317             * @param dx           Pixels to scroll horizontally
11318             * @param dy           Pixels to scroll vertically
11319             * @param duration     Duration of the animation in milliseconds
11320             * @param interpolator Interpolator to be used when calculating scroll position in each
11321             *                     animation step
11322             */
11323            public Action(int dx, int dy, int duration, Interpolator interpolator) {
11324                mDx = dx;
11325                mDy = dy;
11326                mDuration = duration;
11327                mInterpolator = interpolator;
11328            }
11329
11330            /**
11331             * Instead of specifying pixels to scroll, use the target position to jump using
11332             * {@link RecyclerView#scrollToPosition(int)}.
11333             * <p>
11334             * You may prefer using this method if scroll target is really far away and you prefer
11335             * to jump to a location and smooth scroll afterwards.
11336             * <p>
11337             * Note that calling this method takes priority over other update methods such as
11338             * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
11339             * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
11340             * {@link #jumpTo(int)}, the other changes will not be considered for this animation
11341             * frame.
11342             *
11343             * @param targetPosition The target item position to scroll to using instant scrolling.
11344             */
11345            public void jumpTo(int targetPosition) {
11346                mJumpToPosition = targetPosition;
11347            }
11348
11349            boolean hasJumpTarget() {
11350                return mJumpToPosition >= 0;
11351            }
11352
11353            void runIfNecessary(RecyclerView recyclerView) {
11354                if (mJumpToPosition >= 0) {
11355                    final int position = mJumpToPosition;
11356                    mJumpToPosition = NO_POSITION;
11357                    recyclerView.jumpToPositionForSmoothScroller(position);
11358                    mChanged = false;
11359                    return;
11360                }
11361                if (mChanged) {
11362                    validate();
11363                    if (mInterpolator == null) {
11364                        if (mDuration == UNDEFINED_DURATION) {
11365                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
11366                        } else {
11367                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
11368                        }
11369                    } else {
11370                        recyclerView.mViewFlinger.smoothScrollBy(
11371                                mDx, mDy, mDuration, mInterpolator);
11372                    }
11373                    mConsecutiveUpdates++;
11374                    if (mConsecutiveUpdates > 10) {
11375                        // A new action is being set in every animation step. This looks like a bad
11376                        // implementation. Inform developer.
11377                        Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
11378                                + " you are not changing it unless necessary");
11379                    }
11380                    mChanged = false;
11381                } else {
11382                    mConsecutiveUpdates = 0;
11383                }
11384            }
11385
11386            private void validate() {
11387                if (mInterpolator != null && mDuration < 1) {
11388                    throw new IllegalStateException("If you provide an interpolator, you must"
11389                            + " set a positive duration");
11390                } else if (mDuration < 1) {
11391                    throw new IllegalStateException("Scroll duration must be a positive number");
11392                }
11393            }
11394
11395            public int getDx() {
11396                return mDx;
11397            }
11398
11399            public void setDx(int dx) {
11400                mChanged = true;
11401                mDx = dx;
11402            }
11403
11404            public int getDy() {
11405                return mDy;
11406            }
11407
11408            public void setDy(int dy) {
11409                mChanged = true;
11410                mDy = dy;
11411            }
11412
11413            public int getDuration() {
11414                return mDuration;
11415            }
11416
11417            public void setDuration(int duration) {
11418                mChanged = true;
11419                mDuration = duration;
11420            }
11421
11422            public Interpolator getInterpolator() {
11423                return mInterpolator;
11424            }
11425
11426            /**
11427             * Sets the interpolator to calculate scroll steps
11428             * @param interpolator The interpolator to use. If you specify an interpolator, you must
11429             *                     also set the duration.
11430             * @see #setDuration(int)
11431             */
11432            public void setInterpolator(Interpolator interpolator) {
11433                mChanged = true;
11434                mInterpolator = interpolator;
11435            }
11436
11437            /**
11438             * Updates the action with given parameters.
11439             * @param dx Pixels to scroll horizontally
11440             * @param dy Pixels to scroll vertically
11441             * @param duration Duration of the animation in milliseconds
11442             * @param interpolator Interpolator to be used when calculating scroll position in each
11443             *                     animation step
11444             */
11445            public void update(int dx, int dy, int duration, Interpolator interpolator) {
11446                mDx = dx;
11447                mDy = dy;
11448                mDuration = duration;
11449                mInterpolator = interpolator;
11450                mChanged = true;
11451            }
11452        }
11453
11454        /**
11455         * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
11456         * to provide a hint to a {@link SmoothScroller} about the location of the target position.
11457         */
11458        public interface ScrollVectorProvider {
11459            /**
11460             * Should calculate the vector that points to the direction where the target position
11461             * can be found.
11462             * <p>
11463             * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
11464             * the target position.
11465             * <p>
11466             * The magnitude of the vector is not important. It is always normalized before being
11467             * used by the {@link LinearSmoothScroller}.
11468             * <p>
11469             * LayoutManager should not check whether the position exists in the adapter or not.
11470             *
11471             * @param targetPosition the target position to which the returned vector should point
11472             *
11473             * @return the scroll vector for a given position.
11474             */
11475            PointF computeScrollVectorForPosition(int targetPosition);
11476        }
11477    }
11478
11479    static class AdapterDataObservable extends Observable<AdapterDataObserver> {
11480        public boolean hasObservers() {
11481            return !mObservers.isEmpty();
11482        }
11483
11484        public void notifyChanged() {
11485            // since onChanged() is implemented by the app, it could do anything, including
11486            // removing itself from {@link mObservers} - and that could cause problems if
11487            // an iterator is used on the ArrayList {@link mObservers}.
11488            // to avoid such problems, just march thru the list in the reverse order.
11489            for (int i = mObservers.size() - 1; i >= 0; i--) {
11490                mObservers.get(i).onChanged();
11491            }
11492        }
11493
11494        public void notifyItemRangeChanged(int positionStart, int itemCount) {
11495            notifyItemRangeChanged(positionStart, itemCount, null);
11496        }
11497
11498        public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
11499            // since onItemRangeChanged() is implemented by the app, it could do anything, including
11500            // removing itself from {@link mObservers} - and that could cause problems if
11501            // an iterator is used on the ArrayList {@link mObservers}.
11502            // to avoid such problems, just march thru the list in the reverse order.
11503            for (int i = mObservers.size() - 1; i >= 0; i--) {
11504                mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
11505            }
11506        }
11507
11508        public void notifyItemRangeInserted(int positionStart, int itemCount) {
11509            // since onItemRangeInserted() is implemented by the app, it could do anything,
11510            // including removing itself from {@link mObservers} - and that could cause problems if
11511            // an iterator is used on the ArrayList {@link mObservers}.
11512            // to avoid such problems, just march thru the list in the reverse order.
11513            for (int i = mObservers.size() - 1; i >= 0; i--) {
11514                mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
11515            }
11516        }
11517
11518        public void notifyItemRangeRemoved(int positionStart, int itemCount) {
11519            // since onItemRangeRemoved() is implemented by the app, it could do anything, including
11520            // removing itself from {@link mObservers} - and that could cause problems if
11521            // an iterator is used on the ArrayList {@link mObservers}.
11522            // to avoid such problems, just march thru the list in the reverse order.
11523            for (int i = mObservers.size() - 1; i >= 0; i--) {
11524                mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
11525            }
11526        }
11527
11528        public void notifyItemMoved(int fromPosition, int toPosition) {
11529            for (int i = mObservers.size() - 1; i >= 0; i--) {
11530                mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
11531            }
11532        }
11533    }
11534
11535    /**
11536     * This is public so that the CREATOR can be accessed on cold launch.
11537     * @hide
11538     */
11539    @RestrictTo(LIBRARY_GROUP)
11540    public static class SavedState extends AbsSavedState {
11541
11542        Parcelable mLayoutState;
11543
11544        /**
11545         * called by CREATOR
11546         */
11547        SavedState(Parcel in, ClassLoader loader) {
11548            super(in, loader);
11549            mLayoutState = in.readParcelable(
11550                    loader != null ? loader : LayoutManager.class.getClassLoader());
11551        }
11552
11553        /**
11554         * Called by onSaveInstanceState
11555         */
11556        SavedState(Parcelable superState) {
11557            super(superState);
11558        }
11559
11560        @Override
11561        public void writeToParcel(Parcel dest, int flags) {
11562            super.writeToParcel(dest, flags);
11563            dest.writeParcelable(mLayoutState, 0);
11564        }
11565
11566        void copyFrom(SavedState other) {
11567            mLayoutState = other.mLayoutState;
11568        }
11569
11570        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
11571            @Override
11572            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
11573                return new SavedState(in, loader);
11574            }
11575
11576            @Override
11577            public SavedState createFromParcel(Parcel in) {
11578                return new SavedState(in, null);
11579            }
11580
11581            @Override
11582            public SavedState[] newArray(int size) {
11583                return new SavedState[size];
11584            }
11585        };
11586    }
11587    /**
11588     * <p>Contains useful information about the current RecyclerView state like target scroll
11589     * position or view focus. State object can also keep arbitrary data, identified by resource
11590     * ids.</p>
11591     * <p>Often times, RecyclerView components will need to pass information between each other.
11592     * To provide a well defined data bus between components, RecyclerView passes the same State
11593     * object to component callbacks and these components can use it to exchange data.</p>
11594     * <p>If you implement custom components, you can use State's put/get/remove methods to pass
11595     * data between your components without needing to manage their lifecycles.</p>
11596     */
11597    public static class State {
11598        static final int STEP_START = 1;
11599        static final int STEP_LAYOUT = 1 << 1;
11600        static final int STEP_ANIMATIONS = 1 << 2;
11601
11602        void assertLayoutStep(int accepted) {
11603            if ((accepted & mLayoutStep) == 0) {
11604                throw new IllegalStateException("Layout state should be one of "
11605                        + Integer.toBinaryString(accepted) + " but it is "
11606                        + Integer.toBinaryString(mLayoutStep));
11607            }
11608        }
11609
11610
11611        /** Owned by SmoothScroller */
11612        private int mTargetPosition = RecyclerView.NO_POSITION;
11613
11614        private SparseArray<Object> mData;
11615
11616        ////////////////////////////////////////////////////////////////////////////////////////////
11617        // Fields below are carried from one layout pass to the next
11618        ////////////////////////////////////////////////////////////////////////////////////////////
11619
11620        /**
11621         * Number of items adapter had in the previous layout.
11622         */
11623        int mPreviousLayoutItemCount = 0;
11624
11625        /**
11626         * Number of items that were NOT laid out but has been deleted from the adapter after the
11627         * previous layout.
11628         */
11629        int mDeletedInvisibleItemCountSincePreviousLayout = 0;
11630
11631        ////////////////////////////////////////////////////////////////////////////////////////////
11632        // Fields below must be updated or cleared before they are used (generally before a pass)
11633        ////////////////////////////////////////////////////////////////////////////////////////////
11634
11635        @IntDef(flag = true, value = {
11636                STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
11637        })
11638        @Retention(RetentionPolicy.SOURCE)
11639        @interface LayoutState {}
11640
11641        @LayoutState
11642        int mLayoutStep = STEP_START;
11643
11644        /**
11645         * Number of items adapter has.
11646         */
11647        int mItemCount = 0;
11648
11649        boolean mStructureChanged = false;
11650
11651        boolean mInPreLayout = false;
11652
11653        boolean mTrackOldChangeHolders = false;
11654
11655        boolean mIsMeasuring = false;
11656
11657        ////////////////////////////////////////////////////////////////////////////////////////////
11658        // Fields below are always reset outside of the pass (or passes) that use them
11659        ////////////////////////////////////////////////////////////////////////////////////////////
11660
11661        boolean mRunSimpleAnimations = false;
11662
11663        boolean mRunPredictiveAnimations = false;
11664
11665        /**
11666         * This data is saved before a layout calculation happens. After the layout is finished,
11667         * if the previously focused view has been replaced with another view for the same item, we
11668         * move the focus to the new item automatically.
11669         */
11670        int mFocusedItemPosition;
11671        long mFocusedItemId;
11672        // when a sub child has focus, record its id and see if we can directly request focus on
11673        // that one instead
11674        int mFocusedSubChildId;
11675
11676        ////////////////////////////////////////////////////////////////////////////////////////////
11677
11678        State reset() {
11679            mTargetPosition = RecyclerView.NO_POSITION;
11680            if (mData != null) {
11681                mData.clear();
11682            }
11683            mItemCount = 0;
11684            mStructureChanged = false;
11685            mIsMeasuring = false;
11686            return this;
11687        }
11688
11689        /**
11690         * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
11691         * prior to any layout passes.
11692         *
11693         * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
11694         * that Recycler#getViewForPosition() can function safely.</p>
11695         */
11696        void prepareForNestedPrefetch(Adapter adapter) {
11697            mLayoutStep = STEP_START;
11698            mItemCount = adapter.getItemCount();
11699            mInPreLayout = false;
11700            mTrackOldChangeHolders = false;
11701            mIsMeasuring = false;
11702        }
11703
11704        /**
11705         * Returns true if the RecyclerView is currently measuring the layout. This value is
11706         * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
11707         * has non-exact measurement specs.
11708         * <p>
11709         * Note that if the LayoutManager supports predictive animations and it is calculating the
11710         * pre-layout step, this value will be {@code false} even if the RecyclerView is in
11711         * {@code onMeasure} call. This is because pre-layout means the previous state of the
11712         * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
11713         * LayoutManager is always guaranteed to receive another call to
11714         * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
11715         *
11716         * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
11717         */
11718        public boolean isMeasuring() {
11719            return mIsMeasuring;
11720        }
11721
11722        /**
11723         * Returns true if
11724         * @return
11725         */
11726        public boolean isPreLayout() {
11727            return mInPreLayout;
11728        }
11729
11730        /**
11731         * Returns whether RecyclerView will run predictive animations in this layout pass
11732         * or not.
11733         *
11734         * @return true if RecyclerView is calculating predictive animations to be run at the end
11735         *         of the layout pass.
11736         */
11737        public boolean willRunPredictiveAnimations() {
11738            return mRunPredictiveAnimations;
11739        }
11740
11741        /**
11742         * Returns whether RecyclerView will run simple animations in this layout pass
11743         * or not.
11744         *
11745         * @return true if RecyclerView is calculating simple animations to be run at the end of
11746         *         the layout pass.
11747         */
11748        public boolean willRunSimpleAnimations() {
11749            return mRunSimpleAnimations;
11750        }
11751
11752        /**
11753         * Removes the mapping from the specified id, if there was any.
11754         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
11755         *                   preserve cross functionality and avoid conflicts.
11756         */
11757        public void remove(int resourceId) {
11758            if (mData == null) {
11759                return;
11760            }
11761            mData.remove(resourceId);
11762        }
11763
11764        /**
11765         * Gets the Object mapped from the specified id, or <code>null</code>
11766         * if no such data exists.
11767         *
11768         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
11769         *                   to
11770         *                   preserve cross functionality and avoid conflicts.
11771         */
11772        @SuppressWarnings("TypeParameterUnusedInFormals")
11773        public <T> T get(int resourceId) {
11774            if (mData == null) {
11775                return null;
11776            }
11777            return (T) mData.get(resourceId);
11778        }
11779
11780        /**
11781         * Adds a mapping from the specified id to the specified value, replacing the previous
11782         * mapping from the specified key if there was one.
11783         *
11784         * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
11785         *                   preserve cross functionality and avoid conflicts.
11786         * @param data       The data you want to associate with the resourceId.
11787         */
11788        public void put(int resourceId, Object data) {
11789            if (mData == null) {
11790                mData = new SparseArray<Object>();
11791            }
11792            mData.put(resourceId, data);
11793        }
11794
11795        /**
11796         * If scroll is triggered to make a certain item visible, this value will return the
11797         * adapter index of that item.
11798         * @return Adapter index of the target item or
11799         * {@link RecyclerView#NO_POSITION} if there is no target
11800         * position.
11801         */
11802        public int getTargetScrollPosition() {
11803            return mTargetPosition;
11804        }
11805
11806        /**
11807         * Returns if current scroll has a target position.
11808         * @return true if scroll is being triggered to make a certain position visible
11809         * @see #getTargetScrollPosition()
11810         */
11811        public boolean hasTargetScrollPosition() {
11812            return mTargetPosition != RecyclerView.NO_POSITION;
11813        }
11814
11815        /**
11816         * @return true if the structure of the data set has changed since the last call to
11817         *         onLayoutChildren, false otherwise
11818         */
11819        public boolean didStructureChange() {
11820            return mStructureChanged;
11821        }
11822
11823        /**
11824         * Returns the total number of items that can be laid out. Note that this number is not
11825         * necessarily equal to the number of items in the adapter, so you should always use this
11826         * number for your position calculations and never access the adapter directly.
11827         * <p>
11828         * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
11829         * data changes on existing Views. These calculations are used to decide which animations
11830         * should be run.
11831         * <p>
11832         * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
11833         * present the correct state to LayoutManager in pre-layout pass.
11834         * <p>
11835         * For example, a newly added item is not included in pre-layout item count because
11836         * pre-layout reflects the contents of the adapter before the item is added. Behind the
11837         * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
11838         * LayoutManager does not know about the new item's existence in pre-layout. The item will
11839         * be available in second layout pass and will be included in the item count. Similar
11840         * adjustments are made for moved and removed items as well.
11841         * <p>
11842         * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
11843         *
11844         * @return The number of items currently available
11845         * @see LayoutManager#getItemCount()
11846         */
11847        public int getItemCount() {
11848            return mInPreLayout
11849                    ? (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout)
11850                    : mItemCount;
11851        }
11852
11853        @Override
11854        public String toString() {
11855            return "State{"
11856                    + "mTargetPosition=" + mTargetPosition
11857                    + ", mData=" + mData
11858                    + ", mItemCount=" + mItemCount
11859                    + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
11860                    + ", mDeletedInvisibleItemCountSincePreviousLayout="
11861                    + mDeletedInvisibleItemCountSincePreviousLayout
11862                    + ", mStructureChanged=" + mStructureChanged
11863                    + ", mInPreLayout=" + mInPreLayout
11864                    + ", mRunSimpleAnimations=" + mRunSimpleAnimations
11865                    + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
11866                    + '}';
11867        }
11868    }
11869
11870    /**
11871     * This class defines the behavior of fling if the developer wishes to handle it.
11872     * <p>
11873     * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
11874     *
11875     * @see #setOnFlingListener(OnFlingListener)
11876     */
11877    public abstract static class OnFlingListener {
11878
11879        /**
11880         * Override this to handle a fling given the velocities in both x and y directions.
11881         * Note that this method will only be called if the associated {@link LayoutManager}
11882         * supports scrolling and the fling is not handled by nested scrolls first.
11883         *
11884         * @param velocityX the fling velocity on the X axis
11885         * @param velocityY the fling velocity on the Y axis
11886         *
11887         * @return true if the fling was handled, false otherwise.
11888         */
11889        public abstract boolean onFling(int velocityX, int velocityY);
11890    }
11891
11892    /**
11893     * Internal listener that manages items after animations finish. This is how items are
11894     * retained (not recycled) during animations, but allowed to be recycled afterwards.
11895     * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
11896     * method on the animator's listener when it is done animating any item.
11897     */
11898    private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
11899
11900        ItemAnimatorRestoreListener() {
11901        }
11902
11903        @Override
11904        public void onAnimationFinished(ViewHolder item) {
11905            item.setIsRecyclable(true);
11906            if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
11907                item.mShadowedHolder = null;
11908            }
11909            // always null this because an OldViewHolder can never become NewViewHolder w/o being
11910            // recycled.
11911            item.mShadowingHolder = null;
11912            if (!item.shouldBeKeptAsChild()) {
11913                if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
11914                    removeDetachedView(item.itemView, false);
11915                }
11916            }
11917        }
11918    }
11919
11920    /**
11921     * This class defines the animations that take place on items as changes are made
11922     * to the adapter.
11923     *
11924     * Subclasses of ItemAnimator can be used to implement custom animations for actions on
11925     * ViewHolder items. The RecyclerView will manage retaining these items while they
11926     * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
11927     * when a ViewHolder's animation is finished. In other words, there must be a matching
11928     * {@link #dispatchAnimationFinished(ViewHolder)} call for each
11929     * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
11930     * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11931     * animateChange()}
11932     * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
11933     * and
11934     * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11935     * animateDisappearance()} call.
11936     *
11937     * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
11938     *
11939     * @see #setItemAnimator(ItemAnimator)
11940     */
11941    @SuppressWarnings("UnusedParameters")
11942    public abstract static class ItemAnimator {
11943
11944        /**
11945         * The Item represented by this ViewHolder is updated.
11946         * <p>
11947         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11948         */
11949        public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
11950
11951        /**
11952         * The Item represented by this ViewHolder is removed from the adapter.
11953         * <p>
11954         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11955         */
11956        public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
11957
11958        /**
11959         * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
11960         * represented by this ViewHolder is invalid.
11961         * <p>
11962         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11963         */
11964        public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
11965
11966        /**
11967         * The position of the Item represented by this ViewHolder has been changed. This flag is
11968         * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
11969         * any adapter change that may have a side effect on this item. (e.g. The item before this
11970         * one has been removed from the Adapter).
11971         * <p>
11972         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11973         */
11974        public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
11975
11976        /**
11977         * This ViewHolder was not laid out but has been added to the layout in pre-layout state
11978         * by the {@link LayoutManager}. This means that the item was already in the Adapter but
11979         * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
11980         * to add new items in pre-layout to specify their virtual location when they are invisible
11981         * (e.g. to specify the item should <i>animate in</i> from below the visible area).
11982         * <p>
11983         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11984         */
11985        public static final int FLAG_APPEARED_IN_PRE_LAYOUT =
11986                ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
11987
11988        /**
11989         * The set of flags that might be passed to
11990         * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11991         */
11992        @IntDef(flag = true, value = {
11993                FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
11994                FLAG_APPEARED_IN_PRE_LAYOUT
11995        })
11996        @Retention(RetentionPolicy.SOURCE)
11997        public @interface AdapterChanges {}
11998        private ItemAnimatorListener mListener = null;
11999        private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
12000                new ArrayList<ItemAnimatorFinishedListener>();
12001
12002        private long mAddDuration = 120;
12003        private long mRemoveDuration = 120;
12004        private long mMoveDuration = 250;
12005        private long mChangeDuration = 250;
12006
12007        /**
12008         * Gets the current duration for which all move animations will run.
12009         *
12010         * @return The current move duration
12011         */
12012        public long getMoveDuration() {
12013            return mMoveDuration;
12014        }
12015
12016        /**
12017         * Sets the duration for which all move animations will run.
12018         *
12019         * @param moveDuration The move duration
12020         */
12021        public void setMoveDuration(long moveDuration) {
12022            mMoveDuration = moveDuration;
12023        }
12024
12025        /**
12026         * Gets the current duration for which all add animations will run.
12027         *
12028         * @return The current add duration
12029         */
12030        public long getAddDuration() {
12031            return mAddDuration;
12032        }
12033
12034        /**
12035         * Sets the duration for which all add animations will run.
12036         *
12037         * @param addDuration The add duration
12038         */
12039        public void setAddDuration(long addDuration) {
12040            mAddDuration = addDuration;
12041        }
12042
12043        /**
12044         * Gets the current duration for which all remove animations will run.
12045         *
12046         * @return The current remove duration
12047         */
12048        public long getRemoveDuration() {
12049            return mRemoveDuration;
12050        }
12051
12052        /**
12053         * Sets the duration for which all remove animations will run.
12054         *
12055         * @param removeDuration The remove duration
12056         */
12057        public void setRemoveDuration(long removeDuration) {
12058            mRemoveDuration = removeDuration;
12059        }
12060
12061        /**
12062         * Gets the current duration for which all change animations will run.
12063         *
12064         * @return The current change duration
12065         */
12066        public long getChangeDuration() {
12067            return mChangeDuration;
12068        }
12069
12070        /**
12071         * Sets the duration for which all change animations will run.
12072         *
12073         * @param changeDuration The change duration
12074         */
12075        public void setChangeDuration(long changeDuration) {
12076            mChangeDuration = changeDuration;
12077        }
12078
12079        /**
12080         * Internal only:
12081         * Sets the listener that must be called when the animator is finished
12082         * animating the item (or immediately if no animation happens). This is set
12083         * internally and is not intended to be set by external code.
12084         *
12085         * @param listener The listener that must be called.
12086         */
12087        void setListener(ItemAnimatorListener listener) {
12088            mListener = listener;
12089        }
12090
12091        /**
12092         * Called by the RecyclerView before the layout begins. Item animator should record
12093         * necessary information about the View before it is potentially rebound, moved or removed.
12094         * <p>
12095         * The data returned from this method will be passed to the related <code>animate**</code>
12096         * methods.
12097         * <p>
12098         * Note that this method may be called after pre-layout phase if LayoutManager adds new
12099         * Views to the layout in pre-layout pass.
12100         * <p>
12101         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
12102         * the View and the adapter change flags.
12103         *
12104         * @param state       The current State of RecyclerView which includes some useful data
12105         *                    about the layout that will be calculated.
12106         * @param viewHolder  The ViewHolder whose information should be recorded.
12107         * @param changeFlags Additional information about what changes happened in the Adapter
12108         *                    about the Item represented by this ViewHolder. For instance, if
12109         *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
12110         * @param payloads    The payload list that was previously passed to
12111         *                    {@link Adapter#notifyItemChanged(int, Object)} or
12112         *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
12113         *
12114         * @return An ItemHolderInfo instance that preserves necessary information about the
12115         * ViewHolder. This object will be passed back to related <code>animate**</code> methods
12116         * after layout is complete.
12117         *
12118         * @see #recordPostLayoutInformation(State, ViewHolder)
12119         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12120         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12121         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12122         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12123         */
12124        public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
12125                @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
12126                @NonNull List<Object> payloads) {
12127            return obtainHolderInfo().setFrom(viewHolder);
12128        }
12129
12130        /**
12131         * Called by the RecyclerView after the layout is complete. Item animator should record
12132         * necessary information about the View's final state.
12133         * <p>
12134         * The data returned from this method will be passed to the related <code>animate**</code>
12135         * methods.
12136         * <p>
12137         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
12138         * the View.
12139         *
12140         * @param state      The current State of RecyclerView which includes some useful data about
12141         *                   the layout that will be calculated.
12142         * @param viewHolder The ViewHolder whose information should be recorded.
12143         *
12144         * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
12145         * This object will be passed back to related <code>animate**</code> methods when
12146         * RecyclerView decides how items should be animated.
12147         *
12148         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
12149         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12150         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12151         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12152         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12153         */
12154        public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
12155                @NonNull ViewHolder viewHolder) {
12156            return obtainHolderInfo().setFrom(viewHolder);
12157        }
12158
12159        /**
12160         * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
12161         * <p>
12162         * This means that the View was a child of the LayoutManager when layout started but has
12163         * been removed by the LayoutManager. It might have been removed from the adapter or simply
12164         * become invisible due to other factors. You can distinguish these two cases by checking
12165         * the change flags that were passed to
12166         * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12167         * <p>
12168         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
12169         * animation callback method which will be called by the RecyclerView depends on the
12170         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
12171         * LayoutManager's decision whether to layout the changed version of a disappearing
12172         * ViewHolder or not. RecyclerView will call
12173         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12174         * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
12175         * returns {@code false} from
12176         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
12177         * LayoutManager lays out a new disappearing view that holds the updated information.
12178         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
12179         * <p>
12180         * If LayoutManager supports predictive animations, it might provide a target disappear
12181         * location for the View by laying it out in that location. When that happens,
12182         * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
12183         * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
12184         * <p>
12185         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12186         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12187         * decides not to animate the view).
12188         *
12189         * @param viewHolder    The ViewHolder which should be animated
12190         * @param preLayoutInfo The information that was returned from
12191         *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12192         * @param postLayoutInfo The information that was returned from
12193         *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
12194         *                       null if the LayoutManager did not layout the item.
12195         *
12196         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12197         * false otherwise.
12198         */
12199        public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
12200                @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
12201
12202        /**
12203         * Called by the RecyclerView when a ViewHolder is added to the layout.
12204         * <p>
12205         * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
12206         * but has  been added by the LayoutManager. It might be newly added to the adapter or
12207         * simply become visible due to other factors.
12208         * <p>
12209         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12210         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12211         * decides not to animate the view).
12212         *
12213         * @param viewHolder     The ViewHolder which should be animated
12214         * @param preLayoutInfo  The information that was returned from
12215         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12216         *                       Might be null if Item was just added to the adapter or
12217         *                       LayoutManager does not support predictive animations or it could
12218         *                       not predict that this ViewHolder will become visible.
12219         * @param postLayoutInfo The information that was returned from {@link
12220         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12221         *
12222         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12223         * false otherwise.
12224         */
12225        public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
12226                @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12227
12228        /**
12229         * Called by the RecyclerView when a ViewHolder is present in both before and after the
12230         * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
12231         * for it or a {@link Adapter#notifyDataSetChanged()} call.
12232         * <p>
12233         * This ViewHolder still represents the same data that it was representing when the layout
12234         * started but its position / size may be changed by the LayoutManager.
12235         * <p>
12236         * If the Item's layout position didn't change, RecyclerView still calls this method because
12237         * it does not track this information (or does not necessarily know that an animation is
12238         * not required). Your ItemAnimator should handle this case and if there is nothing to
12239         * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
12240         * <code>false</code>.
12241         * <p>
12242         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
12243         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
12244         * decides not to animate the view).
12245         *
12246         * @param viewHolder     The ViewHolder which should be animated
12247         * @param preLayoutInfo  The information that was returned from
12248         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12249         * @param postLayoutInfo The information that was returned from {@link
12250         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12251         *
12252         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12253         * false otherwise.
12254         */
12255        public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
12256                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12257
12258        /**
12259         * Called by the RecyclerView when an adapter item is present both before and after the
12260         * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
12261         * for it. This method may also be called when
12262         * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
12263         * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
12264         * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
12265         * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
12266         * called for the new ViewHolder and the old one will be recycled.
12267         * <p>
12268         * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
12269         * a good possibility that item contents didn't really change but it is rebound from the
12270         * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
12271         * screen didn't change and your animator should handle this case as well and avoid creating
12272         * unnecessary animations.
12273         * <p>
12274         * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
12275         * previous presentation of the item as-is and supply a new ViewHolder for the updated
12276         * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
12277         * This is useful if you don't know the contents of the Item and would like
12278         * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
12279         * <p>
12280         * When you are writing a custom item animator for your layout, it might be more performant
12281         * and elegant to re-use the same ViewHolder and animate the content changes manually.
12282         * <p>
12283         * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
12284         * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
12285         * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
12286         * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
12287         * which represent the same Item. In that case, only the new ViewHolder is visible
12288         * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
12289         * <p>
12290         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
12291         * ViewHolder when their animation is complete
12292         * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
12293         * animate the view).
12294         * <p>
12295         *  If oldHolder and newHolder are the same instance, you should call
12296         * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
12297         * <p>
12298         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
12299         * animation callback method which will be called by the RecyclerView depends on the
12300         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
12301         * LayoutManager's decision whether to layout the changed version of a disappearing
12302         * ViewHolder or not. RecyclerView will call
12303         * {@code animateChange} instead of
12304         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12305         * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
12306         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
12307         * LayoutManager lays out a new disappearing view that holds the updated information.
12308         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
12309         *
12310         * @param oldHolder     The ViewHolder before the layout is started, might be the same
12311         *                      instance with newHolder.
12312         * @param newHolder     The ViewHolder after the layout is finished, might be the same
12313         *                      instance with oldHolder.
12314         * @param preLayoutInfo  The information that was returned from
12315         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12316         * @param postLayoutInfo The information that was returned from {@link
12317         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
12318         *
12319         * @return true if a later call to {@link #runPendingAnimations()} is requested,
12320         * false otherwise.
12321         */
12322        public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
12323                @NonNull ViewHolder newHolder,
12324                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
12325
12326        @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
12327            int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
12328            if (viewHolder.isInvalid()) {
12329                return FLAG_INVALIDATED;
12330            }
12331            if ((flags & FLAG_INVALIDATED) == 0) {
12332                final int oldPos = viewHolder.getOldPosition();
12333                final int pos = viewHolder.getAdapterPosition();
12334                if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
12335                    flags |= FLAG_MOVED;
12336                }
12337            }
12338            return flags;
12339        }
12340
12341        /**
12342         * Called when there are pending animations waiting to be started. This state
12343         * is governed by the return values from
12344         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12345         * animateAppearance()},
12346         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12347         * animateChange()}
12348         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12349         * animatePersistence()}, and
12350         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12351         * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
12352         * called later to start the associated animations. runPendingAnimations() will be scheduled
12353         * to be run on the next frame.
12354         */
12355        public abstract void runPendingAnimations();
12356
12357        /**
12358         * Method called when an animation on a view should be ended immediately.
12359         * This could happen when other events, like scrolling, occur, so that
12360         * animating views can be quickly put into their proper end locations.
12361         * Implementations should ensure that any animations running on the item
12362         * are canceled and affected properties are set to their end values.
12363         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
12364         * animation since the animations are effectively done when this method is called.
12365         *
12366         * @param item The item for which an animation should be stopped.
12367         */
12368        public abstract void endAnimation(ViewHolder item);
12369
12370        /**
12371         * Method called when all item animations should be ended immediately.
12372         * This could happen when other events, like scrolling, occur, so that
12373         * animating views can be quickly put into their proper end locations.
12374         * Implementations should ensure that any animations running on any items
12375         * are canceled and affected properties are set to their end values.
12376         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
12377         * animation since the animations are effectively done when this method is called.
12378         */
12379        public abstract void endAnimations();
12380
12381        /**
12382         * Method which returns whether there are any item animations currently running.
12383         * This method can be used to determine whether to delay other actions until
12384         * animations end.
12385         *
12386         * @return true if there are any item animations currently running, false otherwise.
12387         */
12388        public abstract boolean isRunning();
12389
12390        /**
12391         * Method to be called by subclasses when an animation is finished.
12392         * <p>
12393         * For each call RecyclerView makes to
12394         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12395         * animateAppearance()},
12396         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12397         * animatePersistence()}, or
12398         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12399         * animateDisappearance()}, there
12400         * should
12401         * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
12402         * <p>
12403         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12404         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
12405         * and <code>newHolder</code>  (if they are not the same instance).
12406         *
12407         * @param viewHolder The ViewHolder whose animation is finished.
12408         * @see #onAnimationFinished(ViewHolder)
12409         */
12410        public final void dispatchAnimationFinished(ViewHolder viewHolder) {
12411            onAnimationFinished(viewHolder);
12412            if (mListener != null) {
12413                mListener.onAnimationFinished(viewHolder);
12414            }
12415        }
12416
12417        /**
12418         * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
12419         * ItemAnimator.
12420         *
12421         * @param viewHolder The ViewHolder whose animation is finished. There might still be other
12422         *                   animations running on this ViewHolder.
12423         * @see #dispatchAnimationFinished(ViewHolder)
12424         */
12425        public void onAnimationFinished(ViewHolder viewHolder) {
12426        }
12427
12428        /**
12429         * Method to be called by subclasses when an animation is started.
12430         * <p>
12431         * For each call RecyclerView makes to
12432         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12433         * animateAppearance()},
12434         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12435         * animatePersistence()}, or
12436         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
12437         * animateDisappearance()}, there should be a matching
12438         * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
12439         * <p>
12440         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12441         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
12442         * and <code>newHolder</code> (if they are not the same instance).
12443         * <p>
12444         * If your ItemAnimator decides not to animate a ViewHolder, it should call
12445         * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
12446         * {@link #dispatchAnimationStarted(ViewHolder)}.
12447         *
12448         * @param viewHolder The ViewHolder whose animation is starting.
12449         * @see #onAnimationStarted(ViewHolder)
12450         */
12451        public final void dispatchAnimationStarted(ViewHolder viewHolder) {
12452            onAnimationStarted(viewHolder);
12453        }
12454
12455        /**
12456         * Called when a new animation is started on the given ViewHolder.
12457         *
12458         * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
12459         *                   might already be animating and this might be another animation.
12460         * @see #dispatchAnimationStarted(ViewHolder)
12461         */
12462        public void onAnimationStarted(ViewHolder viewHolder) {
12463
12464        }
12465
12466        /**
12467         * Like {@link #isRunning()}, this method returns whether there are any item
12468         * animations currently running. Additionally, the listener passed in will be called
12469         * when there are no item animations running, either immediately (before the method
12470         * returns) if no animations are currently running, or when the currently running
12471         * animations are {@link #dispatchAnimationsFinished() finished}.
12472         *
12473         * <p>Note that the listener is transient - it is either called immediately and not
12474         * stored at all, or stored only until it is called when running animations
12475         * are finished sometime later.</p>
12476         *
12477         * @param listener A listener to be called immediately if no animations are running
12478         * or later when currently-running animations have finished. A null listener is
12479         * equivalent to calling {@link #isRunning()}.
12480         * @return true if there are any item animations currently running, false otherwise.
12481         */
12482        public final boolean isRunning(ItemAnimatorFinishedListener listener) {
12483            boolean running = isRunning();
12484            if (listener != null) {
12485                if (!running) {
12486                    listener.onAnimationsFinished();
12487                } else {
12488                    mFinishedListeners.add(listener);
12489                }
12490            }
12491            return running;
12492        }
12493
12494        /**
12495         * When an item is changed, ItemAnimator can decide whether it wants to re-use
12496         * the same ViewHolder for animations or RecyclerView should create a copy of the
12497         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12498         * <p>
12499         * Note that this method will only be called if the {@link ViewHolder} still has the same
12500         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12501         * both {@link ViewHolder}s in the
12502         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12503         * <p>
12504         * If your application is using change payloads, you can override
12505         * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
12506         *
12507         * @param viewHolder The ViewHolder which represents the changed item's old content.
12508         *
12509         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
12510         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
12511         *         ItemAnimator to animate. Default implementation returns <code>true</code>.
12512         *
12513         * @see #canReuseUpdatedViewHolder(ViewHolder, List)
12514         */
12515        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
12516            return true;
12517        }
12518
12519        /**
12520         * When an item is changed, ItemAnimator can decide whether it wants to re-use
12521         * the same ViewHolder for animations or RecyclerView should create a copy of the
12522         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12523         * <p>
12524         * Note that this method will only be called if the {@link ViewHolder} still has the same
12525         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12526         * both {@link ViewHolder}s in the
12527         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12528         *
12529         * @param viewHolder The ViewHolder which represents the changed item's old content.
12530         * @param payloads A non-null list of merged payloads that were sent with change
12531         *                 notifications. Can be empty if the adapter is invalidated via
12532         *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
12533         *                 payloads will be passed into
12534         *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
12535         *                 method <b>if</b> this method returns <code>true</code>.
12536         *
12537         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
12538         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
12539         *         ItemAnimator to animate. Default implementation calls
12540         *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
12541         *
12542         * @see #canReuseUpdatedViewHolder(ViewHolder)
12543         */
12544        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
12545                @NonNull List<Object> payloads) {
12546            return canReuseUpdatedViewHolder(viewHolder);
12547        }
12548
12549        /**
12550         * This method should be called by ItemAnimator implementations to notify
12551         * any listeners that all pending and active item animations are finished.
12552         */
12553        public final void dispatchAnimationsFinished() {
12554            final int count = mFinishedListeners.size();
12555            for (int i = 0; i < count; ++i) {
12556                mFinishedListeners.get(i).onAnimationsFinished();
12557            }
12558            mFinishedListeners.clear();
12559        }
12560
12561        /**
12562         * Returns a new {@link ItemHolderInfo} which will be used to store information about the
12563         * ViewHolder. This information will later be passed into <code>animate**</code> methods.
12564         * <p>
12565         * You can override this method if you want to extend {@link ItemHolderInfo} and provide
12566         * your own instances.
12567         *
12568         * @return A new {@link ItemHolderInfo}.
12569         */
12570        public ItemHolderInfo obtainHolderInfo() {
12571            return new ItemHolderInfo();
12572        }
12573
12574        /**
12575         * The interface to be implemented by listeners to animation events from this
12576         * ItemAnimator. This is used internally and is not intended for developers to
12577         * create directly.
12578         */
12579        interface ItemAnimatorListener {
12580            void onAnimationFinished(ViewHolder item);
12581        }
12582
12583        /**
12584         * This interface is used to inform listeners when all pending or running animations
12585         * in an ItemAnimator are finished. This can be used, for example, to delay an action
12586         * in a data set until currently-running animations are complete.
12587         *
12588         * @see #isRunning(ItemAnimatorFinishedListener)
12589         */
12590        public interface ItemAnimatorFinishedListener {
12591            /**
12592             * Notifies when all pending or running animations in an ItemAnimator are finished.
12593             */
12594            void onAnimationsFinished();
12595        }
12596
12597        /**
12598         * A simple data structure that holds information about an item's bounds.
12599         * This information is used in calculating item animations. Default implementation of
12600         * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
12601         * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
12602         * structure. You can extend this class if you would like to keep more information about
12603         * the Views.
12604         * <p>
12605         * If you want to provide your own implementation but still use `super` methods to record
12606         * basic information, you can override {@link #obtainHolderInfo()} to provide your own
12607         * instances.
12608         */
12609        public static class ItemHolderInfo {
12610
12611            /**
12612             * The left edge of the View (excluding decorations)
12613             */
12614            public int left;
12615
12616            /**
12617             * The top edge of the View (excluding decorations)
12618             */
12619            public int top;
12620
12621            /**
12622             * The right edge of the View (excluding decorations)
12623             */
12624            public int right;
12625
12626            /**
12627             * The bottom edge of the View (excluding decorations)
12628             */
12629            public int bottom;
12630
12631            /**
12632             * The change flags that were passed to
12633             * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
12634             */
12635            @AdapterChanges
12636            public int changeFlags;
12637
12638            public ItemHolderInfo() {
12639            }
12640
12641            /**
12642             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
12643             * the given ViewHolder. Clears all {@link #changeFlags}.
12644             *
12645             * @param holder The ViewHolder whose bounds should be copied.
12646             * @return This {@link ItemHolderInfo}
12647             */
12648            public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder) {
12649                return setFrom(holder, 0);
12650            }
12651
12652            /**
12653             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
12654             * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
12655             *
12656             * @param holder The ViewHolder whose bounds should be copied.
12657             * @param flags  The adapter change flags that were passed into
12658             *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
12659             *               List)}.
12660             * @return This {@link ItemHolderInfo}
12661             */
12662            public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder,
12663                    @AdapterChanges int flags) {
12664                final View view = holder.itemView;
12665                this.left = view.getLeft();
12666                this.top = view.getTop();
12667                this.right = view.getRight();
12668                this.bottom = view.getBottom();
12669                return this;
12670            }
12671        }
12672    }
12673
12674    @Override
12675    protected int getChildDrawingOrder(int childCount, int i) {
12676        if (mChildDrawingOrderCallback == null) {
12677            return super.getChildDrawingOrder(childCount, i);
12678        } else {
12679            return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
12680        }
12681    }
12682
12683    /**
12684     * A callback interface that can be used to alter the drawing order of RecyclerView children.
12685     * <p>
12686     * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
12687     * that applies to that method also applies to this callback. For example, changing the drawing
12688     * order of two views will not have any effect if their elevation values are different since
12689     * elevation overrides the result of this callback.
12690     */
12691    public interface ChildDrawingOrderCallback {
12692        /**
12693         * Returns the index of the child to draw for this iteration. Override this
12694         * if you want to change the drawing order of children. By default, it
12695         * returns i.
12696         *
12697         * @param i The current iteration.
12698         * @return The index of the child to draw this iteration.
12699         *
12700         * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
12701         */
12702        int onGetChildDrawingOrder(int childCount, int i);
12703    }
12704
12705    private NestedScrollingChildHelper getScrollingChildHelper() {
12706        if (mScrollingChildHelper == null) {
12707            mScrollingChildHelper = new NestedScrollingChildHelper(this);
12708        }
12709        return mScrollingChildHelper;
12710    }
12711}
12712