1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18package android.support.v7.widget;
19
20import android.content.Context;
21import android.database.Observable;
22import android.graphics.Canvas;
23import android.graphics.PointF;
24import android.graphics.Rect;
25import android.os.Build;
26import android.os.Bundle;
27import android.os.Parcel;
28import android.os.Parcelable;
29import android.support.annotation.Nullable;
30import android.support.v4.util.ArrayMap;
31import android.support.v4.view.MotionEventCompat;
32import android.support.v4.view.VelocityTrackerCompat;
33import android.support.v4.view.ViewCompat;
34import android.support.v4.view.ViewConfigurationCompat;
35import android.support.v4.view.accessibility.AccessibilityEventCompat;
36import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
37import android.support.v4.view.accessibility.AccessibilityRecordCompat;
38import android.support.v4.widget.EdgeEffectCompat;
39import android.support.v4.widget.ScrollerCompat;
40import static android.support.v7.widget.AdapterHelper.UpdateOp;
41import static android.support.v7.widget.AdapterHelper.Callback;
42
43import android.util.AttributeSet;
44import android.util.Log;
45import android.util.SparseArray;
46import android.util.SparseIntArray;
47import android.view.FocusFinder;
48import android.view.MotionEvent;
49import android.view.VelocityTracker;
50import android.view.View;
51import android.view.ViewConfiguration;
52import android.view.ViewGroup;
53import android.view.ViewParent;
54import android.view.accessibility.AccessibilityEvent;
55import android.view.accessibility.AccessibilityManager;
56import android.view.animation.Interpolator;
57
58import java.util.ArrayList;
59import java.util.Collections;
60import java.util.List;
61
62/**
63 * A flexible view for providing a limited window into a large data set.
64 *
65 * <h3>Glossary of terms:</h3>
66 *
67 * <ul>
68 *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
69 *     that represent items in a data set.</li>
70 *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
71 *     <li><em>Index:</em> The index of an attached child view as used in a call to
72 *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
73 *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
74 *     to a <em>position</em> within the adapter.</li>
75 *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
76 *     position may be placed in a cache for later reuse to display the same type of data again
77 *     later. This can drastically improve performance by skipping initial layout inflation
78 *     or construction.</li>
79 *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
80 *     state during layout. Scrap views may be reused without becoming fully detached
81 *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
82 *     by the adapter if the view was considered <em>dirty</em>.</li>
83 *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
84 *     being displayed.</li>
85 * </ul>
86 *
87 * <h4>Positions in RecyclerView:</h4>
88 * <p>
89 * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
90 * {@link LayoutManager} to be able to detect data set changes in batches during a layout
91 * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
92 * It also helps with performance because all view bindings happen at the same time and unnecessary
93 * bindings are avoided.
94 * <p>
95 * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
96 * <ul>
97 *     <li>layout position: Position of an item in the latest layout calculation. This is the
98 *     position from the LayoutManager's perspective.</li>
99 *     <li>adapter position: Position of an item in the adapter. This is the position from
100 *     the Adapter's perspective.</li>
101 * </ul>
102 * <p>
103 * These two positions are the same except the time between dispatching <code>adapter.notify*
104 * </code> events and calculating the updated layout.
105 * <p>
106 * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
107 * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
108 * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
109 * last layout calculation. You can rely on these positions to be consistent with what user is
110 * currently seeing on the screen. For example, if you have a list of items on the screen and user
111 * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
112 * is seeing.
113 * <p>
114 * The other set of position related methods are in the form of
115 * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
116 * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
117 * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
118 * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
119 * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
120 * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
121 * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
122 * <code>null</code> results from these methods.
123 * <p>
124 * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
125 * writing an {@link Adapter}, you probably want to use adapter positions.
126 */
127public class RecyclerView extends ViewGroup {
128    private static final String TAG = "RecyclerView";
129
130    private static final boolean DEBUG = false;
131
132    /**
133     * On Kitkat, there is a bug which prevents DisplayList from being invalidated if a View is two
134     * levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by setting
135     * View's visibility to INVISIBLE when View is detached. On Kitkat, Recycler recursively
136     * traverses itemView and invalidates display list for each ViewGroup that matches this
137     * criteria.
138     */
139    private static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 19 ||
140            Build.VERSION.SDK_INT == 20;
141
142    private static final boolean DISPATCH_TEMP_DETACH = false;
143    public static final int HORIZONTAL = 0;
144    public static final int VERTICAL = 1;
145
146    public static final int NO_POSITION = -1;
147    public static final long NO_ID = -1;
148    public static final int INVALID_TYPE = -1;
149
150    /**
151     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
152     * that the RecyclerView should use the standard touch slop for smooth,
153     * continuous scrolling.
154     */
155    public static final int TOUCH_SLOP_DEFAULT = 0;
156
157    /**
158     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
159     * that the RecyclerView should use the standard touch slop for scrolling
160     * widgets that snap to a page or other coarse-grained barrier.
161     */
162    public static final int TOUCH_SLOP_PAGING = 1;
163
164    private static final int MAX_SCROLL_DURATION = 2000;
165
166    private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
167
168    final Recycler mRecycler = new Recycler();
169
170    private SavedState mPendingSavedState;
171
172    AdapterHelper mAdapterHelper;
173
174    ChildHelper mChildHelper;
175
176    // we use this like a set
177    final List<View> mDisappearingViewsInLayoutPass = new ArrayList<View>();
178
179    /**
180     * Prior to L, there is no way to query this variable which is why we override the setter and
181     * track it here.
182     */
183    private boolean mClipToPadding;
184
185    /**
186     * Note: this Runnable is only ever posted if:
187     * 1) We've been through first layout
188     * 2) We know we have a fixed size (mHasFixedSize)
189     * 3) We're attached
190     */
191    private final Runnable mUpdateChildViewsRunnable = new Runnable() {
192        public void run() {
193            if (!mFirstLayoutComplete) {
194                // a layout request will happen, we should not do layout here.
195                return;
196            }
197            if (mDataSetHasChangedAfterLayout) {
198                dispatchLayout();
199            } else if (mAdapterHelper.hasPendingUpdates()) {
200                eatRequestLayout();
201                mAdapterHelper.preProcess();
202                if (!mLayoutRequestEaten) {
203                    // We run this after pre-processing is complete so that ViewHolders have their
204                    // final adapter positions. No need to run it if a layout is already requested.
205                    rebindUpdatedViewHolders();
206                }
207                resumeRequestLayout(true);
208            }
209        }
210    };
211
212    private final Rect mTempRect = new Rect();
213    private Adapter mAdapter;
214    private LayoutManager mLayout;
215    private RecyclerListener mRecyclerListener;
216    private final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<ItemDecoration>();
217    private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
218            new ArrayList<OnItemTouchListener>();
219    private OnItemTouchListener mActiveOnItemTouchListener;
220    private boolean mIsAttached;
221    private boolean mHasFixedSize;
222    private boolean mFirstLayoutComplete;
223    private boolean mEatRequestLayout;
224    private boolean mLayoutRequestEaten;
225    private boolean mAdapterUpdateDuringMeasure;
226    private final boolean mPostUpdatesOnAnimation;
227    private final AccessibilityManager mAccessibilityManager;
228
229    /**
230     * Set to true when an adapter data set changed notification is received.
231     * In that case, we cannot run any animations since we don't know what happened.
232     */
233    private boolean mDataSetHasChangedAfterLayout = false;
234
235    /**
236     * This variable is set to true during a dispatchLayout and/or scroll.
237     * Some methods should not be called during these periods (e.g. adapter data change).
238     * Doing so will create hard to find bugs so we better check it and throw an exception.
239     *
240     * @see #assertInLayoutOrScroll(String)
241     * @see #assertNotInLayoutOrScroll(String)
242     */
243    private boolean mRunningLayoutOrScroll = false;
244
245    private EdgeEffectCompat mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
246
247    ItemAnimator mItemAnimator = new DefaultItemAnimator();
248
249    private static final int INVALID_POINTER = -1;
250
251    /**
252     * The RecyclerView is not currently scrolling.
253     * @see #getScrollState()
254     */
255    public static final int SCROLL_STATE_IDLE = 0;
256
257    /**
258     * The RecyclerView is currently being dragged by outside input such as user touch input.
259     * @see #getScrollState()
260     */
261    public static final int SCROLL_STATE_DRAGGING = 1;
262
263    /**
264     * The RecyclerView is currently animating to a final position while not under
265     * outside control.
266     * @see #getScrollState()
267     */
268    public static final int SCROLL_STATE_SETTLING = 2;
269
270    // Touch/scrolling handling
271
272    private int mScrollState = SCROLL_STATE_IDLE;
273    private int mScrollPointerId = INVALID_POINTER;
274    private VelocityTracker mVelocityTracker;
275    private int mInitialTouchX;
276    private int mInitialTouchY;
277    private int mLastTouchX;
278    private int mLastTouchY;
279    private int mTouchSlop;
280    private final int mMinFlingVelocity;
281    private final int mMaxFlingVelocity;
282
283    private final ViewFlinger mViewFlinger = new ViewFlinger();
284
285    final State mState = new State();
286
287    private OnScrollListener mScrollListener;
288
289    // For use in item animations
290    boolean mItemsAddedOrRemoved = false;
291    boolean mItemsChanged = false;
292    private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
293            new ItemAnimatorRestoreListener();
294    private boolean mPostedAnimatorRunner = false;
295    private RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
296
297    // simple array to keep min and max child position during a layout calculation
298    // preserved not to create a new one in each layout pass
299    private final int[] mMinMaxLayoutPositions = new int[2];
300
301    private Runnable mItemAnimatorRunner = new Runnable() {
302        @Override
303        public void run() {
304            if (mItemAnimator != null) {
305                mItemAnimator.runPendingAnimations();
306            }
307            mPostedAnimatorRunner = false;
308        }
309    };
310
311    private static final Interpolator sQuinticInterpolator = new Interpolator() {
312        public float getInterpolation(float t) {
313            t -= 1.0f;
314            return t * t * t * t * t + 1.0f;
315        }
316    };
317
318    public RecyclerView(Context context) {
319        this(context, null);
320    }
321
322    public RecyclerView(Context context, AttributeSet attrs) {
323        this(context, attrs, 0);
324    }
325
326    public RecyclerView(Context context, AttributeSet attrs, int defStyle) {
327        super(context, attrs, defStyle);
328
329        final int version = Build.VERSION.SDK_INT;
330        mPostUpdatesOnAnimation = version >= 16;
331
332        final ViewConfiguration vc = ViewConfiguration.get(context);
333        mTouchSlop = vc.getScaledTouchSlop();
334        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
335        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
336        setWillNotDraw(ViewCompat.getOverScrollMode(this) == ViewCompat.OVER_SCROLL_NEVER);
337
338        mItemAnimator.setListener(mItemAnimatorListener);
339        initAdapterManager();
340        initChildrenHelper();
341        // If not explicitly specified this view is important for accessibility.
342        if (ViewCompat.getImportantForAccessibility(this)
343                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
344            ViewCompat.setImportantForAccessibility(this,
345                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
346        }
347        mAccessibilityManager = (AccessibilityManager) getContext()
348                .getSystemService(Context.ACCESSIBILITY_SERVICE);
349        setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
350    }
351
352    /**
353     * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
354     * @return An instance of AccessibilityDelegateCompat used by RecyclerView
355     */
356    public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
357        return mAccessibilityDelegate;
358    }
359
360    /**
361     * Sets the accessibility delegate compatibility implementation used by RecyclerView.
362     * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
363     */
364    public void setAccessibilityDelegateCompat(
365            RecyclerViewAccessibilityDelegate accessibilityDelegate) {
366        mAccessibilityDelegate = accessibilityDelegate;
367        ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
368    }
369
370    private void initChildrenHelper() {
371        mChildHelper = new ChildHelper(new ChildHelper.Callback() {
372            @Override
373            public int getChildCount() {
374                return RecyclerView.this.getChildCount();
375            }
376
377            @Override
378            public void addView(View child, int index) {
379                RecyclerView.this.addView(child, index);
380                dispatchChildAttached(child);
381            }
382
383            @Override
384            public int indexOfChild(View view) {
385                return RecyclerView.this.indexOfChild(view);
386            }
387
388            @Override
389            public void removeViewAt(int index) {
390                final View child = RecyclerView.this.getChildAt(index);
391                if (child != null) {
392                    dispatchChildDetached(child);
393                }
394                RecyclerView.this.removeViewAt(index);
395            }
396
397            @Override
398            public View getChildAt(int offset) {
399                return RecyclerView.this.getChildAt(offset);
400            }
401
402            @Override
403            public void removeAllViews() {
404                final int count = getChildCount();
405                for (int i = 0; i < count; i ++) {
406                    dispatchChildDetached(getChildAt(i));
407                }
408                RecyclerView.this.removeAllViews();
409            }
410
411            @Override
412            public ViewHolder getChildViewHolder(View view) {
413                return getChildViewHolderInt(view);
414            }
415
416            @Override
417            public void attachViewToParent(View child, int index,
418                    ViewGroup.LayoutParams layoutParams) {
419                final ViewHolder vh = getChildViewHolderInt(child);
420                if (vh != null) {
421                    if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
422                        throw new IllegalArgumentException("Called attach on a child which is not"
423                                + " detached: " + vh);
424                    }
425                    if (DEBUG) {
426                        Log.d(TAG, "reAttach " + vh);
427                    }
428                    vh.clearTmpDetachFlag();
429                }
430                RecyclerView.this.attachViewToParent(child, index, layoutParams);
431            }
432
433            @Override
434            public void detachViewFromParent(int offset) {
435                final View view = getChildAt(offset);
436                if (view != null) {
437                    final ViewHolder vh = getChildViewHolderInt(view);
438                    if (vh != null) {
439                        if (vh.isTmpDetached() && !vh.shouldIgnore()) {
440                            throw new IllegalArgumentException("called detach on an already"
441                                    + " detached child " + vh);
442                        }
443                        if (DEBUG) {
444                            Log.d(TAG, "tmpDetach " + vh);
445                        }
446                        vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
447                    }
448                }
449                RecyclerView.this.detachViewFromParent(offset);
450            }
451        });
452    }
453
454    void initAdapterManager() {
455        mAdapterHelper = new AdapterHelper(new Callback() {
456            @Override
457            public ViewHolder findViewHolder(int position) {
458                return findViewHolderForPosition(position, true);
459            }
460
461            @Override
462            public void offsetPositionsForRemovingInvisible(int start, int count) {
463                offsetPositionRecordsForRemove(start, count, true);
464                mItemsAddedOrRemoved = true;
465                mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
466            }
467
468            @Override
469            public void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount) {
470                offsetPositionRecordsForRemove(positionStart, itemCount, false);
471                mItemsAddedOrRemoved = true;
472            }
473
474            @Override
475            public void markViewHoldersUpdated(int positionStart, int itemCount) {
476                viewRangeUpdate(positionStart, itemCount);
477                mItemsChanged = true;
478            }
479
480            @Override
481            public void onDispatchFirstPass(UpdateOp op) {
482                dispatchUpdate(op);
483            }
484
485            void dispatchUpdate(UpdateOp op) {
486                switch (op.cmd) {
487                    case UpdateOp.ADD:
488                        mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
489                        break;
490                    case UpdateOp.REMOVE:
491                        mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
492                        break;
493                    case UpdateOp.UPDATE:
494                        mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount);
495                        break;
496                    case UpdateOp.MOVE:
497                        mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
498                        break;
499                }
500            }
501
502            @Override
503            public void onDispatchSecondPass(UpdateOp op) {
504                dispatchUpdate(op);
505            }
506
507            @Override
508            public void offsetPositionsForAdd(int positionStart, int itemCount) {
509                offsetPositionRecordsForInsert(positionStart, itemCount);
510                mItemsAddedOrRemoved = true;
511            }
512
513            @Override
514            public void offsetPositionsForMove(int from, int to) {
515                offsetPositionRecordsForMove(from, to);
516                // should we create mItemsMoved ?
517                mItemsAddedOrRemoved = true;
518            }
519        });
520    }
521
522    /**
523     * RecyclerView can perform several optimizations if it can know in advance that changes in
524     * adapter content cannot change the size of the RecyclerView itself.
525     * If your use of RecyclerView falls into this category, set this to true.
526     *
527     * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
528     */
529    public void setHasFixedSize(boolean hasFixedSize) {
530        mHasFixedSize = hasFixedSize;
531    }
532
533    /**
534     * @return true if the app has specified that changes in adapter content cannot change
535     * the size of the RecyclerView itself.
536     */
537    public boolean hasFixedSize() {
538        return mHasFixedSize;
539    }
540
541    @Override
542    public void setClipToPadding(boolean clipToPadding) {
543        if (clipToPadding != mClipToPadding) {
544            invalidateGlows();
545        }
546        mClipToPadding = clipToPadding;
547        super.setClipToPadding(clipToPadding);
548        if (mFirstLayoutComplete) {
549            requestLayout();
550        }
551    }
552
553    /**
554     * Configure the scrolling touch slop for a specific use case.
555     *
556     * Set up the RecyclerView's scrolling motion threshold based on common usages.
557     * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
558     *
559     * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
560     *                     the intended usage of this RecyclerView
561     */
562    public void setScrollingTouchSlop(int slopConstant) {
563        final ViewConfiguration vc = ViewConfiguration.get(getContext());
564        switch (slopConstant) {
565            default:
566                Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
567                      + slopConstant + "; using default value");
568                // fall-through
569            case TOUCH_SLOP_DEFAULT:
570                mTouchSlop = vc.getScaledTouchSlop();
571                break;
572
573            case TOUCH_SLOP_PAGING:
574                mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(vc);
575                break;
576        }
577    }
578
579    /**
580     * Swaps the current adapter with the provided one. It is similar to
581     * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
582     * {@link ViewHolder} and does not clear the RecycledViewPool.
583     * <p>
584     * Note that it still calls onAdapterChanged callbacks.
585     *
586     * @param adapter The new adapter to set, or null to set no adapter.
587     * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
588     *                                      Views. If adapters have stable ids and/or you want to
589     *                                      animate the disappearing views, you may prefer to set
590     *                                      this to false.
591     * @see #setAdapter(Adapter)
592     */
593    public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
594        setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
595        setDataSetChangedAfterLayout();
596        requestLayout();
597    }
598    /**
599     * Set a new adapter to provide child views on demand.
600     * <p>
601     * When adapter is changed, all existing views are recycled back to the pool. If the pool has
602     * only one adapter, it will be cleared.
603     *
604     * @param adapter The new adapter to set, or null to set no adapter.
605     * @see #swapAdapter(Adapter, boolean)
606     */
607    public void setAdapter(Adapter adapter) {
608        setAdapterInternal(adapter, false, true);
609        requestLayout();
610    }
611
612    /**
613     * Replaces the current adapter with the new one and triggers listeners.
614     * @param adapter The new adapter
615     * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
616     *                               item types with the current adapter (helps us avoid cache
617     *                               invalidation).
618     * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
619     *                               compatibleWithPrevious is false, this parameter is ignored.
620     */
621    private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
622            boolean removeAndRecycleViews) {
623        if (mAdapter != null) {
624            mAdapter.unregisterAdapterDataObserver(mObserver);
625            mAdapter.onDetachedFromRecyclerView(this);
626        }
627        if (!compatibleWithPrevious || removeAndRecycleViews) {
628            // end all running animations
629            if (mItemAnimator != null) {
630                mItemAnimator.endAnimations();
631            }
632            // Since animations are ended, mLayout.children should be equal to
633            // recyclerView.children. This may not be true if item animator's end does not work as
634            // expected. (e.g. not release children instantly). It is safer to use mLayout's child
635            // count.
636            if (mLayout != null) {
637                mLayout.removeAndRecycleAllViews(mRecycler);
638                mLayout.removeAndRecycleScrapInt(mRecycler);
639            }
640            // we should clear it here before adapters are swapped to ensure correct callbacks.
641            mRecycler.clear();
642        }
643        mAdapterHelper.reset();
644        final Adapter oldAdapter = mAdapter;
645        mAdapter = adapter;
646        if (adapter != null) {
647            adapter.registerAdapterDataObserver(mObserver);
648            adapter.onAttachedToRecyclerView(this);
649        }
650        if (mLayout != null) {
651            mLayout.onAdapterChanged(oldAdapter, mAdapter);
652        }
653        mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
654        mState.mStructureChanged = true;
655        markKnownViewsInvalid();
656    }
657
658    /**
659     * Retrieves the previously set adapter or null if no adapter is set.
660     *
661     * @return The previously set adapter
662     * @see #setAdapter(Adapter)
663     */
664    public Adapter getAdapter() {
665        return mAdapter;
666    }
667
668    /**
669     * Register a listener that will be notified whenever a child view is recycled.
670     *
671     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
672     * that a child view is no longer needed. If an application associates expensive
673     * or heavyweight data with item views, this may be a good place to release
674     * or free those resources.</p>
675     *
676     * @param listener Listener to register, or null to clear
677     */
678    public void setRecyclerListener(RecyclerListener listener) {
679        mRecyclerListener = listener;
680    }
681
682    /**
683     * Set the {@link LayoutManager} that this RecyclerView will use.
684     *
685     * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
686     * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
687     * layout arrangements for child views. These arrangements are controlled by the
688     * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
689     *
690     * <p>Several default strategies are provided for common uses such as lists and grids.</p>
691     *
692     * @param layout LayoutManager to use
693     */
694    public void setLayoutManager(LayoutManager layout) {
695        if (layout == mLayout) {
696            return;
697        }
698        // TODO We should do this switch a dispachLayout pass and animate children. There is a good
699        // chance that LayoutManagers will re-use views.
700        if (mLayout != null) {
701            if (mIsAttached) {
702                mLayout.onDetachedFromWindow(this, mRecycler);
703            }
704            mLayout.setRecyclerView(null);
705        }
706        mRecycler.clear();
707        mChildHelper.removeAllViewsUnfiltered();
708        mLayout = layout;
709        if (layout != null) {
710            if (layout.mRecyclerView != null) {
711                throw new IllegalArgumentException("LayoutManager " + layout +
712                        " is already attached to a RecyclerView: " + layout.mRecyclerView);
713            }
714            mLayout.setRecyclerView(this);
715            if (mIsAttached) {
716                mLayout.onAttachedToWindow(this);
717            }
718        }
719        requestLayout();
720    }
721
722    @Override
723    protected Parcelable onSaveInstanceState() {
724        SavedState state = new SavedState(super.onSaveInstanceState());
725        if (mPendingSavedState != null) {
726            state.copyFrom(mPendingSavedState);
727        } else if (mLayout != null) {
728            state.mLayoutState = mLayout.onSaveInstanceState();
729        } else {
730            state.mLayoutState = null;
731        }
732
733        return state;
734    }
735
736    @Override
737    protected void onRestoreInstanceState(Parcelable state) {
738        mPendingSavedState = (SavedState) state;
739        super.onRestoreInstanceState(mPendingSavedState.getSuperState());
740        if (mLayout != null && mPendingSavedState.mLayoutState != null) {
741            mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
742        }
743    }
744
745    /**
746     * Adds a view to the animatingViews list.
747     * mAnimatingViews holds the child views that are currently being kept around
748     * purely for the purpose of being animated out of view. They are drawn as a regular
749     * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
750     * as they are managed separately from the regular child views.
751     * @param viewHolder The ViewHolder to be removed
752     */
753    private void addAnimatingView(ViewHolder viewHolder) {
754        final View view = viewHolder.itemView;
755        final boolean alreadyParented = view.getParent() == this;
756        mRecycler.unscrapView(getChildViewHolder(view));
757        if (viewHolder.isTmpDetached()) {
758            // re-attach
759            mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
760        } else if(!alreadyParented) {
761            mChildHelper.addView(view, true);
762        } else {
763            mChildHelper.hide(view);
764        }
765    }
766
767    /**
768     * Removes a view from the animatingViews list.
769     * @param view The view to be removed
770     * @see #addAnimatingView(RecyclerView.ViewHolder)
771     * @return true if an animating view is removed
772     */
773    private boolean removeAnimatingView(View view) {
774        eatRequestLayout();
775        final boolean removed = mChildHelper.removeViewIfHidden(view);
776        if (removed) {
777            final ViewHolder viewHolder = getChildViewHolderInt(view);
778            mRecycler.unscrapView(viewHolder);
779            mRecycler.recycleViewHolderInternal(viewHolder);
780            if (DEBUG) {
781                Log.d(TAG, "after removing animated view: " + view + ", " + this);
782            }
783        }
784        resumeRequestLayout(false);
785        return removed;
786    }
787
788    /**
789     * Return the {@link LayoutManager} currently responsible for
790     * layout policy for this RecyclerView.
791     *
792     * @return The currently bound LayoutManager
793     */
794    public LayoutManager getLayoutManager() {
795        return mLayout;
796    }
797
798    /**
799     * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
800     * if no pool is set for this view a new one will be created. See
801     * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
802     *
803     * @return The pool used to store recycled item views for reuse.
804     * @see #setRecycledViewPool(RecycledViewPool)
805     */
806    public RecycledViewPool getRecycledViewPool() {
807        return mRecycler.getRecycledViewPool();
808    }
809
810    /**
811     * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
812     * This can be useful if you have multiple RecyclerViews with adapters that use the same
813     * view types, for example if you have several data sets with the same kinds of item views
814     * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
815     *
816     * @param pool Pool to set. If this parameter is null a new pool will be created and used.
817     */
818    public void setRecycledViewPool(RecycledViewPool pool) {
819        mRecycler.setRecycledViewPool(pool);
820    }
821
822    /**
823     * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
824     *
825     * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
826     *
827     * @see {@link ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)}
828     */
829    public void setViewCacheExtension(ViewCacheExtension extension) {
830        mRecycler.setViewCacheExtension(extension);
831    }
832
833    /**
834     * Set the number of offscreen views to retain before adding them to the potentially shared
835     * {@link #getRecycledViewPool() recycled view pool}.
836     *
837     * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
838     * a LayoutManager to reuse those views unmodified without needing to return to the adapter
839     * to rebind them.</p>
840     *
841     * @param size Number of views to cache offscreen before returning them to the general
842     *             recycled view pool
843     */
844    public void setItemViewCacheSize(int size) {
845        mRecycler.setViewCacheSize(size);
846    }
847
848    /**
849     * Return the current scrolling state of the RecyclerView.
850     *
851     * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
852     * {@link #SCROLL_STATE_SETTLING}
853     */
854    public int getScrollState() {
855        return mScrollState;
856    }
857
858    private void setScrollState(int state) {
859        if (state == mScrollState) {
860            return;
861        }
862        if (DEBUG) {
863            Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState, new Exception());
864        }
865        mScrollState = state;
866        if (state != SCROLL_STATE_SETTLING) {
867            stopScrollersInternal();
868        }
869        if (mScrollListener != null) {
870            mScrollListener.onScrollStateChanged(this, state);
871        }
872        if (mLayout != null) {
873            mLayout.onScrollStateChanged(state);
874        }
875    }
876
877    /**
878     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
879     * affect both measurement and drawing of individual item views.
880     *
881     * <p>Item decorations are ordered. Decorations placed earlier in the list will
882     * be run/queried/drawn first for their effects on item views. Padding added to views
883     * will be nested; a padding added by an earlier decoration will mean further
884     * item decorations in the list will be asked to draw/pad within the previous decoration's
885     * given area.</p>
886     *
887     * @param decor Decoration to add
888     * @param index Position in the decoration chain to insert this decoration at. If this value
889     *              is negative the decoration will be added at the end.
890     */
891    public void addItemDecoration(ItemDecoration decor, int index) {
892        if (mLayout != null) {
893            mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
894                    + " layout");
895        }
896        if (mItemDecorations.isEmpty()) {
897            setWillNotDraw(false);
898        }
899        if (index < 0) {
900            mItemDecorations.add(decor);
901        } else {
902            mItemDecorations.add(index, decor);
903        }
904        markItemDecorInsetsDirty();
905        requestLayout();
906    }
907
908    /**
909     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
910     * affect both measurement and drawing of individual item views.
911     *
912     * <p>Item decorations are ordered. Decorations placed earlier in the list will
913     * be run/queried/drawn first for their effects on item views. Padding added to views
914     * will be nested; a padding added by an earlier decoration will mean further
915     * item decorations in the list will be asked to draw/pad within the previous decoration's
916     * given area.</p>
917     *
918     * @param decor Decoration to add
919     */
920    public void addItemDecoration(ItemDecoration decor) {
921        addItemDecoration(decor, -1);
922    }
923
924    /**
925     * Remove an {@link ItemDecoration} from this RecyclerView.
926     *
927     * <p>The given decoration will no longer impact the measurement and drawing of
928     * item views.</p>
929     *
930     * @param decor Decoration to remove
931     * @see #addItemDecoration(ItemDecoration)
932     */
933    public void removeItemDecoration(ItemDecoration decor) {
934        if (mLayout != null) {
935            mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
936                    + " layout");
937        }
938        mItemDecorations.remove(decor);
939        if (mItemDecorations.isEmpty()) {
940            setWillNotDraw(ViewCompat.getOverScrollMode(this) == ViewCompat.OVER_SCROLL_NEVER);
941        }
942        markItemDecorInsetsDirty();
943        requestLayout();
944    }
945
946    /**
947     * Set a listener that will be notified of any changes in scroll state or position.
948     *
949     * @param listener Listener to set or null to clear
950     */
951    public void setOnScrollListener(OnScrollListener listener) {
952        mScrollListener = listener;
953    }
954
955    /**
956     * Convenience method to scroll to a certain position.
957     *
958     * RecyclerView does not implement scrolling logic, rather forwards the call to
959     * {@link android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
960     * @param position Scroll to this adapter position
961     * @see android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)
962     */
963    public void scrollToPosition(int position) {
964        stopScroll();
965        if (mLayout == null) {
966            Log.e(TAG, "Cannot scroll to position a LayoutManager set. " +
967                    "Call setLayoutManager with a non-null argument.");
968            return;
969        }
970        mLayout.scrollToPosition(position);
971        awakenScrollBars();
972    }
973
974    /**
975     * Starts a smooth scroll to an adapter position.
976     * <p>
977     * To support smooth scrolling, you must override
978     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
979     * {@link SmoothScroller}.
980     * <p>
981     * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
982     * provide a custom smooth scroll logic, override
983     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
984     * LayoutManager.
985     *
986     * @param position The adapter position to scroll to
987     * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
988     */
989    public void smoothScrollToPosition(int position) {
990        if (mLayout == null) {
991            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
992                    "Call setLayoutManager with a non-null argument.");
993            return;
994        }
995        mLayout.smoothScrollToPosition(this, mState, position);
996    }
997
998    @Override
999    public void scrollTo(int x, int y) {
1000        throw new UnsupportedOperationException(
1001                "RecyclerView does not support scrolling to an absolute position.");
1002    }
1003
1004    @Override
1005    public void scrollBy(int x, int y) {
1006        if (mLayout == null) {
1007            Log.e(TAG, "Cannot scroll without a LayoutManager set. " +
1008                    "Call setLayoutManager with a non-null argument.");
1009            return;
1010        }
1011        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1012        final boolean canScrollVertical = mLayout.canScrollVertically();
1013        if (canScrollHorizontal || canScrollVertical) {
1014            scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0);
1015        }
1016    }
1017
1018    /**
1019     * Helper method reflect data changes to the state.
1020     * <p>
1021     * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
1022     * but data actually changed.
1023     * <p>
1024     * This method consumes all deferred changes to avoid that case.
1025     */
1026    private void consumePendingUpdateOperations() {
1027        mUpdateChildViewsRunnable.run();
1028    }
1029
1030    /**
1031     * Does not perform bounds checking. Used by internal methods that have already validated input.
1032     *
1033     * @return Whether any scroll was consumed in either direction.
1034     */
1035    boolean scrollByInternal(int x, int y) {
1036        int overscrollX = 0, overscrollY = 0;
1037        int hresult = 0, vresult = 0;
1038        consumePendingUpdateOperations();
1039        if (mAdapter != null) {
1040            eatRequestLayout();
1041            mRunningLayoutOrScroll = true;
1042            if (x != 0) {
1043                hresult = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
1044                overscrollX = x - hresult;
1045            }
1046            if (y != 0) {
1047                vresult = mLayout.scrollVerticallyBy(y, mRecycler, mState);
1048                overscrollY = y - vresult;
1049            }
1050            if (supportsChangeAnimations()) {
1051                // Fix up shadow views used by changing animations
1052                int count = mChildHelper.getChildCount();
1053                for (int i = 0; i < count; i++) {
1054                    View view = mChildHelper.getChildAt(i);
1055                    ViewHolder holder = getChildViewHolder(view);
1056                    if (holder != null && holder.mShadowingHolder != null) {
1057                        ViewHolder shadowingHolder = holder.mShadowingHolder;
1058                        View shadowingView = shadowingHolder != null ? shadowingHolder.itemView : null;
1059                        if (shadowingView != null) {
1060                            int left = view.getLeft();
1061                            int top = view.getTop();
1062                            if (left != shadowingView.getLeft() || top != shadowingView.getTop()) {
1063                                shadowingView.layout(left, top,
1064                                        left + shadowingView.getWidth(),
1065                                        top + shadowingView.getHeight());
1066                            }
1067                        }
1068                    }
1069                }
1070            }
1071            mRunningLayoutOrScroll = false;
1072            resumeRequestLayout(false);
1073        }
1074        if (!mItemDecorations.isEmpty()) {
1075            invalidate();
1076        }
1077        if (ViewCompat.getOverScrollMode(this) != ViewCompat.OVER_SCROLL_NEVER) {
1078            considerReleasingGlowsOnScroll(x, y);
1079            pullGlows(overscrollX, overscrollY);
1080        }
1081        if (hresult != 0 || vresult != 0) {
1082            notifyOnScrolled(hresult, vresult);
1083        }
1084        if (!awakenScrollBars()) {
1085            invalidate();
1086        }
1087        return hresult != 0 || vresult != 0;
1088    }
1089
1090    /**
1091     * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
1092     * range. This value is used to compute the length of the thumb within the scrollbar's track.
1093     * </p>
1094     *
1095     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1096     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
1097     *
1098     * <p>Default implementation returns 0.</p>
1099     *
1100     * <p>If you want to support scroll bars, override
1101     * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
1102     * LayoutManager. </p>
1103     *
1104     * @return The horizontal offset of the scrollbar's thumb
1105     * @see android.support.v7.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset
1106     * (RecyclerView.Adapter)
1107     */
1108    @Override
1109    protected int computeHorizontalScrollOffset() {
1110        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState)
1111                : 0;
1112    }
1113
1114    /**
1115     * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
1116     * horizontal range. This value is used to compute the length of the thumb within the
1117     * scrollbar's track.</p>
1118     *
1119     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1120     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
1121     *
1122     * <p>Default implementation returns 0.</p>
1123     *
1124     * <p>If you want to support scroll bars, override
1125     * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
1126     * LayoutManager.</p>
1127     *
1128     * @return The horizontal extent of the scrollbar's thumb
1129     * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
1130     */
1131    @Override
1132    protected int computeHorizontalScrollExtent() {
1133        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
1134    }
1135
1136    /**
1137     * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
1138     *
1139     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1140     * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
1141     *
1142     * <p>Default implementation returns 0.</p>
1143     *
1144     * <p>If you want to support scroll bars, override
1145     * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
1146     * LayoutManager.</p>
1147     *
1148     * @return The total horizontal range represented by the vertical scrollbar
1149     * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
1150     */
1151    @Override
1152    protected int computeHorizontalScrollRange() {
1153        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
1154    }
1155
1156    /**
1157     * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
1158     * This value is used to compute the length of the thumb within the scrollbar's track. </p>
1159     *
1160     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1161     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
1162     *
1163     * <p>Default implementation returns 0.</p>
1164     *
1165     * <p>If you want to support scroll bars, override
1166     * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
1167     * LayoutManager.</p>
1168     *
1169     * @return The vertical offset of the scrollbar's thumb
1170     * @see android.support.v7.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset
1171     * (RecyclerView.Adapter)
1172     */
1173    @Override
1174    protected int computeVerticalScrollOffset() {
1175        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
1176    }
1177
1178    /**
1179     * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
1180     * This value is used to compute the length of the thumb within the scrollbar's track.</p>
1181     *
1182     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1183     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
1184     *
1185     * <p>Default implementation returns 0.</p>
1186     *
1187     * <p>If you want to support scroll bars, override
1188     * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
1189     * LayoutManager.</p>
1190     *
1191     * @return The vertical extent of the scrollbar's thumb
1192     * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
1193     */
1194    @Override
1195    protected int computeVerticalScrollExtent() {
1196        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
1197    }
1198
1199    /**
1200     * <p>Compute the vertical range that the vertical scrollbar represents.</p>
1201     *
1202     * <p>The range is expressed in arbitrary units that must be the same as the units used by
1203     * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
1204     *
1205     * <p>Default implementation returns 0.</p>
1206     *
1207     * <p>If you want to support scroll bars, override
1208     * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
1209     * LayoutManager.</p>
1210     *
1211     * @return The total vertical range represented by the vertical scrollbar
1212     * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
1213     */
1214    @Override
1215    protected int computeVerticalScrollRange() {
1216        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
1217    }
1218
1219
1220    void eatRequestLayout() {
1221        if (!mEatRequestLayout) {
1222            mEatRequestLayout = true;
1223            mLayoutRequestEaten = false;
1224        }
1225    }
1226
1227    void resumeRequestLayout(boolean performLayoutChildren) {
1228        if (mEatRequestLayout) {
1229            if (performLayoutChildren && mLayoutRequestEaten &&
1230                    mLayout != null && mAdapter != null) {
1231                dispatchLayout();
1232            }
1233            mEatRequestLayout = false;
1234            mLayoutRequestEaten = false;
1235        }
1236    }
1237
1238    /**
1239     * Animate a scroll by the given amount of pixels along either axis.
1240     *
1241     * @param dx Pixels to scroll horizontally
1242     * @param dy Pixels to scroll vertically
1243     */
1244    public void smoothScrollBy(int dx, int dy) {
1245        if (mLayout == null) {
1246            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
1247                    "Call setLayoutManager with a non-null argument.");
1248            return;
1249        }
1250        if (!mLayout.canScrollHorizontally()) {
1251            dx = 0;
1252        }
1253        if (!mLayout.canScrollVertically()) {
1254            dy = 0;
1255        }
1256        if (dx != 0 || dy != 0) {
1257            mViewFlinger.smoothScrollBy(dx, dy);
1258        }
1259    }
1260
1261    /**
1262     * Begin a standard fling with an initial velocity along each axis in pixels per second.
1263     * If the velocity given is below the system-defined minimum this method will return false
1264     * and no fling will occur.
1265     *
1266     * @param velocityX Initial horizontal velocity in pixels per second
1267     * @param velocityY Initial vertical velocity in pixels per second
1268     * @return true if the fling was started, false if the velocity was too low to fling or
1269     * LayoutManager does not support scrolling in the axis fling is issued.
1270     *
1271     * @see LayoutManager#canScrollVertically()
1272     * @see LayoutManager#canScrollHorizontally()
1273     */
1274    public boolean fling(int velocityX, int velocityY) {
1275        if (mLayout == null) {
1276            Log.e(TAG, "Cannot fling without a LayoutManager set. " +
1277                    "Call setLayoutManager with a non-null argument.");
1278            return false;
1279        }
1280        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1281        final boolean canScrollVertical = mLayout.canScrollVertically();
1282        if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
1283            velocityX = 0;
1284        }
1285        if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
1286            velocityY = 0;
1287        }
1288        velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
1289        velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
1290        if (velocityX != 0 || velocityY != 0) {
1291            mViewFlinger.fling(velocityX, velocityY);
1292            return true;
1293        }
1294        return false;
1295    }
1296
1297    /**
1298     * Stop any current scroll in progress, such as one started by
1299     * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
1300     */
1301    public void stopScroll() {
1302        setScrollState(SCROLL_STATE_IDLE);
1303        stopScrollersInternal();
1304    }
1305
1306    /**
1307     * Similar to {@link #stopScroll()} but does not set the state.
1308     */
1309    private void stopScrollersInternal() {
1310        mViewFlinger.stop();
1311        if (mLayout != null) {
1312            mLayout.stopSmoothScroller();
1313        }
1314    }
1315
1316    /**
1317     * Apply a pull to relevant overscroll glow effects
1318     */
1319    private void pullGlows(int overscrollX, int overscrollY) {
1320        if (overscrollX < 0) {
1321            ensureLeftGlow();
1322            mLeftGlow.onPull(-overscrollX / (float) getWidth());
1323        } else if (overscrollX > 0) {
1324            ensureRightGlow();
1325            mRightGlow.onPull(overscrollX / (float) getWidth());
1326        }
1327
1328        if (overscrollY < 0) {
1329            ensureTopGlow();
1330            mTopGlow.onPull(-overscrollY / (float) getHeight());
1331        } else if (overscrollY > 0) {
1332            ensureBottomGlow();
1333            mBottomGlow.onPull(overscrollY / (float) getHeight());
1334        }
1335
1336        if (overscrollX != 0 || overscrollY != 0) {
1337            ViewCompat.postInvalidateOnAnimation(this);
1338        }
1339    }
1340
1341    private void releaseGlows() {
1342        boolean needsInvalidate = false;
1343        if (mLeftGlow != null) needsInvalidate = mLeftGlow.onRelease();
1344        if (mTopGlow != null) needsInvalidate |= mTopGlow.onRelease();
1345        if (mRightGlow != null) needsInvalidate |= mRightGlow.onRelease();
1346        if (mBottomGlow != null) needsInvalidate |= mBottomGlow.onRelease();
1347        if (needsInvalidate) {
1348            ViewCompat.postInvalidateOnAnimation(this);
1349        }
1350    }
1351
1352    private void considerReleasingGlowsOnScroll(int dx, int dy) {
1353        boolean needsInvalidate = false;
1354        if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
1355            needsInvalidate = mLeftGlow.onRelease();
1356        }
1357        if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
1358            needsInvalidate |= mRightGlow.onRelease();
1359        }
1360        if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
1361            needsInvalidate |= mTopGlow.onRelease();
1362        }
1363        if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
1364            needsInvalidate |= mBottomGlow.onRelease();
1365        }
1366        if (needsInvalidate) {
1367            ViewCompat.postInvalidateOnAnimation(this);
1368        }
1369    }
1370
1371    void absorbGlows(int velocityX, int velocityY) {
1372        if (velocityX < 0) {
1373            ensureLeftGlow();
1374            mLeftGlow.onAbsorb(-velocityX);
1375        } else if (velocityX > 0) {
1376            ensureRightGlow();
1377            mRightGlow.onAbsorb(velocityX);
1378        }
1379
1380        if (velocityY < 0) {
1381            ensureTopGlow();
1382            mTopGlow.onAbsorb(-velocityY);
1383        } else if (velocityY > 0) {
1384            ensureBottomGlow();
1385            mBottomGlow.onAbsorb(velocityY);
1386        }
1387
1388        if (velocityX != 0 || velocityY != 0) {
1389            ViewCompat.postInvalidateOnAnimation(this);
1390        }
1391    }
1392
1393    void ensureLeftGlow() {
1394        if (mLeftGlow != null) {
1395            return;
1396        }
1397        mLeftGlow = new EdgeEffectCompat(getContext());
1398        if (mClipToPadding) {
1399            mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
1400                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
1401        } else {
1402            mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
1403        }
1404    }
1405
1406    void ensureRightGlow() {
1407        if (mRightGlow != null) {
1408            return;
1409        }
1410        mRightGlow = new EdgeEffectCompat(getContext());
1411        if (mClipToPadding) {
1412            mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
1413                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
1414        } else {
1415            mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
1416        }
1417    }
1418
1419    void ensureTopGlow() {
1420        if (mTopGlow != null) {
1421            return;
1422        }
1423        mTopGlow = new EdgeEffectCompat(getContext());
1424        if (mClipToPadding) {
1425            mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
1426                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
1427        } else {
1428            mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
1429        }
1430
1431    }
1432
1433    void ensureBottomGlow() {
1434        if (mBottomGlow != null) {
1435            return;
1436        }
1437        mBottomGlow = new EdgeEffectCompat(getContext());
1438        if (mClipToPadding) {
1439            mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
1440                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
1441        } else {
1442            mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
1443        }
1444    }
1445
1446    void invalidateGlows() {
1447        mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
1448    }
1449
1450    // Focus handling
1451
1452    @Override
1453    public View focusSearch(View focused, int direction) {
1454        View result = mLayout.onInterceptFocusSearch(focused, direction);
1455        if (result != null) {
1456            return result;
1457        }
1458        final FocusFinder ff = FocusFinder.getInstance();
1459        result = ff.findNextFocus(this, focused, direction);
1460        if (result == null && mAdapter != null && mLayout != null) {
1461            eatRequestLayout();
1462            result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
1463            resumeRequestLayout(false);
1464        }
1465        return result != null ? result : super.focusSearch(focused, direction);
1466    }
1467
1468    @Override
1469    public void requestChildFocus(View child, View focused) {
1470        if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
1471            mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
1472            offsetDescendantRectToMyCoords(focused, mTempRect);
1473            offsetRectIntoDescendantCoords(child, mTempRect);
1474            requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete);
1475        }
1476        super.requestChildFocus(child, focused);
1477    }
1478
1479    @Override
1480    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
1481        return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
1482    }
1483
1484    @Override
1485    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
1486        if (!mLayout.onAddFocusables(this, views, direction, focusableMode)) {
1487            super.addFocusables(views, direction, focusableMode);
1488        }
1489    }
1490
1491    @Override
1492    protected void onAttachedToWindow() {
1493        super.onAttachedToWindow();
1494        mIsAttached = true;
1495        mFirstLayoutComplete = false;
1496        if (mLayout != null) {
1497            mLayout.onAttachedToWindow(this);
1498        }
1499        mPostedAnimatorRunner = false;
1500    }
1501
1502    @Override
1503    protected void onDetachedFromWindow() {
1504        super.onDetachedFromWindow();
1505        if (mItemAnimator != null) {
1506            mItemAnimator.endAnimations();
1507        }
1508        mFirstLayoutComplete = false;
1509
1510        stopScroll();
1511        mIsAttached = false;
1512        if (mLayout != null) {
1513            mLayout.onDetachedFromWindow(this, mRecycler);
1514        }
1515        removeCallbacks(mItemAnimatorRunner);
1516    }
1517
1518    /**
1519     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
1520     * {@link IllegalStateException} if it <b>is not</b>.
1521     *
1522     * @param message The message for the exception. Can be null.
1523     * @see #assertNotInLayoutOrScroll(String)
1524     */
1525    void assertInLayoutOrScroll(String message) {
1526        if (!mRunningLayoutOrScroll) {
1527            if (message == null) {
1528                throw new IllegalStateException("Cannot call this method unless RecyclerView is "
1529                        + "computing a layout or scrolling");
1530            }
1531            throw new IllegalStateException(message);
1532
1533        }
1534    }
1535
1536    /**
1537     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
1538     * {@link IllegalStateException} if it <b>is</b>.
1539     *
1540     * @param message The message for the exception. Can be null.
1541     * @see #assertInLayoutOrScroll(String)
1542     */
1543    void assertNotInLayoutOrScroll(String message) {
1544        if (mRunningLayoutOrScroll) {
1545            if (message == null) {
1546                throw new IllegalStateException("Cannot call this method while RecyclerView is "
1547                        + "computing a layout or scrolling");
1548            }
1549            throw new IllegalStateException(message);
1550        }
1551    }
1552
1553    /**
1554     * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
1555     * to child views or this view's standard scrolling behavior.
1556     *
1557     * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
1558     * returns true from
1559     * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
1560     * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
1561     * for each incoming MotionEvent until the end of the gesture.</p>
1562     *
1563     * @param listener Listener to add
1564     */
1565    public void addOnItemTouchListener(OnItemTouchListener listener) {
1566        mOnItemTouchListeners.add(listener);
1567    }
1568
1569    /**
1570     * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
1571     *
1572     * @param listener Listener to remove
1573     */
1574    public void removeOnItemTouchListener(OnItemTouchListener listener) {
1575        mOnItemTouchListeners.remove(listener);
1576        if (mActiveOnItemTouchListener == listener) {
1577            mActiveOnItemTouchListener = null;
1578        }
1579    }
1580
1581    private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
1582        final int action = e.getAction();
1583        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
1584            mActiveOnItemTouchListener = null;
1585        }
1586
1587        final int listenerCount = mOnItemTouchListeners.size();
1588        for (int i = 0; i < listenerCount; i++) {
1589            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
1590            if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
1591                mActiveOnItemTouchListener = listener;
1592                return true;
1593            }
1594        }
1595        return false;
1596    }
1597
1598    private boolean dispatchOnItemTouch(MotionEvent e) {
1599        final int action = e.getAction();
1600        if (mActiveOnItemTouchListener != null) {
1601            if (action == MotionEvent.ACTION_DOWN) {
1602                // Stale state from a previous gesture, we're starting a new one. Clear it.
1603                mActiveOnItemTouchListener = null;
1604            } else {
1605                mActiveOnItemTouchListener.onTouchEvent(this, e);
1606                if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
1607                    // Clean up for the next gesture.
1608                    mActiveOnItemTouchListener = null;
1609                }
1610                return true;
1611            }
1612        }
1613
1614        // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
1615        // as called from onInterceptTouchEvent; skip it.
1616        if (action != MotionEvent.ACTION_DOWN) {
1617            final int listenerCount = mOnItemTouchListeners.size();
1618            for (int i = 0; i < listenerCount; i++) {
1619                final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
1620                if (listener.onInterceptTouchEvent(this, e)) {
1621                    mActiveOnItemTouchListener = listener;
1622                    return true;
1623                }
1624            }
1625        }
1626        return false;
1627    }
1628
1629    @Override
1630    public boolean onInterceptTouchEvent(MotionEvent e) {
1631        if (dispatchOnItemTouchIntercept(e)) {
1632            cancelTouch();
1633            return true;
1634        }
1635
1636        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
1637        final boolean canScrollVertically = mLayout.canScrollVertically();
1638
1639        if (mVelocityTracker == null) {
1640            mVelocityTracker = VelocityTracker.obtain();
1641        }
1642        mVelocityTracker.addMovement(e);
1643
1644        final int action = MotionEventCompat.getActionMasked(e);
1645        final int actionIndex = MotionEventCompat.getActionIndex(e);
1646
1647        switch (action) {
1648            case MotionEvent.ACTION_DOWN:
1649                mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
1650                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
1651                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
1652
1653                if (mScrollState == SCROLL_STATE_SETTLING) {
1654                    getParent().requestDisallowInterceptTouchEvent(true);
1655                    setScrollState(SCROLL_STATE_DRAGGING);
1656                }
1657                break;
1658
1659            case MotionEventCompat.ACTION_POINTER_DOWN:
1660                mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);
1661                mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);
1662                mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);
1663                break;
1664
1665            case MotionEvent.ACTION_MOVE: {
1666                final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
1667                if (index < 0) {
1668                    Log.e(TAG, "Error processing scroll; pointer index for id " +
1669                            mScrollPointerId + " not found. Did any MotionEvents get skipped?");
1670                    return false;
1671                }
1672
1673                final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
1674                final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);
1675                if (mScrollState != SCROLL_STATE_DRAGGING) {
1676                    final int dx = x - mInitialTouchX;
1677                    final int dy = y - mInitialTouchY;
1678                    boolean startScroll = false;
1679                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
1680                        mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
1681                        startScroll = true;
1682                    }
1683                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
1684                        mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
1685                        startScroll = true;
1686                    }
1687                    if (startScroll) {
1688                        setScrollState(SCROLL_STATE_DRAGGING);
1689                    }
1690                }
1691            } break;
1692
1693            case MotionEventCompat.ACTION_POINTER_UP: {
1694                onPointerUp(e);
1695            } break;
1696
1697            case MotionEvent.ACTION_UP: {
1698                mVelocityTracker.clear();
1699            } break;
1700
1701            case MotionEvent.ACTION_CANCEL: {
1702                cancelTouch();
1703            }
1704        }
1705        return mScrollState == SCROLL_STATE_DRAGGING;
1706    }
1707
1708    @Override
1709    public boolean onTouchEvent(MotionEvent e) {
1710        if (dispatchOnItemTouch(e)) {
1711            cancelTouch();
1712            return true;
1713        }
1714
1715        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
1716        final boolean canScrollVertically = mLayout.canScrollVertically();
1717
1718        if (mVelocityTracker == null) {
1719            mVelocityTracker = VelocityTracker.obtain();
1720        }
1721        mVelocityTracker.addMovement(e);
1722
1723        final int action = MotionEventCompat.getActionMasked(e);
1724        final int actionIndex = MotionEventCompat.getActionIndex(e);
1725
1726        switch (action) {
1727            case MotionEvent.ACTION_DOWN: {
1728                mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
1729                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
1730                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
1731            } break;
1732
1733            case MotionEventCompat.ACTION_POINTER_DOWN: {
1734                mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);
1735                mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);
1736                mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);
1737            } break;
1738
1739            case MotionEvent.ACTION_MOVE: {
1740                final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
1741                if (index < 0) {
1742                    Log.e(TAG, "Error processing scroll; pointer index for id " +
1743                            mScrollPointerId + " not found. Did any MotionEvents get skipped?");
1744                    return false;
1745                }
1746
1747                final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
1748                final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);
1749                if (mScrollState != SCROLL_STATE_DRAGGING) {
1750                    final int dx = x - mInitialTouchX;
1751                    final int dy = y - mInitialTouchY;
1752                    boolean startScroll = false;
1753                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
1754                        mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
1755                        startScroll = true;
1756                    }
1757                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
1758                        mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
1759                        startScroll = true;
1760                    }
1761                    if (startScroll) {
1762                        setScrollState(SCROLL_STATE_DRAGGING);
1763                    }
1764                }
1765                if (mScrollState == SCROLL_STATE_DRAGGING) {
1766                    final int dx = x - mLastTouchX;
1767                    final int dy = y - mLastTouchY;
1768                    if (scrollByInternal(
1769                            canScrollHorizontally ? -dx : 0, canScrollVertically ? -dy : 0)) {
1770                        getParent().requestDisallowInterceptTouchEvent(true);
1771                    }
1772                }
1773                mLastTouchX = x;
1774                mLastTouchY = y;
1775            } break;
1776
1777            case MotionEventCompat.ACTION_POINTER_UP: {
1778                onPointerUp(e);
1779            } break;
1780
1781            case MotionEvent.ACTION_UP: {
1782                mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
1783                final float xvel = canScrollHorizontally ?
1784                        -VelocityTrackerCompat.getXVelocity(mVelocityTracker, mScrollPointerId) : 0;
1785                final float yvel = canScrollVertically ?
1786                        -VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId) : 0;
1787                if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
1788                    setScrollState(SCROLL_STATE_IDLE);
1789                }
1790                mVelocityTracker.clear();
1791                releaseGlows();
1792            } break;
1793
1794            case MotionEvent.ACTION_CANCEL: {
1795                cancelTouch();
1796            } break;
1797        }
1798
1799        return true;
1800    }
1801
1802    private void cancelTouch() {
1803        if (mVelocityTracker != null) {
1804            mVelocityTracker.clear();
1805        }
1806        releaseGlows();
1807        setScrollState(SCROLL_STATE_IDLE);
1808    }
1809
1810    private void onPointerUp(MotionEvent e) {
1811        final int actionIndex = MotionEventCompat.getActionIndex(e);
1812        if (MotionEventCompat.getPointerId(e, actionIndex) == mScrollPointerId) {
1813            // Pick a new pointer to pick up the slack.
1814            final int newIndex = actionIndex == 0 ? 1 : 0;
1815            mScrollPointerId = MotionEventCompat.getPointerId(e, newIndex);
1816            mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, newIndex) + 0.5f);
1817            mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, newIndex) + 0.5f);
1818        }
1819    }
1820
1821    @Override
1822    protected void onMeasure(int widthSpec, int heightSpec) {
1823        if (mAdapterUpdateDuringMeasure) {
1824            eatRequestLayout();
1825            processAdapterUpdatesAndSetAnimationFlags();
1826
1827            if (mState.mRunPredictiveAnimations) {
1828                // TODO: try to provide a better approach.
1829                // When RV decides to run predictive animations, we need to measure in pre-layout
1830                // state so that pre-layout pass results in correct layout.
1831                // On the other hand, this will prevent the layout manager from resizing properly.
1832                mState.mInPreLayout = true;
1833            } else {
1834                // consume remaining updates to provide a consistent state with the layout pass.
1835                mAdapterHelper.consumeUpdatesInOnePass();
1836                mState.mInPreLayout = false;
1837            }
1838            mAdapterUpdateDuringMeasure = false;
1839            resumeRequestLayout(false);
1840        }
1841
1842        if (mAdapter != null) {
1843            mState.mItemCount = mAdapter.getItemCount();
1844        } else {
1845            mState.mItemCount = 0;
1846        }
1847        if (mLayout == null) {
1848            defaultOnMeasure(widthSpec, heightSpec);
1849        } else {
1850            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
1851        }
1852
1853        mState.mInPreLayout = false; // clear
1854    }
1855
1856    /**
1857     * Used when onMeasure is called before layout manager is set
1858     */
1859    private void defaultOnMeasure(int widthSpec, int heightSpec) {
1860        final int widthMode = MeasureSpec.getMode(widthSpec);
1861        final int heightMode = MeasureSpec.getMode(heightSpec);
1862        final int widthSize = MeasureSpec.getSize(widthSpec);
1863        final int heightSize = MeasureSpec.getSize(heightSpec);
1864
1865        int width = 0;
1866        int height = 0;
1867
1868        switch (widthMode) {
1869            case MeasureSpec.EXACTLY:
1870            case MeasureSpec.AT_MOST:
1871                width = widthSize;
1872                break;
1873            case MeasureSpec.UNSPECIFIED:
1874            default:
1875                width = ViewCompat.getMinimumWidth(this);
1876                break;
1877        }
1878
1879        switch (heightMode) {
1880            case MeasureSpec.EXACTLY:
1881            case MeasureSpec.AT_MOST:
1882                height = heightSize;
1883                break;
1884            case MeasureSpec.UNSPECIFIED:
1885            default:
1886                height = ViewCompat.getMinimumHeight(this);
1887                break;
1888        }
1889
1890        setMeasuredDimension(width, height);
1891    }
1892
1893    @Override
1894    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
1895        super.onSizeChanged(w, h, oldw, oldh);
1896        if (w != oldw || h != oldh) {
1897            invalidateGlows();
1898        }
1899    }
1900
1901    /**
1902     * Sets the {@link ItemAnimator} that will handle animations involving changes
1903     * to the items in this RecyclerView. By default, RecyclerView instantiates and
1904     * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
1905     * enabled for the RecyclerView depends on the ItemAnimator and whether
1906     * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
1907     * supports item animations}.
1908     *
1909     * @param animator The ItemAnimator being set. If null, no animations will occur
1910     * when changes occur to the items in this RecyclerView.
1911     */
1912    public void setItemAnimator(ItemAnimator animator) {
1913        if (mItemAnimator != null) {
1914            mItemAnimator.endAnimations();
1915            mItemAnimator.setListener(null);
1916        }
1917        mItemAnimator = animator;
1918        if (mItemAnimator != null) {
1919            mItemAnimator.setListener(mItemAnimatorListener);
1920        }
1921    }
1922
1923    /**
1924     * Gets the current ItemAnimator for this RecyclerView. A null return value
1925     * indicates that there is no animator and that item changes will happen without
1926     * any animations. By default, RecyclerView instantiates and
1927     * uses an instance of {@link DefaultItemAnimator}.
1928     *
1929     * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
1930     * when changes occur to the items in this RecyclerView.
1931     */
1932    public ItemAnimator getItemAnimator() {
1933        return mItemAnimator;
1934    }
1935
1936    private boolean supportsChangeAnimations() {
1937        return mItemAnimator != null && mItemAnimator.getSupportsChangeAnimations();
1938    }
1939
1940    /**
1941     * Post a runnable to the next frame to run pending item animations. Only the first such
1942     * request will be posted, governed by the mPostedAnimatorRunner flag.
1943     */
1944    private void postAnimationRunner() {
1945        if (!mPostedAnimatorRunner && mIsAttached) {
1946            ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
1947            mPostedAnimatorRunner = true;
1948        }
1949    }
1950
1951    private boolean predictiveItemAnimationsEnabled() {
1952        return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
1953    }
1954
1955    /**
1956     * Consumes adapter updates and calculates which type of animations we want to run.
1957     * Called in onMeasure and dispatchLayout.
1958     * <p>
1959     * This method may process only the pre-layout state of updates or all of them.
1960     */
1961    private void processAdapterUpdatesAndSetAnimationFlags() {
1962        if (mDataSetHasChangedAfterLayout) {
1963            // Processing these items have no value since data set changed unexpectedly.
1964            // Instead, we just reset it.
1965            mAdapterHelper.reset();
1966            markKnownViewsInvalid();
1967            mLayout.onItemsChanged(this);
1968        }
1969        // simple animations are a subset of advanced animations (which will cause a
1970        // pre-layout step)
1971        // If layout supports predictive animations, pre-process to decide if we want to run them
1972        if (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations()) {
1973            mAdapterHelper.preProcess();
1974        } else {
1975            mAdapterHelper.consumeUpdatesInOnePass();
1976        }
1977        boolean animationTypeSupported = (mItemsAddedOrRemoved && !mItemsChanged) ||
1978                (mItemsAddedOrRemoved || (mItemsChanged && supportsChangeAnimations()));
1979        mState.mRunSimpleAnimations = mFirstLayoutComplete && mItemAnimator != null &&
1980                (mDataSetHasChangedAfterLayout || animationTypeSupported ||
1981                        mLayout.mRequestedSimpleAnimations) &&
1982                (!mDataSetHasChangedAfterLayout || mAdapter.hasStableIds());
1983        mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations &&
1984                animationTypeSupported && !mDataSetHasChangedAfterLayout &&
1985                predictiveItemAnimationsEnabled();
1986    }
1987
1988    /**
1989     * Wrapper around layoutChildren() that handles animating changes caused by layout.
1990     * Animations work on the assumption that there are five different kinds of items
1991     * in play:
1992     * PERSISTENT: items are visible before and after layout
1993     * REMOVED: items were visible before layout and were removed by the app
1994     * ADDED: items did not exist before layout and were added by the app
1995     * DISAPPEARING: items exist in the data set before/after, but changed from
1996     * visible to non-visible in the process of layout (they were moved off
1997     * screen as a side-effect of other changes)
1998     * APPEARING: items exist in the data set before/after, but changed from
1999     * non-visible to visible in the process of layout (they were moved on
2000     * screen as a side-effect of other changes)
2001     * The overall approach figures out what items exist before/after layout and
2002     * infers one of the five above states for each of the items. Then the animations
2003     * are set up accordingly:
2004     * PERSISTENT views are moved ({@link ItemAnimator#animateMove(ViewHolder, int, int, int, int)})
2005     * REMOVED views are removed ({@link ItemAnimator#animateRemove(ViewHolder)})
2006     * ADDED views are added ({@link ItemAnimator#animateAdd(ViewHolder)})
2007     * DISAPPEARING views are moved off screen
2008     * APPEARING views are moved on screen
2009     */
2010    void dispatchLayout() {
2011        if (mAdapter == null) {
2012            Log.e(TAG, "No adapter attached; skipping layout");
2013            return;
2014        }
2015        if (mLayout == null) {
2016            Log.e(TAG, "No layout manager attached; skipping layout");
2017            return;
2018        }
2019        mDisappearingViewsInLayoutPass.clear();
2020        eatRequestLayout();
2021        mRunningLayoutOrScroll = true;
2022
2023        processAdapterUpdatesAndSetAnimationFlags();
2024
2025        mState.mOldChangedHolders = mState.mRunSimpleAnimations && mItemsChanged
2026                && supportsChangeAnimations() ? new ArrayMap<Long, ViewHolder>() : null;
2027        mItemsAddedOrRemoved = mItemsChanged = false;
2028        ArrayMap<View, Rect> appearingViewInitialBounds = null;
2029        mState.mInPreLayout = mState.mRunPredictiveAnimations;
2030        mState.mItemCount = mAdapter.getItemCount();
2031        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
2032
2033        if (mState.mRunSimpleAnimations) {
2034            // Step 0: Find out where all non-removed items are, pre-layout
2035            mState.mPreLayoutHolderMap.clear();
2036            mState.mPostLayoutHolderMap.clear();
2037            int count = mChildHelper.getChildCount();
2038            for (int i = 0; i < count; ++i) {
2039                final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
2040                if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
2041                    continue;
2042                }
2043                final View view = holder.itemView;
2044                mState.mPreLayoutHolderMap.put(holder, new ItemHolderInfo(holder,
2045                        view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
2046            }
2047        }
2048        if (mState.mRunPredictiveAnimations) {
2049            // Step 1: run prelayout: This will use the old positions of items. The layout manager
2050            // is expected to layout everything, even removed items (though not to add removed
2051            // items back to the container). This gives the pre-layout position of APPEARING views
2052            // which come into existence as part of the real layout.
2053
2054            // Save old positions so that LayoutManager can run its mapping logic.
2055            saveOldPositions();
2056            // processAdapterUpdatesAndSetAnimationFlags already run pre-layout animations.
2057            if (mState.mOldChangedHolders != null) {
2058                int count = mChildHelper.getChildCount();
2059                for (int i = 0; i < count; ++i) {
2060                    final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
2061                    if (holder.isChanged() && !holder.isRemoved() && !holder.shouldIgnore()) {
2062                        long key = getChangedHolderKey(holder);
2063                        mState.mOldChangedHolders.put(key, holder);
2064                        mState.mPreLayoutHolderMap.remove(holder);
2065                    }
2066                }
2067            }
2068
2069            final boolean didStructureChange = mState.mStructureChanged;
2070            mState.mStructureChanged = false;
2071            // temporarily disable flag because we are asking for previous layout
2072            mLayout.onLayoutChildren(mRecycler, mState);
2073            mState.mStructureChanged = didStructureChange;
2074
2075            appearingViewInitialBounds = new ArrayMap<View, Rect>();
2076            for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
2077                boolean found = false;
2078                View child = mChildHelper.getChildAt(i);
2079                if (getChildViewHolderInt(child).shouldIgnore()) {
2080                    continue;
2081                }
2082                for (int j = 0; j < mState.mPreLayoutHolderMap.size(); ++j) {
2083                    ViewHolder holder = mState.mPreLayoutHolderMap.keyAt(j);
2084                    if (holder.itemView == child) {
2085                        found = true;
2086                        break;
2087                    }
2088                }
2089                if (!found) {
2090                    appearingViewInitialBounds.put(child, new Rect(child.getLeft(), child.getTop(),
2091                            child.getRight(), child.getBottom()));
2092                }
2093            }
2094            // we don't process disappearing list because they may re-appear in post layout pass.
2095            clearOldPositions();
2096            mAdapterHelper.consumePostponedUpdates();
2097        } else {
2098            clearOldPositions();
2099            // in case pre layout did run but we decided not to run predictive animations.
2100            mAdapterHelper.consumeUpdatesInOnePass();
2101            if (mState.mOldChangedHolders != null) {
2102                int count = mChildHelper.getChildCount();
2103                for (int i = 0; i < count; ++i) {
2104                    final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
2105                    if (holder.isChanged() && !holder.isRemoved() && !holder.shouldIgnore()) {
2106                        long key = getChangedHolderKey(holder);
2107                        mState.mOldChangedHolders.put(key, holder);
2108                        mState.mPreLayoutHolderMap.remove(holder);
2109                    }
2110                }
2111            }
2112        }
2113        mState.mItemCount = mAdapter.getItemCount();
2114        mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
2115
2116        // Step 2: Run layout
2117        mState.mInPreLayout = false;
2118        mLayout.onLayoutChildren(mRecycler, mState);
2119
2120        mState.mStructureChanged = false;
2121        mPendingSavedState = null;
2122
2123        // onLayoutChildren may have caused client code to disable item animations; re-check
2124        mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
2125
2126        if (mState.mRunSimpleAnimations) {
2127            // Step 3: Find out where things are now, post-layout
2128            ArrayMap<Long, ViewHolder> newChangedHolders = mState.mOldChangedHolders != null ?
2129                    new ArrayMap<Long, ViewHolder>() : null;
2130            int count = mChildHelper.getChildCount();
2131            for (int i = 0; i < count; ++i) {
2132                ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
2133                if (holder.shouldIgnore()) {
2134                    continue;
2135                }
2136                final View view = holder.itemView;
2137                long key = getChangedHolderKey(holder);
2138                if (newChangedHolders != null && mState.mOldChangedHolders.get(key) != null) {
2139                    newChangedHolders.put(key, holder);
2140                } else {
2141                    mState.mPostLayoutHolderMap.put(holder, new ItemHolderInfo(holder,
2142                            view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
2143                }
2144            }
2145            processDisappearingList(appearingViewInitialBounds);
2146            // Step 4: Animate DISAPPEARING and REMOVED items
2147            int preLayoutCount = mState.mPreLayoutHolderMap.size();
2148            for (int i = preLayoutCount - 1; i >= 0; i--) {
2149                ViewHolder itemHolder = mState.mPreLayoutHolderMap.keyAt(i);
2150                if (!mState.mPostLayoutHolderMap.containsKey(itemHolder)) {
2151                    ItemHolderInfo disappearingItem = mState.mPreLayoutHolderMap.valueAt(i);
2152                    mState.mPreLayoutHolderMap.removeAt(i);
2153
2154                    View disappearingItemView = disappearingItem.holder.itemView;
2155                    mRecycler.unscrapView(disappearingItem.holder);
2156                    animateDisappearance(disappearingItem);
2157                }
2158            }
2159            // Step 5: Animate APPEARING and ADDED items
2160            int postLayoutCount = mState.mPostLayoutHolderMap.size();
2161            if (postLayoutCount > 0) {
2162                for (int i = postLayoutCount - 1; i >= 0; i--) {
2163                    ViewHolder itemHolder = mState.mPostLayoutHolderMap.keyAt(i);
2164                    ItemHolderInfo info = mState.mPostLayoutHolderMap.valueAt(i);
2165                    if ((mState.mPreLayoutHolderMap.isEmpty() ||
2166                            !mState.mPreLayoutHolderMap.containsKey(itemHolder))) {
2167                        mState.mPostLayoutHolderMap.removeAt(i);
2168                        Rect initialBounds = (appearingViewInitialBounds != null) ?
2169                                appearingViewInitialBounds.get(itemHolder.itemView) : null;
2170                        animateAppearance(itemHolder, initialBounds,
2171                                info.left, info.top);
2172                    }
2173                }
2174            }
2175            // Step 6: Animate PERSISTENT items
2176            count = mState.mPostLayoutHolderMap.size();
2177            for (int i = 0; i < count; ++i) {
2178                ViewHolder postHolder = mState.mPostLayoutHolderMap.keyAt(i);
2179                ItemHolderInfo postInfo = mState.mPostLayoutHolderMap.valueAt(i);
2180                ItemHolderInfo preInfo = mState.mPreLayoutHolderMap.get(postHolder);
2181                if (preInfo != null && postInfo != null) {
2182                    if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) {
2183                        postHolder.setIsRecyclable(false);
2184                        if (DEBUG) {
2185                            Log.d(TAG, "PERSISTENT: " + postHolder +
2186                                    " with view " + postHolder.itemView);
2187                        }
2188                        if (mItemAnimator.animateMove(postHolder,
2189                                preInfo.left, preInfo.top, postInfo.left, postInfo.top)) {
2190                            postAnimationRunner();
2191                        }
2192                    }
2193                }
2194            }
2195            // Step 7: Animate CHANGING items
2196            count = mState.mOldChangedHolders != null ? mState.mOldChangedHolders.size() : 0;
2197            // traverse reverse in case view gets recycled while we are traversing the list.
2198            for (int i = count - 1; i >= 0; i--) {
2199                long key = mState.mOldChangedHolders.keyAt(i);
2200                ViewHolder oldHolder = mState.mOldChangedHolders.get(key);
2201                View oldView = oldHolder.itemView;
2202                if (oldHolder.shouldIgnore()) {
2203                    continue;
2204                }
2205                // We probably don't need this check anymore since these views are removed from
2206                // the list if they are recycled.
2207                if (mRecycler.mChangedScrap != null &&
2208                        mRecycler.mChangedScrap.contains(oldHolder)) {
2209                    animateChange(oldHolder, newChangedHolders.get(key));
2210                } else if (DEBUG) {
2211                    Log.e(TAG, "cannot find old changed holder in changed scrap :/" + oldHolder);
2212                }
2213            }
2214        }
2215        resumeRequestLayout(false);
2216        mLayout.removeAndRecycleScrapInt(mRecycler);
2217        mState.mPreviousLayoutItemCount = mState.mItemCount;
2218        mDataSetHasChangedAfterLayout = false;
2219        mState.mRunSimpleAnimations = false;
2220        mState.mRunPredictiveAnimations = false;
2221        mRunningLayoutOrScroll = false;
2222        mLayout.mRequestedSimpleAnimations = false;
2223        if (mRecycler.mChangedScrap != null) {
2224            mRecycler.mChangedScrap.clear();
2225        }
2226        mState.mOldChangedHolders = null;
2227
2228        if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
2229            notifyOnScrolled(0, 0);
2230        }
2231    }
2232
2233    private void findMinMaxChildLayoutPositions(int[] into) {
2234        final int count = mChildHelper.getChildCount();
2235        if (count == 0) {
2236            into[0] = 0;
2237            into[1] = 0;
2238            return;
2239        }
2240        int minPositionPreLayout = Integer.MAX_VALUE;
2241        int maxPositionPreLayout = Integer.MIN_VALUE;
2242        for (int i = 0; i < count; ++i) {
2243            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
2244            if (holder.shouldIgnore()) {
2245                continue;
2246            }
2247            final int pos = holder.getLayoutPosition();
2248            if (pos < minPositionPreLayout) {
2249                minPositionPreLayout = pos;
2250            }
2251            if (pos > maxPositionPreLayout) {
2252                maxPositionPreLayout = pos;
2253            }
2254        }
2255        into[0] = minPositionPreLayout;
2256        into[1] = maxPositionPreLayout;
2257    }
2258
2259    private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
2260        int count = mChildHelper.getChildCount();
2261        if (count == 0) {
2262            return minPositionPreLayout != 0 || maxPositionPreLayout != 0;
2263        }
2264        for (int i = 0; i < count; ++i) {
2265            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
2266            if (holder.shouldIgnore()) {
2267                continue;
2268            }
2269            final int pos = holder.getLayoutPosition();
2270            if (pos < minPositionPreLayout || pos > maxPositionPreLayout) {
2271                return true;
2272            }
2273        }
2274        return false;
2275    }
2276
2277    @Override
2278    protected void removeDetachedView(View child, boolean animate) {
2279        ViewHolder vh = getChildViewHolderInt(child);
2280        if (vh != null) {
2281            if (vh.isTmpDetached()) {
2282                vh.clearTmpDetachFlag();
2283            } else if (!vh.shouldIgnore()) {
2284                throw new IllegalArgumentException("Called removeDetachedView with a view which"
2285                        + " is not flagged as tmp detached." + vh);
2286            }
2287        }
2288        dispatchChildDetached(child);
2289        super.removeDetachedView(child, animate);
2290    }
2291
2292    /**
2293     * Returns a unique key to be used while handling change animations.
2294     * It might be child's position or stable id depending on the adapter type.
2295     */
2296    long getChangedHolderKey(ViewHolder holder) {
2297        return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
2298    }
2299
2300    /**
2301     * A LayoutManager may want to layout a view just to animate disappearance.
2302     * This method handles those views and triggers remove animation on them.
2303     */
2304    private void processDisappearingList(ArrayMap<View, Rect> appearingViews) {
2305        final int count = mDisappearingViewsInLayoutPass.size();
2306        for (int i = 0; i < count; i ++) {
2307            View view = mDisappearingViewsInLayoutPass.get(i);
2308            ViewHolder vh = getChildViewHolderInt(view);
2309            final ItemHolderInfo info = mState.mPreLayoutHolderMap.remove(vh);
2310            if (!mState.isPreLayout()) {
2311                mState.mPostLayoutHolderMap.remove(vh);
2312            }
2313            if (appearingViews.remove(view) != null) {
2314                mLayout.removeAndRecycleView(view, mRecycler);
2315                continue;
2316            }
2317            if (info != null) {
2318                animateDisappearance(info);
2319            } else {
2320                // let it disappear from the position it becomes visible
2321                animateDisappearance(new ItemHolderInfo(vh, view.getLeft(), view.getTop(),
2322                        view.getRight(), view.getBottom()));
2323            }
2324        }
2325        mDisappearingViewsInLayoutPass.clear();
2326    }
2327
2328    private void animateAppearance(ViewHolder itemHolder, Rect beforeBounds, int afterLeft,
2329            int afterTop) {
2330        View newItemView = itemHolder.itemView;
2331        if (beforeBounds != null &&
2332                (beforeBounds.left != afterLeft || beforeBounds.top != afterTop)) {
2333            // slide items in if before/after locations differ
2334            itemHolder.setIsRecyclable(false);
2335            if (DEBUG) {
2336                Log.d(TAG, "APPEARING: " + itemHolder + " with view " + newItemView);
2337            }
2338            if (mItemAnimator.animateMove(itemHolder,
2339                    beforeBounds.left, beforeBounds.top,
2340                    afterLeft, afterTop)) {
2341                postAnimationRunner();
2342            }
2343        } else {
2344            if (DEBUG) {
2345                Log.d(TAG, "ADDED: " + itemHolder + " with view " + newItemView);
2346            }
2347            itemHolder.setIsRecyclable(false);
2348            if (mItemAnimator.animateAdd(itemHolder)) {
2349                postAnimationRunner();
2350            }
2351        }
2352    }
2353
2354    private void animateDisappearance(ItemHolderInfo disappearingItem) {
2355        View disappearingItemView = disappearingItem.holder.itemView;
2356        addAnimatingView(disappearingItem.holder);
2357        int oldLeft = disappearingItem.left;
2358        int oldTop = disappearingItem.top;
2359        int newLeft = disappearingItemView.getLeft();
2360        int newTop = disappearingItemView.getTop();
2361        if (oldLeft != newLeft || oldTop != newTop) {
2362            disappearingItem.holder.setIsRecyclable(false);
2363            disappearingItemView.layout(newLeft, newTop,
2364                    newLeft + disappearingItemView.getWidth(),
2365                    newTop + disappearingItemView.getHeight());
2366            if (DEBUG) {
2367                Log.d(TAG, "DISAPPEARING: " + disappearingItem.holder +
2368                        " with view " + disappearingItemView);
2369            }
2370            if (mItemAnimator.animateMove(disappearingItem.holder, oldLeft, oldTop,
2371                    newLeft, newTop)) {
2372                postAnimationRunner();
2373            }
2374        } else {
2375            if (DEBUG) {
2376                Log.d(TAG, "REMOVED: " + disappearingItem.holder +
2377                        " with view " + disappearingItemView);
2378            }
2379            disappearingItem.holder.setIsRecyclable(false);
2380            if (mItemAnimator.animateRemove(disappearingItem.holder)) {
2381                postAnimationRunner();
2382            }
2383        }
2384    }
2385
2386    private void animateChange(ViewHolder oldHolder, ViewHolder newHolder) {
2387        oldHolder.setIsRecyclable(false);
2388        addAnimatingView(oldHolder);
2389        oldHolder.mShadowedHolder = newHolder;
2390        mRecycler.unscrapView(oldHolder);
2391        if (DEBUG) {
2392            Log.d(TAG, "CHANGED: " + oldHolder + " with view " + oldHolder.itemView);
2393        }
2394        final int fromLeft = oldHolder.itemView.getLeft();
2395        final int fromTop = oldHolder.itemView.getTop();
2396        final int toLeft, toTop;
2397        if (newHolder == null || newHolder.shouldIgnore()) {
2398            toLeft = fromLeft;
2399            toTop = fromTop;
2400        } else {
2401            toLeft = newHolder.itemView.getLeft();
2402            toTop = newHolder.itemView.getTop();
2403            newHolder.setIsRecyclable(false);
2404            newHolder.mShadowingHolder = oldHolder;
2405        }
2406        if(mItemAnimator.animateChange(oldHolder, newHolder,
2407                fromLeft, fromTop, toLeft, toTop)) {
2408            postAnimationRunner();
2409        }
2410    }
2411
2412    @Override
2413    protected void onLayout(boolean changed, int l, int t, int r, int b) {
2414        eatRequestLayout();
2415        dispatchLayout();
2416        resumeRequestLayout(false);
2417        mFirstLayoutComplete = true;
2418    }
2419
2420    @Override
2421    public void requestLayout() {
2422        if (!mEatRequestLayout) {
2423            super.requestLayout();
2424        } else {
2425            mLayoutRequestEaten = true;
2426        }
2427    }
2428
2429    void markItemDecorInsetsDirty() {
2430        final int childCount = mChildHelper.getUnfilteredChildCount();
2431        for (int i = 0; i < childCount; i++) {
2432            final View child = mChildHelper.getUnfilteredChildAt(i);
2433            ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
2434        }
2435        mRecycler.markItemDecorInsetsDirty();
2436    }
2437
2438    @Override
2439    public void draw(Canvas c) {
2440        super.draw(c);
2441
2442        final int count = mItemDecorations.size();
2443        for (int i = 0; i < count; i++) {
2444            mItemDecorations.get(i).onDrawOver(c, this, mState);
2445        }
2446        // TODO If padding is not 0 and chilChildrenToPadding is false, to draw glows properly, we
2447        // need find children closest to edges. Not sure if it is worth the effort.
2448        boolean needsInvalidate = false;
2449        if (mLeftGlow != null && !mLeftGlow.isFinished()) {
2450            final int restore = c.save();
2451            final int padding = mClipToPadding ? getPaddingBottom() : 0;
2452            c.rotate(270);
2453            c.translate(-getHeight() + padding, 0);
2454            needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
2455            c.restoreToCount(restore);
2456        }
2457        if (mTopGlow != null && !mTopGlow.isFinished()) {
2458            final int restore = c.save();
2459            if (mClipToPadding) {
2460                c.translate(getPaddingLeft(), getPaddingTop());
2461            }
2462            needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
2463            c.restoreToCount(restore);
2464        }
2465        if (mRightGlow != null && !mRightGlow.isFinished()) {
2466            final int restore = c.save();
2467            final int width = getWidth();
2468            final int padding = mClipToPadding ? getPaddingTop() : 0;
2469            c.rotate(90);
2470            c.translate(-padding, -width);
2471            needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
2472            c.restoreToCount(restore);
2473        }
2474        if (mBottomGlow != null && !mBottomGlow.isFinished()) {
2475            final int restore = c.save();
2476            c.rotate(180);
2477            if (mClipToPadding) {
2478                c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
2479            } else {
2480                c.translate(-getWidth(), -getHeight());
2481            }
2482            needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
2483            c.restoreToCount(restore);
2484        }
2485
2486        // If some views are animating, ItemDecorators are likely to move/change with them.
2487        // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
2488        // display lists are not invalidated.
2489        if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0 &&
2490                mItemAnimator.isRunning()) {
2491            needsInvalidate = true;
2492        }
2493
2494        if (needsInvalidate) {
2495            ViewCompat.postInvalidateOnAnimation(this);
2496        }
2497    }
2498
2499    @Override
2500    public void onDraw(Canvas c) {
2501        super.onDraw(c);
2502
2503        final int count = mItemDecorations.size();
2504        for (int i = 0; i < count; i++) {
2505            mItemDecorations.get(i).onDraw(c, this, mState);
2506        }
2507    }
2508
2509    @Override
2510    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
2511        return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
2512    }
2513
2514    @Override
2515    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
2516        if (mLayout == null) {
2517            throw new IllegalStateException("RecyclerView has no LayoutManager");
2518        }
2519        return mLayout.generateDefaultLayoutParams();
2520    }
2521
2522    @Override
2523    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
2524        if (mLayout == null) {
2525            throw new IllegalStateException("RecyclerView has no LayoutManager");
2526        }
2527        return mLayout.generateLayoutParams(getContext(), attrs);
2528    }
2529
2530    @Override
2531    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
2532        if (mLayout == null) {
2533            throw new IllegalStateException("RecyclerView has no LayoutManager");
2534        }
2535        return mLayout.generateLayoutParams(p);
2536    }
2537
2538    void saveOldPositions() {
2539        final int childCount = mChildHelper.getUnfilteredChildCount();
2540        for (int i = 0; i < childCount; i++) {
2541            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
2542            if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
2543                throw new IllegalStateException("view holder cannot have position -1 unless it"
2544                        + " is removed");
2545            }
2546            if (!holder.shouldIgnore()) {
2547                holder.saveOldPosition();
2548            }
2549        }
2550    }
2551
2552    void clearOldPositions() {
2553        final int childCount = mChildHelper.getUnfilteredChildCount();
2554        for (int i = 0; i < childCount; i++) {
2555            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
2556            if (!holder.shouldIgnore()) {
2557                holder.clearOldPosition();
2558            }
2559        }
2560        mRecycler.clearOldPositions();
2561    }
2562
2563    void offsetPositionRecordsForMove(int from, int to) {
2564        final int childCount = mChildHelper.getUnfilteredChildCount();
2565        final int start, end, inBetweenOffset;
2566        if (from < to) {
2567            start = from;
2568            end = to;
2569            inBetweenOffset = -1;
2570        } else {
2571            start = to;
2572            end = from;
2573            inBetweenOffset = 1;
2574        }
2575
2576        for (int i = 0; i < childCount; i++) {
2577            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
2578            if (holder == null || holder.mPosition < start || holder.mPosition > end) {
2579                continue;
2580            }
2581            if (DEBUG) {
2582                Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder " +
2583                        holder);
2584            }
2585            if (holder.mPosition == from) {
2586                holder.offsetPosition(to - from, false);
2587            } else {
2588                holder.offsetPosition(inBetweenOffset, false);
2589            }
2590
2591            mState.mStructureChanged = true;
2592        }
2593        mRecycler.offsetPositionRecordsForMove(from, to);
2594        requestLayout();
2595    }
2596
2597    void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
2598        final int childCount = mChildHelper.getUnfilteredChildCount();
2599        for (int i = 0; i < childCount; i++) {
2600            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
2601            if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
2602                if (DEBUG) {
2603                    Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder " +
2604                            holder + " now at position " + (holder.mPosition + itemCount));
2605                }
2606                holder.offsetPosition(itemCount, false);
2607                mState.mStructureChanged = true;
2608            }
2609        }
2610        mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
2611        requestLayout();
2612    }
2613
2614    void offsetPositionRecordsForRemove(int positionStart, int itemCount,
2615            boolean applyToPreLayout) {
2616        final int positionEnd = positionStart + itemCount;
2617        final int childCount = mChildHelper.getUnfilteredChildCount();
2618        for (int i = 0; i < childCount; i++) {
2619            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
2620            if (holder != null && !holder.shouldIgnore()) {
2621                if (holder.mPosition >= positionEnd) {
2622                    if (DEBUG) {
2623                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +
2624                                " holder " + holder + " now at position " +
2625                                (holder.mPosition - itemCount));
2626                    }
2627                    holder.offsetPosition(-itemCount, applyToPreLayout);
2628                    mState.mStructureChanged = true;
2629                } else if (holder.mPosition >= positionStart) {
2630                    if (DEBUG) {
2631                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +
2632                                " holder " + holder + " now REMOVED");
2633                    }
2634                    holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
2635                            applyToPreLayout);
2636                    mState.mStructureChanged = true;
2637                }
2638            }
2639        }
2640        mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
2641        requestLayout();
2642    }
2643
2644    /**
2645     * Rebind existing views for the given range, or create as needed.
2646     *
2647     * @param positionStart Adapter position to start at
2648     * @param itemCount Number of views that must explicitly be rebound
2649     */
2650    void viewRangeUpdate(int positionStart, int itemCount) {
2651        final int childCount = mChildHelper.getUnfilteredChildCount();
2652        final int positionEnd = positionStart + itemCount;
2653
2654        for (int i = 0; i < childCount; i++) {
2655            final View child = mChildHelper.getUnfilteredChildAt(i);
2656            final ViewHolder holder = getChildViewHolderInt(child);
2657            if (holder == null || holder.shouldIgnore()) {
2658                continue;
2659            }
2660            if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
2661                // We re-bind these view holders after pre-processing is complete so that
2662                // ViewHolders have their final positions assigned.
2663                holder.addFlags(ViewHolder.FLAG_UPDATE);
2664                if (supportsChangeAnimations()) {
2665                    holder.addFlags(ViewHolder.FLAG_CHANGED);
2666                }
2667                // lp cannot be null since we get ViewHolder from it.
2668                ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
2669            }
2670        }
2671        mRecycler.viewRangeUpdate(positionStart, itemCount);
2672    }
2673
2674    void rebindUpdatedViewHolders() {
2675        final int childCount = mChildHelper.getChildCount();
2676        for (int i = 0; i < childCount; i++) {
2677            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
2678            // validate type is correct
2679            if (holder == null || holder.shouldIgnore()) {
2680                continue;
2681            }
2682            if (holder.isRemoved() || holder.isInvalid()) {
2683                requestLayout();
2684            } else if (holder.needsUpdate()) {
2685                final int type = mAdapter.getItemViewType(holder.mPosition);
2686                if (holder.getItemViewType() == type) {
2687                    // Binding an attached view will request a layout if needed.
2688                    if (!holder.isChanged() || !supportsChangeAnimations()) {
2689                        mAdapter.bindViewHolder(holder, holder.mPosition);
2690                    } else {
2691                        // Don't rebind changed holders if change animations are enabled.
2692                        // We want the old contents for the animation and will get a new
2693                        // holder for the new contents.
2694                        requestLayout();
2695                    }
2696                } else {
2697                    // binding to a new view will need re-layout anyways. We can as well trigger
2698                    // it here so that it happens during layout
2699                    holder.addFlags(ViewHolder.FLAG_INVALID);
2700                    requestLayout();
2701                }
2702            }
2703        }
2704    }
2705
2706    private void setDataSetChangedAfterLayout() {
2707        if (mDataSetHasChangedAfterLayout) {
2708            return;
2709        }
2710        mDataSetHasChangedAfterLayout = true;
2711        final int childCount = mChildHelper.getUnfilteredChildCount();
2712        for (int i = 0; i < childCount; i++) {
2713            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
2714            if (holder != null && !holder.shouldIgnore()) {
2715                holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
2716            }
2717        }
2718        mRecycler.setAdapterPositionsAsUnknown();
2719    }
2720
2721    /**
2722     * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
2723     * data change event.
2724     */
2725    void markKnownViewsInvalid() {
2726        final int childCount = mChildHelper.getUnfilteredChildCount();
2727        for (int i = 0; i < childCount; i++) {
2728            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
2729            if (holder != null && !holder.shouldIgnore()) {
2730                holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
2731            }
2732        }
2733        markItemDecorInsetsDirty();
2734        mRecycler.markKnownViewsInvalid();
2735    }
2736
2737    /**
2738     * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
2739     * will trigger a {@link #requestLayout()} call.
2740     */
2741    public void invalidateItemDecorations() {
2742        if (mItemDecorations.size() == 0) {
2743            return;
2744        }
2745        if (mLayout != null) {
2746            mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
2747                    + " or layout");
2748        }
2749        markItemDecorInsetsDirty();
2750        requestLayout();
2751    }
2752
2753    /**
2754     * Retrieve the {@link ViewHolder} for the given child view.
2755     *
2756     * @param child Child of this RecyclerView to query for its ViewHolder
2757     * @return The child view's ViewHolder
2758     */
2759    public ViewHolder getChildViewHolder(View child) {
2760        final ViewParent parent = child.getParent();
2761        if (parent != null && parent != this) {
2762            throw new IllegalArgumentException("View " + child + " is not a direct child of " +
2763                    this);
2764        }
2765        return getChildViewHolderInt(child);
2766    }
2767
2768    static ViewHolder getChildViewHolderInt(View child) {
2769        if (child == null) {
2770            return null;
2771        }
2772        return ((LayoutParams) child.getLayoutParams()).mViewHolder;
2773    }
2774
2775    /**
2776     * @deprecated use {@link #getChildAdapterPosition(View)} or
2777     * {@link #getChildLayoutPosition(View)}.
2778     */
2779    @Deprecated
2780    public int getChildPosition(View child) {
2781        return getChildAdapterPosition(child);
2782    }
2783
2784    /**
2785     * Return the adapter position that the given child view corresponds to.
2786     *
2787     * @param child Child View to query
2788     * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
2789     */
2790    public int getChildAdapterPosition(View child) {
2791        final ViewHolder holder = getChildViewHolderInt(child);
2792        return holder != null ? holder.getAdapterPosition() : NO_POSITION;
2793    }
2794
2795    /**
2796     * Return the adapter position of the given child view as of the latest completed layout pass.
2797     * <p>
2798     * This position may not be equal to Item's adapter position if there are pending changes
2799     * in the adapter which have not been reflected to the layout yet.
2800     *
2801     * @param child Child View to query
2802     * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
2803     * the View is representing a removed item.
2804     */
2805    public int getChildLayoutPosition(View child) {
2806        final ViewHolder holder = getChildViewHolderInt(child);
2807        return holder != null ? holder.getLayoutPosition() : NO_POSITION;
2808    }
2809
2810    /**
2811     * Return the stable item id that the given child view corresponds to.
2812     *
2813     * @param child Child View to query
2814     * @return Item id corresponding to the given view or {@link #NO_ID}
2815     */
2816    public long getChildItemId(View child) {
2817        if (mAdapter == null || !mAdapter.hasStableIds()) {
2818            return NO_ID;
2819        }
2820        final ViewHolder holder = getChildViewHolderInt(child);
2821        return holder != null ? holder.getItemId() : NO_ID;
2822    }
2823
2824    /**
2825     * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
2826     * {@link #findViewHolderForAdapterPosition(int)}
2827     */
2828    @Deprecated
2829    public ViewHolder findViewHolderForPosition(int position) {
2830        return findViewHolderForPosition(position, false);
2831    }
2832
2833    /**
2834     * Return the ViewHolder for the item in the given position of the data set as of the latest
2835     * layout pass.
2836     * <p>
2837     * This method checks only the children of RecyclerView. If the item at the given
2838     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
2839     * <p>
2840     * Note that when Adapter contents change, ViewHolder positions are not updated until the
2841     * next layout calculation. If there are pending adapter updates, the return value of this
2842     * method may not match your adapter contents. You can use
2843     * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
2844     *
2845     * @param position The position of the item in the data set of the adapter
2846     * @return The ViewHolder at <code>position</code> or null if there is no such item
2847     */
2848    public ViewHolder findViewHolderForLayoutPosition(int position) {
2849        return findViewHolderForPosition(position, false);
2850    }
2851
2852    /**
2853     * Return the ViewHolder for the item in the given position of the data set. Unlike
2854     * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
2855     * adapter changes that may not be reflected to the layout yet. On the other hand, if
2856     * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
2857     * calculated yet, this method will return <code>null</code> since the new positions of views
2858     * are unknown until the layout is calculated.
2859     * <p>
2860     * This method checks only the children of RecyclerView. If the item at the given
2861     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
2862     *
2863     * @param position The position of the item in the data set of the adapter
2864     * @return The ViewHolder at <code>position</code> or null if there is no such item
2865     */
2866    public ViewHolder findViewHolderForAdapterPosition(int position) {
2867        if (mDataSetHasChangedAfterLayout) {
2868            return null;
2869        }
2870        final int childCount = mChildHelper.getUnfilteredChildCount();
2871        for (int i = 0; i < childCount; i++) {
2872            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
2873            if (holder != null && !holder.isRemoved() && getAdapterPositionFor(holder) == position) {
2874                return holder;
2875            }
2876        }
2877        return null;
2878    }
2879
2880    ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
2881        final int childCount = mChildHelper.getUnfilteredChildCount();
2882        for (int i = 0; i < childCount; i++) {
2883            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
2884            if (holder != null && !holder.isRemoved()) {
2885                if (checkNewPosition) {
2886                    if (holder.mPosition == position) {
2887                        return holder;
2888                    }
2889                } else if (holder.getLayoutPosition() == position) {
2890                    return holder;
2891                }
2892            }
2893        }
2894        // This method should not query cached views. It creates a problem during adapter updates
2895        // when we are dealing with already laid out views. Also, for the public method, it is more
2896        // reasonable to return null if position is not laid out.
2897        return null;
2898    }
2899
2900    /**
2901     * Return the ViewHolder for the item with the given id. The RecyclerView must
2902     * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
2903     * return a non-null value.
2904     * <p>
2905     * This method checks only the children of RecyclerView. If the item with the given
2906     * <code>id</code> is not laid out, it <em>will not</em> create a new one.
2907     *
2908     * @param id The id for the requested item
2909     * @return The ViewHolder with the given <code>id</code> or null if there is no such item
2910     */
2911    public ViewHolder findViewHolderForItemId(long id) {
2912        final int childCount = mChildHelper.getUnfilteredChildCount();
2913        for (int i = 0; i < childCount; i++) {
2914            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
2915            if (holder != null && holder.getItemId() == id) {
2916                return holder;
2917            }
2918        }
2919        // this method should not query cached views. They are not children so they
2920        // should not be returned in this public method
2921        return null;
2922    }
2923
2924    /**
2925     * Find the topmost view under the given point.
2926     *
2927     * @param x Horizontal position in pixels to search
2928     * @param y Vertical position in pixels to search
2929     * @return The child view under (x, y) or null if no matching child is found
2930     */
2931    public View findChildViewUnder(float x, float y) {
2932        final int count = mChildHelper.getChildCount();
2933        for (int i = count - 1; i >= 0; i--) {
2934            final View child = mChildHelper.getChildAt(i);
2935            final float translationX = ViewCompat.getTranslationX(child);
2936            final float translationY = ViewCompat.getTranslationY(child);
2937            if (x >= child.getLeft() + translationX &&
2938                    x <= child.getRight() + translationX &&
2939                    y >= child.getTop() + translationY &&
2940                    y <= child.getBottom() + translationY) {
2941                return child;
2942            }
2943        }
2944        return null;
2945    }
2946
2947    /**
2948     * Offset the bounds of all child views by <code>dy</code> pixels.
2949     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
2950     *
2951     * @param dy Vertical pixel offset to apply to the bounds of all child views
2952     */
2953    public void offsetChildrenVertical(int dy) {
2954        final int childCount = mChildHelper.getChildCount();
2955        for (int i = 0; i < childCount; i++) {
2956            mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
2957        }
2958    }
2959
2960    /**
2961     * Called when an item view is attached to this RecyclerView.
2962     *
2963     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
2964     * of child views as they become attached. This will be called before a
2965     * {@link LayoutManager} measures or lays out the view and is a good time to perform these
2966     * changes.</p>
2967     *
2968     * @param child Child view that is now attached to this RecyclerView and its associated window
2969     */
2970    public void onChildAttachedToWindow(View child) {
2971    }
2972
2973    /**
2974     * Called when an item view is detached from this RecyclerView.
2975     *
2976     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
2977     * of child views as they become detached. This will be called as a
2978     * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
2979     *
2980     * @param child Child view that is now detached from this RecyclerView and its associated window
2981     */
2982    public void onChildDetachedFromWindow(View child) {
2983    }
2984
2985    /**
2986     * Offset the bounds of all child views by <code>dx</code> pixels.
2987     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
2988     *
2989     * @param dx Horizontal pixel offset to apply to the bounds of all child views
2990     */
2991    public void offsetChildrenHorizontal(int dx) {
2992        final int childCount = mChildHelper.getChildCount();
2993        for (int i = 0; i < childCount; i++) {
2994            mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
2995        }
2996    }
2997
2998    Rect getItemDecorInsetsForChild(View child) {
2999        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
3000        if (!lp.mInsetsDirty) {
3001            return lp.mDecorInsets;
3002        }
3003
3004        final Rect insets = lp.mDecorInsets;
3005        insets.set(0, 0, 0, 0);
3006        final int decorCount = mItemDecorations.size();
3007        for (int i = 0; i < decorCount; i++) {
3008            mTempRect.set(0, 0, 0, 0);
3009            mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
3010            insets.left += mTempRect.left;
3011            insets.top += mTempRect.top;
3012            insets.right += mTempRect.right;
3013            insets.bottom += mTempRect.bottom;
3014        }
3015        lp.mInsetsDirty = false;
3016        return insets;
3017    }
3018
3019    private class ViewFlinger implements Runnable {
3020        private int mLastFlingX;
3021        private int mLastFlingY;
3022        private ScrollerCompat mScroller;
3023        private Interpolator mInterpolator = sQuinticInterpolator;
3024
3025
3026        // When set to true, postOnAnimation callbacks are delayed until the run method completes
3027        private boolean mEatRunOnAnimationRequest = false;
3028
3029        // Tracks if postAnimationCallback should be re-attached when it is done
3030        private boolean mReSchedulePostAnimationCallback = false;
3031
3032        public ViewFlinger() {
3033            mScroller = ScrollerCompat.create(getContext(), sQuinticInterpolator);
3034        }
3035
3036        @Override
3037        public void run() {
3038            disableRunOnAnimationRequests();
3039            consumePendingUpdateOperations();
3040            // keep a local reference so that if it is changed during onAnimation method, it won't
3041            // cause unexpected behaviors
3042            final ScrollerCompat scroller = mScroller;
3043            final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
3044            if (scroller.computeScrollOffset()) {
3045                final int x = scroller.getCurrX();
3046                final int y = scroller.getCurrY();
3047                final int dx = x - mLastFlingX;
3048                final int dy = y - mLastFlingY;
3049                int hresult = 0;
3050                int vresult = 0;
3051                mLastFlingX = x;
3052                mLastFlingY = y;
3053                int overscrollX = 0, overscrollY = 0;
3054                if (mAdapter != null) {
3055                    eatRequestLayout();
3056                    mRunningLayoutOrScroll = true;
3057                    if (dx != 0) {
3058                        hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
3059                        overscrollX = dx - hresult;
3060                    }
3061                    if (dy != 0) {
3062                        vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
3063                        overscrollY = dy - vresult;
3064                    }
3065                    if (supportsChangeAnimations()) {
3066                        // Fix up shadow views used by changing animations
3067                        int count = mChildHelper.getChildCount();
3068                        for (int i = 0; i < count; i++) {
3069                            View view = mChildHelper.getChildAt(i);
3070                            ViewHolder holder = getChildViewHolder(view);
3071                            if (holder != null && holder.mShadowingHolder != null) {
3072                                View shadowingView = holder.mShadowingHolder.itemView;
3073                                int left = view.getLeft();
3074                                int top = view.getTop();
3075                                if (left != shadowingView.getLeft() ||
3076                                        top != shadowingView.getTop()) {
3077                                    shadowingView.layout(left, top,
3078                                            left + shadowingView.getWidth(),
3079                                            top + shadowingView.getHeight());
3080                                }
3081                            }
3082                        }
3083                    }
3084
3085                    if (smoothScroller != null && !smoothScroller.isPendingInitialRun() &&
3086                            smoothScroller.isRunning()) {
3087                        final int adapterSize = mState.getItemCount();
3088                        if (adapterSize == 0) {
3089                            smoothScroller.stop();
3090                        } else if (smoothScroller.getTargetPosition() >= adapterSize) {
3091                            smoothScroller.setTargetPosition(adapterSize - 1);
3092                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
3093                        } else {
3094                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
3095                        }
3096                    }
3097                    mRunningLayoutOrScroll = false;
3098                    resumeRequestLayout(false);
3099                }
3100                if (!mItemDecorations.isEmpty()) {
3101                    invalidate();
3102                }
3103                if (ViewCompat.getOverScrollMode(RecyclerView.this) !=
3104                        ViewCompat.OVER_SCROLL_NEVER) {
3105                    considerReleasingGlowsOnScroll(dx, dy);
3106                }
3107                if (overscrollX != 0 || overscrollY != 0) {
3108                    final int vel = (int) scroller.getCurrVelocity();
3109
3110                    int velX = 0;
3111                    if (overscrollX != x) {
3112                        velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
3113                    }
3114
3115                    int velY = 0;
3116                    if (overscrollY != y) {
3117                        velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
3118                    }
3119
3120                    if (ViewCompat.getOverScrollMode(RecyclerView.this) !=
3121                            ViewCompat.OVER_SCROLL_NEVER) {
3122                        absorbGlows(velX, velY);
3123                    }
3124                    if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0) &&
3125                            (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
3126                        scroller.abortAnimation();
3127                    }
3128                }
3129                if (hresult != 0 || vresult != 0) {
3130                    notifyOnScrolled(hresult, vresult);
3131                }
3132
3133                if (!awakenScrollBars()) {
3134                    invalidate();
3135                }
3136
3137                final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
3138                        && vresult == dy;
3139                final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
3140                        && hresult == dx;
3141                final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
3142                        || fullyConsumedVertical;
3143
3144                if (scroller.isFinished() || !fullyConsumedAny) {
3145                    setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this.
3146                } else {
3147                    postOnAnimation();
3148                }
3149            }
3150            // call this after the onAnimation is complete not to have inconsistent callbacks etc.
3151            if (smoothScroller != null && smoothScroller.isPendingInitialRun()) {
3152                smoothScroller.onAnimation(0, 0);
3153            }
3154            enableRunOnAnimationRequests();
3155        }
3156
3157        private void disableRunOnAnimationRequests() {
3158            mReSchedulePostAnimationCallback = false;
3159            mEatRunOnAnimationRequest = true;
3160        }
3161
3162        private void enableRunOnAnimationRequests() {
3163            mEatRunOnAnimationRequest = false;
3164            if (mReSchedulePostAnimationCallback) {
3165                postOnAnimation();
3166            }
3167        }
3168
3169        void postOnAnimation() {
3170            if (mEatRunOnAnimationRequest) {
3171                mReSchedulePostAnimationCallback = true;
3172            } else {
3173                removeCallbacks(this);
3174                ViewCompat.postOnAnimation(RecyclerView.this, this);
3175            }
3176        }
3177
3178        public void fling(int velocityX, int velocityY) {
3179            setScrollState(SCROLL_STATE_SETTLING);
3180            mLastFlingX = mLastFlingY = 0;
3181            mScroller.fling(0, 0, velocityX, velocityY,
3182                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
3183            postOnAnimation();
3184        }
3185
3186        public void smoothScrollBy(int dx, int dy) {
3187            smoothScrollBy(dx, dy, 0, 0);
3188        }
3189
3190        public void smoothScrollBy(int dx, int dy, int vx, int vy) {
3191            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
3192        }
3193
3194        private float distanceInfluenceForSnapDuration(float f) {
3195            f -= 0.5f; // center the values about 0.
3196            f *= 0.3f * Math.PI / 2.0f;
3197            return (float) Math.sin(f);
3198        }
3199
3200        private int computeScrollDuration(int dx, int dy, int vx, int vy) {
3201            final int absDx = Math.abs(dx);
3202            final int absDy = Math.abs(dy);
3203            final boolean horizontal = absDx > absDy;
3204            final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
3205            final int delta = (int) Math.sqrt(dx * dx + dy * dy);
3206            final int containerSize = horizontal ? getWidth() : getHeight();
3207            final int halfContainerSize = containerSize / 2;
3208            final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
3209            final float distance = halfContainerSize + halfContainerSize *
3210                    distanceInfluenceForSnapDuration(distanceRatio);
3211
3212            final int duration;
3213            if (velocity > 0) {
3214                duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
3215            } else {
3216                float absDelta = (float) (horizontal ? absDx : absDy);
3217                duration = (int) (((absDelta / containerSize) + 1) * 300);
3218            }
3219            return Math.min(duration, MAX_SCROLL_DURATION);
3220        }
3221
3222        public void smoothScrollBy(int dx, int dy, int duration) {
3223            smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
3224        }
3225
3226        public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
3227            if (mInterpolator != interpolator) {
3228                mInterpolator = interpolator;
3229                mScroller = ScrollerCompat.create(getContext(), interpolator);
3230            }
3231            setScrollState(SCROLL_STATE_SETTLING);
3232            mLastFlingX = mLastFlingY = 0;
3233            mScroller.startScroll(0, 0, dx, dy, duration);
3234            postOnAnimation();
3235        }
3236
3237        public void stop() {
3238            removeCallbacks(this);
3239            mScroller.abortAnimation();
3240        }
3241
3242    }
3243
3244    private void notifyOnScrolled(int hresult, int vresult) {
3245        // dummy values, View's implementation does not use these.
3246        onScrollChanged(0, 0, 0, 0);
3247        if (mScrollListener != null) {
3248            mScrollListener.onScrolled(this, hresult, vresult);
3249        }
3250    }
3251
3252    private class RecyclerViewDataObserver extends AdapterDataObserver {
3253        @Override
3254        public void onChanged() {
3255            assertNotInLayoutOrScroll(null);
3256            if (mAdapter.hasStableIds()) {
3257                // TODO Determine what actually changed.
3258                // This is more important to implement now since this callback will disable all
3259                // animations because we cannot rely on positions.
3260                mState.mStructureChanged = true;
3261                setDataSetChangedAfterLayout();
3262            } else {
3263                mState.mStructureChanged = true;
3264                setDataSetChangedAfterLayout();
3265            }
3266            if (!mAdapterHelper.hasPendingUpdates()) {
3267                requestLayout();
3268            }
3269        }
3270
3271        @Override
3272        public void onItemRangeChanged(int positionStart, int itemCount) {
3273            assertNotInLayoutOrScroll(null);
3274            if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount)) {
3275                triggerUpdateProcessor();
3276            }
3277        }
3278
3279        @Override
3280        public void onItemRangeInserted(int positionStart, int itemCount) {
3281            assertNotInLayoutOrScroll(null);
3282            if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
3283                triggerUpdateProcessor();
3284            }
3285        }
3286
3287        @Override
3288        public void onItemRangeRemoved(int positionStart, int itemCount) {
3289            assertNotInLayoutOrScroll(null);
3290            if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
3291                triggerUpdateProcessor();
3292            }
3293        }
3294
3295        @Override
3296        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
3297            assertNotInLayoutOrScroll(null);
3298            if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
3299                triggerUpdateProcessor();
3300            }
3301        }
3302
3303        void triggerUpdateProcessor() {
3304            if (mPostUpdatesOnAnimation && mHasFixedSize && mIsAttached) {
3305                ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
3306            } else {
3307                mAdapterUpdateDuringMeasure = true;
3308                requestLayout();
3309            }
3310        }
3311    }
3312
3313    /**
3314     * RecycledViewPool lets you share Views between multiple RecyclerViews.
3315     * <p>
3316     * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
3317     * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
3318     * <p>
3319     * RecyclerView automatically creates a pool for itself if you don't provide one.
3320     *
3321     */
3322    public static class RecycledViewPool {
3323        private SparseArray<ArrayList<ViewHolder>> mScrap =
3324                new SparseArray<ArrayList<ViewHolder>>();
3325        private SparseIntArray mMaxScrap = new SparseIntArray();
3326        private int mAttachCount = 0;
3327
3328        private static final int DEFAULT_MAX_SCRAP = 5;
3329
3330        public void clear() {
3331            mScrap.clear();
3332        }
3333
3334        public void setMaxRecycledViews(int viewType, int max) {
3335            mMaxScrap.put(viewType, max);
3336            final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
3337            if (scrapHeap != null) {
3338                while (scrapHeap.size() > max) {
3339                    scrapHeap.remove(scrapHeap.size() - 1);
3340                }
3341            }
3342        }
3343
3344        public ViewHolder getRecycledView(int viewType) {
3345            final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
3346            if (scrapHeap != null && !scrapHeap.isEmpty()) {
3347                final int index = scrapHeap.size() - 1;
3348                final ViewHolder scrap = scrapHeap.get(index);
3349                scrapHeap.remove(index);
3350                return scrap;
3351            }
3352            return null;
3353        }
3354
3355        int size() {
3356            int count = 0;
3357            for (int i = 0; i < mScrap.size(); i ++) {
3358                ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i);
3359                if (viewHolders != null) {
3360                    count += viewHolders.size();
3361                }
3362            }
3363            return count;
3364        }
3365
3366        public void putRecycledView(ViewHolder scrap) {
3367            final int viewType = scrap.getItemViewType();
3368            final ArrayList scrapHeap = getScrapHeapForType(viewType);
3369            if (mMaxScrap.get(viewType) <= scrapHeap.size()) {
3370                return;
3371            }
3372            scrap.resetInternal();
3373            scrapHeap.add(scrap);
3374        }
3375
3376        void attach(Adapter adapter) {
3377            mAttachCount++;
3378        }
3379
3380        void detach() {
3381            mAttachCount--;
3382        }
3383
3384
3385        /**
3386         * Detaches the old adapter and attaches the new one.
3387         * <p>
3388         * RecycledViewPool will clear its cache if it has only one adapter attached and the new
3389         * adapter uses a different ViewHolder than the oldAdapter.
3390         *
3391         * @param oldAdapter The previous adapter instance. Will be detached.
3392         * @param newAdapter The new adapter instance. Will be attached.
3393         * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
3394         *                               ViewHolder and view types.
3395         */
3396        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
3397                boolean compatibleWithPrevious) {
3398            if (oldAdapter != null) {
3399                detach();
3400            }
3401            if (!compatibleWithPrevious && mAttachCount == 0) {
3402                clear();
3403            }
3404            if (newAdapter != null) {
3405                attach(newAdapter);
3406            }
3407        }
3408
3409        private ArrayList<ViewHolder> getScrapHeapForType(int viewType) {
3410            ArrayList<ViewHolder> scrap = mScrap.get(viewType);
3411            if (scrap == null) {
3412                scrap = new ArrayList<ViewHolder>();
3413                mScrap.put(viewType, scrap);
3414                if (mMaxScrap.indexOfKey(viewType) < 0) {
3415                    mMaxScrap.put(viewType, DEFAULT_MAX_SCRAP);
3416                }
3417            }
3418            return scrap;
3419        }
3420    }
3421
3422    /**
3423     * A Recycler is responsible for managing scrapped or detached item views for reuse.
3424     *
3425     * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
3426     * that has been marked for removal or reuse.</p>
3427     *
3428     * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
3429     * an adapter's data set representing the data at a given position or item ID.
3430     * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
3431     * If not, the view can be quickly reused by the LayoutManager with no further work.
3432     * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
3433     * may be repositioned by a LayoutManager without remeasurement.</p>
3434     */
3435    public final class Recycler {
3436        final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<ViewHolder>();
3437        private ArrayList<ViewHolder> mChangedScrap = null;
3438
3439        final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
3440
3441        private final List<ViewHolder>
3442                mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
3443
3444        private int mViewCacheMax = DEFAULT_CACHE_SIZE;
3445
3446        private RecycledViewPool mRecyclerPool;
3447
3448        private ViewCacheExtension mViewCacheExtension;
3449
3450        private static final int DEFAULT_CACHE_SIZE = 2;
3451
3452        /**
3453         * Clear scrap views out of this recycler. Detached views contained within a
3454         * recycled view pool will remain.
3455         */
3456        public void clear() {
3457            mAttachedScrap.clear();
3458            recycleAndClearCachedViews();
3459        }
3460
3461        /**
3462         * Set the maximum number of detached, valid views we should retain for later use.
3463         *
3464         * @param viewCount Number of views to keep before sending views to the shared pool
3465         */
3466        public void setViewCacheSize(int viewCount) {
3467            mViewCacheMax = viewCount;
3468            // first, try the views that can be recycled
3469            for (int i = mCachedViews.size() - 1; i >= 0 && mCachedViews.size() > viewCount; i--) {
3470                recycleCachedViewAt(i);
3471            }
3472        }
3473
3474        /**
3475         * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
3476         *
3477         * @return List of ViewHolders in the scrap list.
3478         */
3479        public List<ViewHolder> getScrapList() {
3480            return mUnmodifiableAttachedScrap;
3481        }
3482
3483        /**
3484         * Helper method for getViewForPosition.
3485         * <p>
3486         * Checks whether a given view holder can be used for the provided position.
3487         *
3488         * @param holder ViewHolder
3489         * @return true if ViewHolder matches the provided position, false otherwise
3490         */
3491        boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
3492            // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
3493            // if it is not removed, verify the type and id.
3494            if (holder.isRemoved()) {
3495                return true;
3496            }
3497            if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
3498                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
3499                        + "adapter position" + holder);
3500            }
3501            if (!mState.isPreLayout()) {
3502                // don't check type if it is pre-layout.
3503                final int type = mAdapter.getItemViewType(holder.mPosition);
3504                if (type != holder.getItemViewType()) {
3505                    return false;
3506                }
3507            }
3508            if (mAdapter.hasStableIds()) {
3509                return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
3510            }
3511            return true;
3512        }
3513
3514        /**
3515         * Binds the given View to the position. The View can be a View previously retrieved via
3516         * {@link #getViewForPosition(int)} or created by
3517         * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
3518         * <p>
3519         * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
3520         * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
3521         * wants to handle its own recycling logic.
3522         * <p>
3523         * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
3524         * you don't need to call this method unless you want to bind this View to another position.
3525         *
3526         * @param view The view to update.
3527         * @param position The position of the item to bind to this View.
3528         */
3529        public void bindViewToPosition(View view, int position) {
3530            ViewHolder holder = getChildViewHolderInt(view);
3531            if (holder == null) {
3532                throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
3533                        + " pass arbitrary views to this method, they should be created by the "
3534                        + "Adapter");
3535            }
3536            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
3537            if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
3538                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
3539                        + "position " + position + "(offset:" + offsetPosition + ")."
3540                        + "state:" + mState.getItemCount());
3541            }
3542            mAdapter.bindViewHolder(holder, offsetPosition);
3543            attachAccessibilityDelegate(view);
3544            if (mState.isPreLayout()) {
3545                holder.mPreLayoutPosition = position;
3546            }
3547
3548            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
3549            final LayoutParams rvLayoutParams;
3550            if (lp == null) {
3551                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
3552                holder.itemView.setLayoutParams(rvLayoutParams);
3553            } else if (!checkLayoutParams(lp)) {
3554                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
3555                holder.itemView.setLayoutParams(rvLayoutParams);
3556            } else {
3557                rvLayoutParams = (LayoutParams) lp;
3558            }
3559
3560            rvLayoutParams.mInsetsDirty = true;
3561            rvLayoutParams.mViewHolder = holder;
3562            rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
3563        }
3564
3565        /**
3566         * RecyclerView provides artificial position range (item count) in pre-layout state and
3567         * automatically maps these positions to {@link Adapter} positions when
3568         * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
3569         * <p>
3570         * Usually, LayoutManager does not need to worry about this. However, in some cases, your
3571         * LayoutManager may need to call some custom component with item positions in which
3572         * case you need the actual adapter position instead of the pre layout position. You
3573         * can use this method to convert a pre-layout position to adapter (post layout) position.
3574         * <p>
3575         * Note that if the provided position belongs to a deleted ViewHolder, this method will
3576         * return -1.
3577         * <p>
3578         * Calling this method in post-layout state returns the same value back.
3579         *
3580         * @param position The pre-layout position to convert. Must be greater or equal to 0 and
3581         *                 less than {@link State#getItemCount()}.
3582         */
3583        public int convertPreLayoutPositionToPostLayout(int position) {
3584            if (position < 0 || position >= mState.getItemCount()) {
3585                throw new IndexOutOfBoundsException("invalid position " + position + ". State "
3586                        + "item count is " + mState.getItemCount());
3587            }
3588            if (!mState.isPreLayout()) {
3589                return position;
3590            }
3591            return mAdapterHelper.findPositionOffset(position);
3592        }
3593
3594        /**
3595         * Obtain a view initialized for the given position.
3596         *
3597         * This method should be used by {@link LayoutManager} implementations to obtain
3598         * views to represent data from an {@link Adapter}.
3599         * <p>
3600         * The Recycler may reuse a scrap or detached view from a shared pool if one is
3601         * available for the correct view type. If the adapter has not indicated that the
3602         * data at the given position has changed, the Recycler will attempt to hand back
3603         * a scrap view that was previously initialized for that data without rebinding.
3604         *
3605         * @param position Position to obtain a view for
3606         * @return A view representing the data at <code>position</code> from <code>adapter</code>
3607         */
3608        public View getViewForPosition(int position) {
3609            return getViewForPosition(position, false);
3610        }
3611
3612        View getViewForPosition(int position, boolean dryRun) {
3613            if (position < 0 || position >= mState.getItemCount()) {
3614                throw new IndexOutOfBoundsException("Invalid item position " + position
3615                        + "(" + position + "). Item count:" + mState.getItemCount());
3616            }
3617            boolean fromScrap = false;
3618            ViewHolder holder = null;
3619            // 0) If there is a changed scrap, try to find from there
3620            if (mState.isPreLayout()) {
3621                holder = getChangedScrapViewForPosition(position);
3622                fromScrap = holder != null;
3623            }
3624            // 1) Find from scrap by position
3625            if (holder == null) {
3626                holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);
3627                if (holder != null) {
3628                    if (!validateViewHolderForOffsetPosition(holder)) {
3629                        // recycle this scrap
3630                        if (!dryRun) {
3631                            // we would like to recycle this but need to make sure it is not used by
3632                            // animation logic etc.
3633                            holder.addFlags(ViewHolder.FLAG_INVALID);
3634                            if (holder.isScrap()) {
3635                                removeDetachedView(holder.itemView, false);
3636                                holder.unScrap();
3637                            } else if (holder.wasReturnedFromScrap()) {
3638                                holder.clearReturnedFromScrapFlag();
3639                            }
3640                            recycleViewHolderInternal(holder);
3641                        }
3642                        holder = null;
3643                    } else {
3644                        fromScrap = true;
3645                    }
3646                }
3647            }
3648            if (holder == null) {
3649                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
3650                if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
3651                    throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
3652                            + "position " + position + "(offset:" + offsetPosition + ")."
3653                            + "state:" + mState.getItemCount());
3654                }
3655
3656                final int type = mAdapter.getItemViewType(offsetPosition);
3657                // 2) Find from scrap via stable ids, if exists
3658                if (mAdapter.hasStableIds()) {
3659                    holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
3660                    if (holder != null) {
3661                        // update position
3662                        holder.mPosition = offsetPosition;
3663                        fromScrap = true;
3664                    }
3665                }
3666                if (holder == null && mViewCacheExtension != null) {
3667                    // We are NOT sending the offsetPosition because LayoutManager does not
3668                    // know it.
3669                    final View view = mViewCacheExtension
3670                            .getViewForPositionAndType(this, position, type);
3671                    if (view != null) {
3672                        holder = getChildViewHolder(view);
3673                        if (holder == null) {
3674                            throw new IllegalArgumentException("getViewForPositionAndType returned"
3675                                    + " a view which does not have a ViewHolder");
3676                        } else if (holder.shouldIgnore()) {
3677                            throw new IllegalArgumentException("getViewForPositionAndType returned"
3678                                    + " a view that is ignored. You must call stopIgnoring before"
3679                                    + " returning this view.");
3680                        }
3681                    }
3682                }
3683                if (holder == null) { // fallback to recycler
3684                    // try recycler.
3685                    // Head to the shared pool.
3686                    if (DEBUG) {
3687                        Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "
3688                                + "pool");
3689                    }
3690                    holder = getRecycledViewPool()
3691                            .getRecycledView(mAdapter.getItemViewType(offsetPosition));
3692                    if (holder != null) {
3693                        holder.resetInternal();
3694                        if (FORCE_INVALIDATE_DISPLAY_LIST) {
3695                            invalidateDisplayListInt(holder);
3696                        }
3697                    }
3698                }
3699                if (holder == null) {
3700                    holder = mAdapter.createViewHolder(RecyclerView.this,
3701                            mAdapter.getItemViewType(offsetPosition));
3702                    if (DEBUG) {
3703                        Log.d(TAG, "getViewForPosition created new ViewHolder");
3704                    }
3705                }
3706            }
3707            boolean bound = false;
3708            if (mState.isPreLayout() && holder.isBound()) {
3709                // do not update unless we absolutely have to.
3710                holder.mPreLayoutPosition = position;
3711            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
3712                if (DEBUG && holder.isRemoved()) {
3713                    throw new IllegalStateException("Removed holder should be bound and it should"
3714                            + " come here only in pre-layout. Holder: " + holder);
3715                }
3716                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
3717                mAdapter.bindViewHolder(holder, offsetPosition);
3718                attachAccessibilityDelegate(holder.itemView);
3719                bound = true;
3720                if (mState.isPreLayout()) {
3721                    holder.mPreLayoutPosition = position;
3722                }
3723            }
3724
3725            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
3726            final LayoutParams rvLayoutParams;
3727            if (lp == null) {
3728                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
3729                holder.itemView.setLayoutParams(rvLayoutParams);
3730            } else if (!checkLayoutParams(lp)) {
3731                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
3732                holder.itemView.setLayoutParams(rvLayoutParams);
3733            } else {
3734                rvLayoutParams = (LayoutParams) lp;
3735            }
3736            rvLayoutParams.mViewHolder = holder;
3737            rvLayoutParams.mPendingInvalidate = fromScrap && bound;
3738            return holder.itemView;
3739        }
3740
3741        private void attachAccessibilityDelegate(View itemView) {
3742            if (mAccessibilityManager != null && mAccessibilityManager.isEnabled()) {
3743                if (ViewCompat.getImportantForAccessibility(itemView) ==
3744                        ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
3745                    ViewCompat.setImportantForAccessibility(itemView,
3746                            ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
3747                }
3748                if (!ViewCompat.hasAccessibilityDelegate(itemView)) {
3749                    ViewCompat.setAccessibilityDelegate(itemView,
3750                            mAccessibilityDelegate.getItemDelegate());
3751                }
3752            }
3753        }
3754
3755        private void invalidateDisplayListInt(ViewHolder holder) {
3756            if (holder.itemView instanceof ViewGroup) {
3757                invalidateDisplayListInt((ViewGroup) holder.itemView, false);
3758            }
3759        }
3760
3761        private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
3762            for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
3763                final View view = viewGroup.getChildAt(i);
3764                if (view instanceof ViewGroup) {
3765                    invalidateDisplayListInt((ViewGroup) view, true);
3766                }
3767            }
3768            if (!invalidateThis) {
3769                return;
3770            }
3771            // we need to force it to become invisible
3772            if (viewGroup.getVisibility() == View.INVISIBLE) {
3773                viewGroup.setVisibility(View.VISIBLE);
3774                viewGroup.setVisibility(View.INVISIBLE);
3775            } else {
3776                final int visibility = viewGroup.getVisibility();
3777                viewGroup.setVisibility(View.INVISIBLE);
3778                viewGroup.setVisibility(visibility);
3779            }
3780        }
3781
3782        /**
3783         * Recycle a detached view. The specified view will be added to a pool of views
3784         * for later rebinding and reuse.
3785         *
3786         * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
3787         * View is scrapped, it will be removed from scrap list.</p>
3788         *
3789         * @param view Removed view for recycling
3790         * @see LayoutManager#removeAndRecycleView(View, Recycler)
3791         */
3792        public void recycleView(View view) {
3793            // This public recycle method tries to make view recycle-able since layout manager
3794            // intended to recycle this view (e.g. even if it is in scrap or change cache)
3795            ViewHolder holder = getChildViewHolderInt(view);
3796            if (holder.isTmpDetached()) {
3797                removeDetachedView(view, false);
3798            }
3799            if (holder.isScrap()) {
3800                holder.unScrap();
3801            } else if (holder.wasReturnedFromScrap()){
3802                holder.clearReturnedFromScrapFlag();
3803            }
3804            recycleViewHolderInternal(holder);
3805        }
3806
3807        /**
3808         * Internally, use this method instead of {@link #recycleView(android.view.View)} to
3809         * catch potential bugs.
3810         * @param view
3811         */
3812        void recycleViewInternal(View view) {
3813            recycleViewHolderInternal(getChildViewHolderInt(view));
3814        }
3815
3816        void recycleAndClearCachedViews() {
3817            final int count = mCachedViews.size();
3818            for (int i = count - 1; i >= 0; i--) {
3819                recycleCachedViewAt(i);
3820            }
3821            mCachedViews.clear();
3822        }
3823
3824        /**
3825         * Recycles a cached view and removes the view from the list. Views are added to cache
3826         * if and only if they are recyclable, so this method does not check it again.
3827         * <p>
3828         * A small exception to this rule is when the view does not have an animator reference
3829         * but transient state is true (due to animations created outside ItemAnimator). In that
3830         * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
3831         * still recyclable since Adapter wants to do so.
3832         *
3833         * @param cachedViewIndex The index of the view in cached views list
3834         */
3835        void recycleCachedViewAt(int cachedViewIndex) {
3836            if (DEBUG) {
3837                Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
3838            }
3839            ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
3840            if (DEBUG) {
3841                Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
3842            }
3843            addViewHolderToRecycledViewPool(viewHolder);
3844            mCachedViews.remove(cachedViewIndex);
3845        }
3846
3847        /**
3848         * internal implementation checks if view is scrapped or attached and throws an exception
3849         * if so.
3850         * Public version un-scraps before calling recycle.
3851         */
3852        void recycleViewHolderInternal(ViewHolder holder) {
3853            if (holder.isScrap() || holder.itemView.getParent() != null) {
3854                throw new IllegalArgumentException(
3855                        "Scrapped or attached views may not be recycled. isScrap:"
3856                                + holder.isScrap() + " isAttached:"
3857                                + (holder.itemView.getParent() != null));
3858            }
3859
3860            if (holder.isTmpDetached()) {
3861                throw new IllegalArgumentException("Tmp detached view should be removed "
3862                        + "from RecyclerView before it can be recycled: " + holder);
3863            }
3864
3865            if (holder.shouldIgnore()) {
3866                throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
3867                        + " should first call stopIgnoringView(view) before calling recycle.");
3868            }
3869            //noinspection unchecked
3870            final boolean forceRecycle = mAdapter != null
3871                    && holder.doesTransientStatePreventRecycling()
3872                    && mAdapter.onFailedToRecycleView(holder);
3873            if (forceRecycle || holder.isRecyclable()) {
3874                boolean cached = false;
3875                if (!holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved()) &&
3876                        !holder.isChanged()) {
3877                    // Retire oldest cached view
3878                    final int cachedViewSize = mCachedViews.size();
3879                    if (cachedViewSize == mViewCacheMax && cachedViewSize > 0) {
3880                        recycleCachedViewAt(0);
3881                    }
3882                    if (cachedViewSize < mViewCacheMax) {
3883                        mCachedViews.add(holder);
3884                        cached = true;
3885                    }
3886                }
3887                if (!cached) {
3888                    addViewHolderToRecycledViewPool(holder);
3889                }
3890            } else if (DEBUG) {
3891                Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
3892                        + "re-visit here. We are stil removing it from animation lists");
3893            }
3894            // even if the holder is not removed, we still call this method so that it is removed
3895            // from view holder lists.
3896            mState.onViewRecycled(holder);
3897        }
3898
3899        void addViewHolderToRecycledViewPool(ViewHolder holder) {
3900            ViewCompat.setAccessibilityDelegate(holder.itemView, null);
3901            getRecycledViewPool().putRecycledView(holder);
3902            dispatchViewRecycled(holder);
3903        }
3904
3905        /**
3906         * Used as a fast path for unscrapping and recycling a view during a bulk operation.
3907         * The caller must call {@link #clearScrap()} when it's done to update the recycler's
3908         * internal bookkeeping.
3909         */
3910        void quickRecycleScrapView(View view) {
3911            final ViewHolder holder = getChildViewHolderInt(view);
3912            holder.mScrapContainer = null;
3913            holder.clearReturnedFromScrapFlag();
3914            recycleViewHolderInternal(holder);
3915        }
3916
3917        /**
3918         * Mark an attached view as scrap.
3919         *
3920         * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
3921         * for rebinding and reuse. Requests for a view for a given position may return a
3922         * reused or rebound scrap view instance.</p>
3923         *
3924         * @param view View to scrap
3925         */
3926        void scrapView(View view) {
3927            final ViewHolder holder = getChildViewHolderInt(view);
3928            holder.setScrapContainer(this);
3929            if (!holder.isChanged() || !supportsChangeAnimations()) {
3930                if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
3931                    throw new IllegalArgumentException("Called scrap view with an invalid view."
3932                            + " Invalid views cannot be reused from scrap, they should rebound from"
3933                            + " recycler pool.");
3934                }
3935                mAttachedScrap.add(holder);
3936            } else {
3937                if (mChangedScrap == null) {
3938                    mChangedScrap = new ArrayList<ViewHolder>();
3939                }
3940                mChangedScrap.add(holder);
3941            }
3942        }
3943
3944        /**
3945         * Remove a previously scrapped view from the pool of eligible scrap.
3946         *
3947         * <p>This view will no longer be eligible for reuse until re-scrapped or
3948         * until it is explicitly removed and recycled.</p>
3949         */
3950        void unscrapView(ViewHolder holder) {
3951            if (!holder.isChanged() || !supportsChangeAnimations() || mChangedScrap == null) {
3952                mAttachedScrap.remove(holder);
3953            } else {
3954                mChangedScrap.remove(holder);
3955            }
3956            holder.mScrapContainer = null;
3957            holder.clearReturnedFromScrapFlag();
3958        }
3959
3960        int getScrapCount() {
3961            return mAttachedScrap.size();
3962        }
3963
3964        View getScrapViewAt(int index) {
3965            return mAttachedScrap.get(index).itemView;
3966        }
3967
3968        void clearScrap() {
3969            mAttachedScrap.clear();
3970        }
3971
3972        ViewHolder getChangedScrapViewForPosition(int position) {
3973            // If pre-layout, check the changed scrap for an exact match.
3974            final int changedScrapSize;
3975            if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
3976                return null;
3977            }
3978            // find by position
3979            for (int i = 0; i < changedScrapSize; i++) {
3980                final ViewHolder holder = mChangedScrap.get(i);
3981                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
3982                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
3983                    return holder;
3984                }
3985            }
3986            // find by id
3987            if (mAdapter.hasStableIds()) {
3988                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
3989                if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
3990                    final long id = mAdapter.getItemId(offsetPosition);
3991                    for (int i = 0; i < changedScrapSize; i++) {
3992                        final ViewHolder holder = mChangedScrap.get(i);
3993                        if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
3994                            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
3995                            return holder;
3996                        }
3997                    }
3998                }
3999            }
4000            return null;
4001        }
4002
4003        /**
4004         * Returns a scrap view for the position. If type is not INVALID_TYPE, it also checks if
4005         * ViewHolder's type matches the provided type.
4006         *
4007         * @param position Item position
4008         * @param type View type
4009         * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
4010         * @return a ViewHolder that can be re-used for this position.
4011         */
4012        ViewHolder getScrapViewForPosition(int position, int type, boolean dryRun) {
4013            final int scrapCount = mAttachedScrap.size();
4014
4015            // Try first for an exact, non-invalid match from scrap.
4016            for (int i = 0; i < scrapCount; i++) {
4017                final ViewHolder holder = mAttachedScrap.get(i);
4018                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
4019                        && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
4020                    if (type != INVALID_TYPE && holder.getItemViewType() != type) {
4021                        Log.e(TAG, "Scrap view for position " + position + " isn't dirty but has" +
4022                                " wrong view type! (found " + holder.getItemViewType() +
4023                                " but expected " + type + ")");
4024                        break;
4025                    }
4026                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
4027                    return holder;
4028                }
4029            }
4030
4031            if (!dryRun) {
4032                View view = mChildHelper.findHiddenNonRemovedView(position, type);
4033                if (view != null) {
4034                    // ending the animation should cause it to get recycled before we reuse it
4035                    mItemAnimator.endAnimation(getChildViewHolder(view));
4036                }
4037            }
4038
4039            // Search in our first-level recycled view cache.
4040            final int cacheSize = mCachedViews.size();
4041            for (int i = 0; i < cacheSize; i++) {
4042                final ViewHolder holder = mCachedViews.get(i);
4043                // invalid view holders may be in cache if adapter has stable ids as they can be
4044                // retrieved via getScrapViewForId
4045                if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
4046                    if (!dryRun) {
4047                        mCachedViews.remove(i);
4048                    }
4049                    if (DEBUG) {
4050                        Log.d(TAG, "getScrapViewForPosition(" + position + ", " + type +
4051                                ") found match in cache: " + holder);
4052                    }
4053                    return holder;
4054                }
4055            }
4056            return null;
4057        }
4058
4059        ViewHolder getScrapViewForId(long id, int type, boolean dryRun) {
4060            // Look in our attached views first
4061            final int count = mAttachedScrap.size();
4062            for (int i = count - 1; i >= 0; i--) {
4063                final ViewHolder holder = mAttachedScrap.get(i);
4064                if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
4065                    if (type == holder.getItemViewType()) {
4066                        holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
4067                        if (holder.isRemoved()) {
4068                            // this might be valid in two cases:
4069                            // > item is removed but we are in pre-layout pass
4070                            // >> do nothing. return as is. make sure we don't rebind
4071                            // > item is removed then added to another position and we are in
4072                            // post layout.
4073                            // >> remove removed and invalid flags, add update flag to rebind
4074                            // because item was invisible to us and we don't know what happened in
4075                            // between.
4076                            if (!mState.isPreLayout()) {
4077                                holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE |
4078                                        ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
4079                            }
4080                        }
4081                        return holder;
4082                    } else if (!dryRun) {
4083                        // Recycle this scrap. Type mismatch.
4084                        mAttachedScrap.remove(i);
4085                        removeDetachedView(holder.itemView, false);
4086                        quickRecycleScrapView(holder.itemView);
4087                    }
4088                }
4089            }
4090
4091            // Search the first-level cache
4092            final int cacheSize = mCachedViews.size();
4093            for (int i = cacheSize - 1; i >= 0; i--) {
4094                final ViewHolder holder = mCachedViews.get(i);
4095                if (holder.getItemId() == id) {
4096                    if (type == holder.getItemViewType()) {
4097                        if (!dryRun) {
4098                            mCachedViews.remove(i);
4099                        }
4100                        return holder;
4101                    } else if (!dryRun) {
4102                        recycleCachedViewAt(i);
4103                    }
4104                }
4105            }
4106            return null;
4107        }
4108
4109        void dispatchViewRecycled(ViewHolder holder) {
4110            if (mRecyclerListener != null) {
4111                mRecyclerListener.onViewRecycled(holder);
4112            }
4113            if (mAdapter != null) {
4114                mAdapter.onViewRecycled(holder);
4115            }
4116            if (mState != null) {
4117                mState.onViewRecycled(holder);
4118            }
4119            if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
4120        }
4121
4122        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
4123                boolean compatibleWithPrevious) {
4124            clear();
4125            getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
4126        }
4127
4128        void offsetPositionRecordsForMove(int from, int to) {
4129            final int start, end, inBetweenOffset;
4130            if (from < to) {
4131                start = from;
4132                end = to;
4133                inBetweenOffset = -1;
4134            } else {
4135                start = to;
4136                end = from;
4137                inBetweenOffset = 1;
4138            }
4139            final int cachedCount = mCachedViews.size();
4140            for (int i = 0; i < cachedCount; i++) {
4141                final ViewHolder holder = mCachedViews.get(i);
4142                if (holder == null || holder.mPosition < start || holder.mPosition > end) {
4143                    continue;
4144                }
4145                if (holder.mPosition == from) {
4146                    holder.offsetPosition(to - from, false);
4147                } else {
4148                    holder.offsetPosition(inBetweenOffset, false);
4149                }
4150                if (DEBUG) {
4151                    Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder " +
4152                            holder);
4153                }
4154            }
4155        }
4156
4157        void offsetPositionRecordsForInsert(int insertedAt, int count) {
4158            final int cachedCount = mCachedViews.size();
4159            for (int i = 0; i < cachedCount; i++) {
4160                final ViewHolder holder = mCachedViews.get(i);
4161                if (holder != null && holder.getLayoutPosition() >= insertedAt) {
4162                    if (DEBUG) {
4163                        Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder " +
4164                                holder + " now at position " + (holder.mPosition + count));
4165                    }
4166                    holder.offsetPosition(count, true);
4167                }
4168            }
4169        }
4170
4171        /**
4172         * @param removedFrom Remove start index
4173         * @param count Remove count
4174         * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
4175         *                         false, they'll be applied before the second layout pass
4176         */
4177        void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
4178            final int removedEnd = removedFrom + count;
4179            final int cachedCount = mCachedViews.size();
4180            for (int i = cachedCount - 1; i >= 0; i--) {
4181                final ViewHolder holder = mCachedViews.get(i);
4182                if (holder != null) {
4183                    if (holder.getLayoutPosition() >= removedEnd) {
4184                        if (DEBUG) {
4185                            Log.d(TAG, "offsetPositionRecordsForRemove cached " + i +
4186                                    " holder " + holder + " now at position " +
4187                                    (holder.mPosition - count));
4188                        }
4189                        holder.offsetPosition(-count, applyToPreLayout);
4190                    } else if (holder.getLayoutPosition() >= removedFrom) {
4191                        // Item for this view was removed. Dump it from the cache.
4192                        recycleCachedViewAt(i);
4193                    }
4194                }
4195            }
4196        }
4197
4198        void setViewCacheExtension(ViewCacheExtension extension) {
4199            mViewCacheExtension = extension;
4200        }
4201
4202        void setRecycledViewPool(RecycledViewPool pool) {
4203            if (mRecyclerPool != null) {
4204                mRecyclerPool.detach();
4205            }
4206            mRecyclerPool = pool;
4207            if (pool != null) {
4208                mRecyclerPool.attach(getAdapter());
4209            }
4210        }
4211
4212        RecycledViewPool getRecycledViewPool() {
4213            if (mRecyclerPool == null) {
4214                mRecyclerPool = new RecycledViewPool();
4215            }
4216            return mRecyclerPool;
4217        }
4218
4219        void viewRangeUpdate(int positionStart, int itemCount) {
4220            final int positionEnd = positionStart + itemCount;
4221            final int cachedCount = mCachedViews.size();
4222            for (int i = 0; i < cachedCount; i++) {
4223                final ViewHolder holder = mCachedViews.get(i);
4224                if (holder == null) {
4225                    continue;
4226                }
4227
4228                final int pos = holder.getLayoutPosition();
4229                if (pos >= positionStart && pos < positionEnd) {
4230                    holder.addFlags(ViewHolder.FLAG_UPDATE);
4231                    // cached views should not be flagged as changed because this will cause them
4232                    // to animate when they are returned from cache.
4233                }
4234            }
4235        }
4236
4237        void setAdapterPositionsAsUnknown() {
4238            final int cachedCount = mCachedViews.size();
4239            for (int i = 0; i < cachedCount; i++) {
4240                final ViewHolder holder = mCachedViews.get(i);
4241                if (holder != null) {
4242                    holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
4243                }
4244            }
4245        }
4246
4247        void markKnownViewsInvalid() {
4248            if (mAdapter != null && mAdapter.hasStableIds()) {
4249                final int cachedCount = mCachedViews.size();
4250                for (int i = 0; i < cachedCount; i++) {
4251                    final ViewHolder holder = mCachedViews.get(i);
4252                    if (holder != null) {
4253                        holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
4254                    }
4255                }
4256            } else {
4257                // we cannot re-use cached views in this case. Recycle them all
4258                recycleAndClearCachedViews();
4259            }
4260        }
4261
4262        void clearOldPositions() {
4263            final int cachedCount = mCachedViews.size();
4264            for (int i = 0; i < cachedCount; i++) {
4265                final ViewHolder holder = mCachedViews.get(i);
4266                holder.clearOldPosition();
4267            }
4268            final int scrapCount = mAttachedScrap.size();
4269            for (int i = 0; i < scrapCount; i++) {
4270                mAttachedScrap.get(i).clearOldPosition();
4271            }
4272            if (mChangedScrap != null) {
4273                final int changedScrapCount = mChangedScrap.size();
4274                for (int i = 0; i < changedScrapCount; i++) {
4275                    mChangedScrap.get(i).clearOldPosition();
4276                }
4277            }
4278        }
4279
4280        void markItemDecorInsetsDirty() {
4281            final int cachedCount = mCachedViews.size();
4282            for (int i = 0; i < cachedCount; i++) {
4283                final ViewHolder holder = mCachedViews.get(i);
4284                LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
4285                if (layoutParams != null) {
4286                    layoutParams.mInsetsDirty = true;
4287                }
4288            }
4289        }
4290    }
4291
4292    /**
4293     * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
4294     * ben controlled by the developer.
4295     * <p>
4296     * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
4297     * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
4298     * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
4299     * {@link RecycledViewPool}.
4300     * <p>
4301     * Note that, Recycler never sends Views to this method to be cached. It is developers
4302     * responsibility to decide whether they want to keep their Views in this custom cache or let
4303     * the default recycling policy handle it.
4304     */
4305    public abstract static class ViewCacheExtension {
4306
4307        /**
4308         * Returns a View that can be binded to the given Adapter position.
4309         * <p>
4310         * This method should <b>not</b> create a new View. Instead, it is expected to return
4311         * an already created View that can be re-used for the given type and position.
4312         * If the View is marked as ignored, it should first call
4313         * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
4314         * <p>
4315         * RecyclerView will re-bind the returned View to the position if necessary.
4316         *
4317         * @param recycler The Recycler that can be used to bind the View
4318         * @param position The adapter position
4319         * @param type     The type of the View, defined by adapter
4320         * @return A View that is bound to the given position or NULL if there is no View to re-use
4321         * @see LayoutManager#ignoreView(View)
4322         */
4323        abstract public View getViewForPositionAndType(Recycler recycler, int position, int type);
4324    }
4325
4326    /**
4327     * Base class for an Adapter
4328     *
4329     * <p>Adapters provide a binding from an app-specific data set to views that are displayed
4330     * within a {@link RecyclerView}.</p>
4331     */
4332    public static abstract class Adapter<VH extends ViewHolder> {
4333        private final AdapterDataObservable mObservable = new AdapterDataObservable();
4334        private boolean mHasStableIds = false;
4335
4336        /**
4337         * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
4338         * an item.
4339         * <p>
4340         * This new ViewHolder should be constructed with a new View that can represent the items
4341         * of the given type. You can either create a new View manually or inflate it from an XML
4342         * layout file.
4343         * <p>
4344         * The new ViewHolder will be used to display items of the adapter using
4345         * {@link #onBindViewHolder(ViewHolder, int)}. Since it will be re-used to display different
4346         * items in the data set, it is a good idea to cache references to sub views of the View to
4347         * avoid unnecessary {@link View#findViewById(int)} calls.
4348         *
4349         * @param parent The ViewGroup into which the new View will be added after it is bound to
4350         *               an adapter position.
4351         * @param viewType The view type of the new View.
4352         *
4353         * @return A new ViewHolder that holds a View of the given view type.
4354         * @see #getItemViewType(int)
4355         * @see #onBindViewHolder(ViewHolder, int)
4356         */
4357        public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
4358
4359        /**
4360         * Called by RecyclerView to display the data at the specified position. This method
4361         * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
4362         * the given position.
4363         * <p>
4364         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this
4365         * method again if the position of the item changes in the data set unless the item itself
4366         * is invalidated or the new position cannot be determined. For this reason, you should only
4367         * use the <code>position</code> parameter while acquiring the related data item inside this
4368         * method and should not keep a copy of it. If you need the position of an item later on
4369         * (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will have
4370         * the updated adapter position.
4371         *
4372         * @param holder The ViewHolder which should be updated to represent the contents of the
4373         *               item at the given position in the data set.
4374         * @param position The position of the item within the adapter's data set.
4375         */
4376        public abstract void onBindViewHolder(VH holder, int position);
4377
4378        /**
4379         * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
4380         * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
4381         *
4382         * @see #onCreateViewHolder(ViewGroup, int)
4383         */
4384        public final VH createViewHolder(ViewGroup parent, int viewType) {
4385            final VH holder = onCreateViewHolder(parent, viewType);
4386            holder.mItemViewType = viewType;
4387            return holder;
4388        }
4389
4390        /**
4391         * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
4392         * {@link ViewHolder} contents with the item at the given position and also sets up some
4393         * private fields to be used by RecyclerView.
4394         *
4395         * @see #onBindViewHolder(ViewHolder, int)
4396         */
4397        public final void bindViewHolder(VH holder, int position) {
4398            holder.mPosition = position;
4399            if (hasStableIds()) {
4400                holder.mItemId = getItemId(position);
4401            }
4402            onBindViewHolder(holder, position);
4403            holder.setFlags(ViewHolder.FLAG_BOUND,
4404                    ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
4405                    | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
4406        }
4407
4408        /**
4409         * Return the view type of the item at <code>position</code> for the purposes
4410         * of view recycling.
4411         *
4412         * <p>The default implementation of this method returns 0, making the assumption of
4413         * a single view type for the adapter. Unlike ListView adapters, types need not
4414         * be contiguous. Consider using id resources to uniquely identify item view types.
4415         *
4416         * @param position position to query
4417         * @return integer value identifying the type of the view needed to represent the item at
4418         *                 <code>position</code>. Type codes need not be contiguous.
4419         */
4420        public int getItemViewType(int position) {
4421            return 0;
4422        }
4423
4424        /**
4425         * Indicates whether each item in the data set can be represented with a unique identifier
4426         * of type {@link java.lang.Long}.
4427         *
4428         * @param hasStableIds Whether items in data set have unique identifiers or not.
4429         * @see #hasStableIds()
4430         * @see #getItemId(int)
4431         */
4432        public void setHasStableIds(boolean hasStableIds) {
4433            if (hasObservers()) {
4434                throw new IllegalStateException("Cannot change whether this adapter has " +
4435                        "stable IDs while the adapter has registered observers.");
4436            }
4437            mHasStableIds = hasStableIds;
4438        }
4439
4440        /**
4441         * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
4442         * would return false this method should return {@link #NO_ID}. The default implementation
4443         * of this method returns {@link #NO_ID}.
4444         *
4445         * @param position Adapter position to query
4446         * @return the stable ID of the item at position
4447         */
4448        public long getItemId(int position) {
4449            return NO_ID;
4450        }
4451
4452        /**
4453         * Returns the total number of items in the data set hold by the adapter.
4454         *
4455         * @return The total number of items in this adapter.
4456         */
4457        public abstract int getItemCount();
4458
4459        /**
4460         * Returns true if this adapter publishes a unique <code>long</code> value that can
4461         * act as a key for the item at a given position in the data set. If that item is relocated
4462         * in the data set, the ID returned for that item should be the same.
4463         *
4464         * @return true if this adapter's items have stable IDs
4465         */
4466        public final boolean hasStableIds() {
4467            return mHasStableIds;
4468        }
4469
4470        /**
4471         * Called when a view created by this adapter has been recycled.
4472         *
4473         * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
4474         * needs to be attached to its parent {@link RecyclerView}. This can be because it has
4475         * fallen out of visibility or a set of cached views represented by views still
4476         * attached to the parent RecyclerView. If an item view has large or expensive data
4477         * bound to it such as large bitmaps, this may be a good place to release those
4478         * resources.</p>
4479         *
4480         * @param holder The ViewHolder for the view being recycled
4481         */
4482        public void onViewRecycled(VH holder) {
4483        }
4484
4485        /**
4486         * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
4487         * due to its transient state. Upon receiving this callback, Adapter can clear the
4488         * animation(s) that effect the View's transient state and return <code>true</code> so that
4489         * the View can be recycled. Keep in mind that the View in question is already removed from
4490         * the RecyclerView.
4491         * <p>
4492         * In some cases, it is acceptable to recycle a View although it has transient state. Most
4493         * of the time, this is a case where the transient state will be cleared in
4494         * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
4495         * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
4496         * value of this method to decide whether the View should be recycled or not.
4497         * <p>
4498         * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
4499         * should never receive this callback because RecyclerView keeps those Views as children
4500         * until their animations are complete. This callback is useful when children of the item
4501         * views create animations which may not be easy to implement using an {@link ItemAnimator}.
4502         * <p>
4503         * You should <em>never</em> fix this issue by calling
4504         * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
4505         * <code>holder.itemView.setHasTransientState(true);</code>. Each
4506         * <code>View.setHasTransientState(true)</code> call must be matched by a
4507         * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
4508         * may become inconsistent. You should always prefer to end or cancel animations that are
4509         * triggering the transient state instead of handling it manually.
4510         *
4511         * @param holder The ViewHolder containing the View that could not be recycled due to its
4512         *               transient state.
4513         * @return True if the View should be recycled, false otherwise. Note that if this method
4514         * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
4515         * the View and recycle it regardless. If this method returns <code>false</code>,
4516         * RecyclerView will check the View's transient state again before giving a final decision.
4517         * Default implementation returns false.
4518         */
4519        public boolean onFailedToRecycleView(VH holder) {
4520            return false;
4521        }
4522
4523        /**
4524         * Called when a view created by this adapter has been attached to a window.
4525         *
4526         * <p>This can be used as a reasonable signal that the view is about to be seen
4527         * by the user. If the adapter previously freed any resources in
4528         * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
4529         * those resources should be restored here.</p>
4530         *
4531         * @param holder Holder of the view being attached
4532         */
4533        public void onViewAttachedToWindow(VH holder) {
4534        }
4535
4536        /**
4537         * Called when a view created by this adapter has been detached from its window.
4538         *
4539         * <p>Becoming detached from the window is not necessarily a permanent condition;
4540         * the consumer of an Adapter's views may choose to cache views offscreen while they
4541         * are not visible, attaching an detaching them as appropriate.</p>
4542         *
4543         * @param holder Holder of the view being detached
4544         */
4545        public void onViewDetachedFromWindow(VH holder) {
4546        }
4547
4548        /**
4549         * Returns true if one or more observers are attached to this adapter.
4550         *
4551         * @return true if this adapter has observers
4552         */
4553        public final boolean hasObservers() {
4554            return mObservable.hasObservers();
4555        }
4556
4557        /**
4558         * Register a new observer to listen for data changes.
4559         *
4560         * <p>The adapter may publish a variety of events describing specific changes.
4561         * Not all adapters may support all change types and some may fall back to a generic
4562         * {@link android.support.v7.widget.RecyclerView.AdapterDataObserver#onChanged()
4563         * "something changed"} event if more specific data is not available.</p>
4564         *
4565         * <p>Components registering observers with an adapter are responsible for
4566         * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
4567         * unregistering} those observers when finished.</p>
4568         *
4569         * @param observer Observer to register
4570         *
4571         * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
4572         */
4573        public void registerAdapterDataObserver(AdapterDataObserver observer) {
4574            mObservable.registerObserver(observer);
4575        }
4576
4577        /**
4578         * Unregister an observer currently listening for data changes.
4579         *
4580         * <p>The unregistered observer will no longer receive events about changes
4581         * to the adapter.</p>
4582         *
4583         * @param observer Observer to unregister
4584         *
4585         * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
4586         */
4587        public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
4588            mObservable.unregisterObserver(observer);
4589        }
4590
4591        /**
4592         * Called by RecyclerView when it starts observing this Adapter.
4593         * <p>
4594         * Keep in mind that same adapter may be observed by multiple RecyclerViews.
4595         *
4596         * @param recyclerView The RecyclerView instance which started observing this adapter.
4597         * @see #onDetachedFromRecyclerView(RecyclerView)
4598         */
4599        public void onAttachedToRecyclerView(RecyclerView recyclerView) {
4600        }
4601
4602        /**
4603         * Called by RecyclerView when it stops observing this Adapter.
4604         *
4605         * @param recyclerView The RecyclerView instance which stopped observing this adapter.
4606         * @see #onAttachedToRecyclerView(RecyclerView)
4607         */
4608        public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
4609        }
4610
4611        /**
4612         * Notify any registered observers that the data set has changed.
4613         *
4614         * <p>There are two different classes of data change events, item changes and structural
4615         * changes. Item changes are when a single item has its data updated but no positional
4616         * changes have occurred. Structural changes are when items are inserted, removed or moved
4617         * within the data set.</p>
4618         *
4619         * <p>This event does not specify what about the data set has changed, forcing
4620         * any observers to assume that all existing items and structure may no longer be valid.
4621         * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
4622         *
4623         * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
4624         * for adapters that report that they have {@link #hasStableIds() stable IDs} when
4625         * this method is used. This can help for the purposes of animation and visual
4626         * object persistence but individual item views will still need to be rebound
4627         * and relaid out.</p>
4628         *
4629         * <p>If you are writing an adapter it will always be more efficient to use the more
4630         * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
4631         * as a last resort.</p>
4632         *
4633         * @see #notifyItemChanged(int)
4634         * @see #notifyItemInserted(int)
4635         * @see #notifyItemRemoved(int)
4636         * @see #notifyItemRangeChanged(int, int)
4637         * @see #notifyItemRangeInserted(int, int)
4638         * @see #notifyItemRangeRemoved(int, int)
4639         */
4640        public final void notifyDataSetChanged() {
4641            mObservable.notifyChanged();
4642        }
4643
4644        /**
4645         * Notify any registered observers that the item at <code>position</code> has changed.
4646         *
4647         * <p>This is an item change event, not a structural change event. It indicates that any
4648         * reflection of the data at <code>position</code> is out of date and should be updated.
4649         * The item at <code>position</code> retains the same identity.</p>
4650         *
4651         * @param position Position of the item that has changed
4652         *
4653         * @see #notifyItemRangeChanged(int, int)
4654         */
4655        public final void notifyItemChanged(int position) {
4656            mObservable.notifyItemRangeChanged(position, 1);
4657        }
4658
4659        /**
4660         * Notify any registered observers that the <code>itemCount</code> items starting at
4661         * position <code>positionStart</code> have changed.
4662         *
4663         * <p>This is an item change event, not a structural change event. It indicates that
4664         * any reflection of the data in the given position range is out of date and should
4665         * be updated. The items in the given range retain the same identity.</p>
4666         *
4667         * @param positionStart Position of the first item that has changed
4668         * @param itemCount Number of items that have changed
4669         *
4670         * @see #notifyItemChanged(int)
4671         */
4672        public final void notifyItemRangeChanged(int positionStart, int itemCount) {
4673            mObservable.notifyItemRangeChanged(positionStart, itemCount);
4674        }
4675
4676        /**
4677         * Notify any registered observers that the item reflected at <code>position</code>
4678         * has been newly inserted. The item previously at <code>position</code> is now at
4679         * position <code>position + 1</code>.
4680         *
4681         * <p>This is a structural change event. Representations of other existing items in the
4682         * data set are still considered up to date and will not be rebound, though their
4683         * positions may be altered.</p>
4684         *
4685         * @param position Position of the newly inserted item in the data set
4686         *
4687         * @see #notifyItemRangeInserted(int, int)
4688         */
4689        public final void notifyItemInserted(int position) {
4690            mObservable.notifyItemRangeInserted(position, 1);
4691        }
4692
4693        /**
4694         * Notify any registered observers that the item reflected at <code>fromPosition</code>
4695         * has been moved to <code>toPosition</code>.
4696         *
4697         * <p>This is a structural change event. Representations of other existing items in the
4698         * data set are still considered up to date and will not be rebound, though their
4699         * positions may be altered.</p>
4700         *
4701         * @param fromPosition Previous position of the item.
4702         * @param toPosition New position of the item.
4703         */
4704        public final void notifyItemMoved(int fromPosition, int toPosition) {
4705            mObservable.notifyItemMoved(fromPosition, toPosition);
4706        }
4707
4708        /**
4709         * Notify any registered observers that the currently reflected <code>itemCount</code>
4710         * items starting at <code>positionStart</code> have been newly inserted. The items
4711         * previously located at <code>positionStart</code> and beyond can now be found starting
4712         * at position <code>positionStart + itemCount</code>.
4713         *
4714         * <p>This is a structural change event. Representations of other existing items in the
4715         * data set are still considered up to date and will not be rebound, though their positions
4716         * may be altered.</p>
4717         *
4718         * @param positionStart Position of the first item that was inserted
4719         * @param itemCount Number of items inserted
4720         *
4721         * @see #notifyItemInserted(int)
4722         */
4723        public final void notifyItemRangeInserted(int positionStart, int itemCount) {
4724            mObservable.notifyItemRangeInserted(positionStart, itemCount);
4725        }
4726
4727        /**
4728         * Notify any registered observers that the item previously located at <code>position</code>
4729         * has been removed from the data set. The items previously located at and after
4730         * <code>position</code> may now be found at <code>oldPosition - 1</code>.
4731         *
4732         * <p>This is a structural change event. Representations of other existing items in the
4733         * data set are still considered up to date and will not be rebound, though their positions
4734         * may be altered.</p>
4735         *
4736         * @param position Position of the item that has now been removed
4737         *
4738         * @see #notifyItemRangeRemoved(int, int)
4739         */
4740        public final void notifyItemRemoved(int position) {
4741            mObservable.notifyItemRangeRemoved(position, 1);
4742        }
4743
4744        /**
4745         * Notify any registered observers that the <code>itemCount</code> items previously
4746         * located at <code>positionStart</code> have been removed from the data set. The items
4747         * previously located at and after <code>positionStart + itemCount</code> may now be found
4748         * at <code>oldPosition - itemCount</code>.
4749         *
4750         * <p>This is a structural change event. Representations of other existing items in the data
4751         * set are still considered up to date and will not be rebound, though their positions
4752         * may be altered.</p>
4753         *
4754         * @param positionStart Previous position of the first item that was removed
4755         * @param itemCount Number of items removed from the data set
4756         */
4757        public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
4758            mObservable.notifyItemRangeRemoved(positionStart, itemCount);
4759        }
4760    }
4761
4762    private void dispatchChildDetached(View child) {
4763        if (mAdapter != null) {
4764            mAdapter.onViewDetachedFromWindow(getChildViewHolderInt(child));
4765        }
4766        onChildDetachedFromWindow(child);
4767    }
4768
4769    private void dispatchChildAttached(View child) {
4770        if (mAdapter != null) {
4771            mAdapter.onViewAttachedToWindow(getChildViewHolderInt(child));
4772        }
4773        onChildAttachedToWindow(child);
4774    }
4775
4776    /**
4777     * A <code>LayoutManager</code> is responsible for measuring and positioning item views
4778     * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
4779     * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
4780     * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
4781     * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
4782     * layout managers are provided for general use.
4783     */
4784    public static abstract class LayoutManager {
4785        ChildHelper mChildHelper;
4786        RecyclerView mRecyclerView;
4787
4788        @Nullable
4789        SmoothScroller mSmoothScroller;
4790
4791        private boolean mRequestedSimpleAnimations = false;
4792
4793        void setRecyclerView(RecyclerView recyclerView) {
4794            if (recyclerView == null) {
4795                mRecyclerView = null;
4796                mChildHelper = null;
4797            } else {
4798                mRecyclerView = recyclerView;
4799                mChildHelper = recyclerView.mChildHelper;
4800            }
4801
4802        }
4803
4804        /**
4805         * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
4806         */
4807        public void requestLayout() {
4808            if(mRecyclerView != null) {
4809                mRecyclerView.requestLayout();
4810            }
4811        }
4812
4813        /**
4814         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
4815         * {@link IllegalStateException} if it <b>is not</b>.
4816         *
4817         * @param message The message for the exception. Can be null.
4818         * @see #assertNotInLayoutOrScroll(String)
4819         */
4820        public void assertInLayoutOrScroll(String message) {
4821            if (mRecyclerView != null) {
4822                mRecyclerView.assertInLayoutOrScroll(message);
4823            }
4824        }
4825
4826        /**
4827         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
4828         * {@link IllegalStateException} if it <b>is</b>.
4829         *
4830         * @param message The message for the exception. Can be null.
4831         * @see #assertInLayoutOrScroll(String)
4832         */
4833        public void assertNotInLayoutOrScroll(String message) {
4834            if (mRecyclerView != null) {
4835                mRecyclerView.assertNotInLayoutOrScroll(message);
4836            }
4837        }
4838
4839        /**
4840         * Returns whether this LayoutManager supports automatic item animations.
4841         * A LayoutManager wishing to support item animations should obey certain
4842         * rules as outlined in {@link #onLayoutChildren(Recycler, State)}.
4843         * The default return value is <code>false</code>, so subclasses of LayoutManager
4844         * will not get predictive item animations by default.
4845         *
4846         * <p>Whether item animations are enabled in a RecyclerView is determined both
4847         * by the return value from this method and the
4848         * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
4849         * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
4850         * method returns false, then simple item animations will be enabled, in which
4851         * views that are moving onto or off of the screen are simply faded in/out. If
4852         * the RecyclerView has a non-null ItemAnimator and this method returns true,
4853         * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to
4854         * setup up the information needed to more intelligently predict where appearing
4855         * and disappearing views should be animated from/to.</p>
4856         *
4857         * @return true if predictive item animations should be enabled, false otherwise
4858         */
4859        public boolean supportsPredictiveItemAnimations() {
4860            return false;
4861        }
4862
4863        /**
4864         * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
4865         * is attached to a window.
4866         *
4867         * <p>Subclass implementations should always call through to the superclass implementation.
4868         * </p>
4869         *
4870         * @param view The RecyclerView this LayoutManager is bound to
4871         */
4872        public void onAttachedToWindow(RecyclerView view) {
4873        }
4874
4875        /**
4876         * @deprecated
4877         * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
4878         */
4879        @Deprecated
4880        public void onDetachedFromWindow(RecyclerView view) {
4881
4882        }
4883
4884        /**
4885         * Called when this LayoutManager is detached from its parent RecyclerView or when
4886         * its parent RecyclerView is detached from its window.
4887         *
4888         * <p>Subclass implementations should always call through to the superclass implementation.
4889         * </p>
4890         *
4891         * @param view The RecyclerView this LayoutManager is bound to
4892         * @param recycler The recycler to use if you prefer to recycle your children instead of
4893         *                 keeping them around.
4894         */
4895        public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
4896            onDetachedFromWindow(view);
4897        }
4898
4899        /**
4900         * Check if the RecyclerView is configured to clip child views to its padding.
4901         *
4902         * @return true if this RecyclerView clips children to its padding, false otherwise
4903         */
4904        public boolean getClipToPadding() {
4905            return mRecyclerView != null && mRecyclerView.mClipToPadding;
4906        }
4907
4908        /**
4909         * Lay out all relevant child views from the given adapter.
4910         *
4911         * The LayoutManager is in charge of the behavior of item animations. By default,
4912         * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
4913         * item animations are enabled. This means that add/remove operations on the
4914         * adapter will result in animations to add new or appearing items, removed or
4915         * disappearing items, and moved items. If a LayoutManager returns false from
4916         * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
4917         * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
4918         * RecyclerView will have enough information to run those animations in a simple
4919         * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
4920         * simple fade views in and out, whether they are actuall added/removed or whether
4921         * they are moved on or off the screen due to other add/remove operations.
4922         *
4923         * <p>A LayoutManager wanting a better item animation experience, where items can be
4924         * animated onto and off of the screen according to where the items exist when they
4925         * are not on screen, then the LayoutManager should return true from
4926         * {@link #supportsPredictiveItemAnimations()} and add additional logic to
4927         * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
4928         * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
4929         * once as a "pre" layout step to determine where items would have been prior to
4930         * a real layout, and again to do the "real" layout. In the pre-layout phase,
4931         * items will remember their pre-layout positions to allow them to be laid out
4932         * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
4933         * be returned from the scrap to help determine correct placement of other items.
4934         * These removed items should not be added to the child list, but should be used
4935         * to help calculate correct positioning of other views, including views that
4936         * were not previously onscreen (referred to as APPEARING views), but whose
4937         * pre-layout offscreen position can be determined given the extra
4938         * information about the pre-layout removed views.</p>
4939         *
4940         * <p>The second layout pass is the real layout in which only non-removed views
4941         * will be used. The only additional requirement during this pass is, if
4942         * {@link #supportsPredictiveItemAnimations()} returns true, to note which
4943         * views exist in the child list prior to layout and which are not there after
4944         * layout (referred to as DISAPPEARING views), and to position/layout those views
4945         * appropriately, without regard to the actual bounds of the RecyclerView. This allows
4946         * the animation system to know the location to which to animate these disappearing
4947         * views.</p>
4948         *
4949         * <p>The default LayoutManager implementations for RecyclerView handle all of these
4950         * requirements for animations already. Clients of RecyclerView can either use one
4951         * of these layout managers directly or look at their implementations of
4952         * onLayoutChildren() to see how they account for the APPEARING and
4953         * DISAPPEARING views.</p>
4954         *
4955         * @param recycler         Recycler to use for fetching potentially cached views for a
4956         *                         position
4957         * @param state            Transient state of RecyclerView
4958         */
4959        public void onLayoutChildren(Recycler recycler, State state) {
4960            Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
4961        }
4962
4963        /**
4964         * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
4965         *
4966         * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
4967         * to store extra information specific to the layout. Client code should subclass
4968         * {@link RecyclerView.LayoutParams} for this purpose.</p>
4969         *
4970         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
4971         * you must also override
4972         * {@link #checkLayoutParams(LayoutParams)},
4973         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
4974         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
4975         *
4976         * @return A new LayoutParams for a child view
4977         */
4978        public abstract LayoutParams generateDefaultLayoutParams();
4979
4980        /**
4981         * Determines the validity of the supplied LayoutParams object.
4982         *
4983         * <p>This should check to make sure that the object is of the correct type
4984         * and all values are within acceptable ranges. The default implementation
4985         * returns <code>true</code> for non-null params.</p>
4986         *
4987         * @param lp LayoutParams object to check
4988         * @return true if this LayoutParams object is valid, false otherwise
4989         */
4990        public boolean checkLayoutParams(LayoutParams lp) {
4991            return lp != null;
4992        }
4993
4994        /**
4995         * Create a LayoutParams object suitable for this LayoutManager, copying relevant
4996         * values from the supplied LayoutParams object if possible.
4997         *
4998         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
4999         * you must also override
5000         * {@link #checkLayoutParams(LayoutParams)},
5001         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
5002         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
5003         *
5004         * @param lp Source LayoutParams object to copy values from
5005         * @return a new LayoutParams object
5006         */
5007        public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
5008            if (lp instanceof LayoutParams) {
5009                return new LayoutParams((LayoutParams) lp);
5010            } else if (lp instanceof MarginLayoutParams) {
5011                return new LayoutParams((MarginLayoutParams) lp);
5012            } else {
5013                return new LayoutParams(lp);
5014            }
5015        }
5016
5017        /**
5018         * Create a LayoutParams object suitable for this LayoutManager from
5019         * an inflated layout resource.
5020         *
5021         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
5022         * you must also override
5023         * {@link #checkLayoutParams(LayoutParams)},
5024         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
5025         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
5026         *
5027         * @param c Context for obtaining styled attributes
5028         * @param attrs AttributeSet describing the supplied arguments
5029         * @return a new LayoutParams object
5030         */
5031        public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
5032            return new LayoutParams(c, attrs);
5033        }
5034
5035        /**
5036         * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
5037         * The default implementation does nothing and returns 0.
5038         *
5039         * @param dx            distance to scroll by in pixels. X increases as scroll position
5040         *                      approaches the right.
5041         * @param recycler      Recycler to use for fetching potentially cached views for a
5042         *                      position
5043         * @param state         Transient state of RecyclerView
5044         * @return The actual distance scrolled. The return value will be negative if dx was
5045         * negative and scrolling proceeeded in that direction.
5046         * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
5047         */
5048        public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
5049            return 0;
5050        }
5051
5052        /**
5053         * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
5054         * The default implementation does nothing and returns 0.
5055         *
5056         * @param dy            distance to scroll in pixels. Y increases as scroll position
5057         *                      approaches the bottom.
5058         * @param recycler      Recycler to use for fetching potentially cached views for a
5059         *                      position
5060         * @param state         Transient state of RecyclerView
5061         * @return The actual distance scrolled. The return value will be negative if dy was
5062         * negative and scrolling proceeeded in that direction.
5063         * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
5064         */
5065        public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
5066            return 0;
5067        }
5068
5069        /**
5070         * Query if horizontal scrolling is currently supported. The default implementation
5071         * returns false.
5072         *
5073         * @return True if this LayoutManager can scroll the current contents horizontally
5074         */
5075        public boolean canScrollHorizontally() {
5076            return false;
5077        }
5078
5079        /**
5080         * Query if vertical scrolling is currently supported. The default implementation
5081         * returns false.
5082         *
5083         * @return True if this LayoutManager can scroll the current contents vertically
5084         */
5085        public boolean canScrollVertically() {
5086            return false;
5087        }
5088
5089        /**
5090         * Scroll to the specified adapter position.
5091         *
5092         * Actual position of the item on the screen depends on the LayoutManager implementation.
5093         * @param position Scroll to this adapter position.
5094         */
5095        public void scrollToPosition(int position) {
5096            if (DEBUG) {
5097                Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
5098            }
5099        }
5100
5101        /**
5102         * <p>Smooth scroll to the specified adapter position.</p>
5103         * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
5104         * instance and call {@link #startSmoothScroll(SmoothScroller)}.
5105         * </p>
5106         * @param recyclerView The RecyclerView to which this layout manager is attached
5107         * @param state    Current State of RecyclerView
5108         * @param position Scroll to this adapter position.
5109         */
5110        public void smoothScrollToPosition(RecyclerView recyclerView, State state,
5111                int position) {
5112            Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
5113        }
5114
5115        /**
5116         * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
5117         * <p>Calling this method will cancel any previous smooth scroll request.</p>
5118         * @param smoothScroller Unstance which defines how smooth scroll should be animated
5119         */
5120        public void startSmoothScroll(SmoothScroller smoothScroller) {
5121            if (mSmoothScroller != null && smoothScroller != mSmoothScroller
5122                    && mSmoothScroller.isRunning()) {
5123                mSmoothScroller.stop();
5124            }
5125            mSmoothScroller = smoothScroller;
5126            mSmoothScroller.start(mRecyclerView, this);
5127        }
5128
5129        /**
5130         * @return true if RecycylerView is currently in the state of smooth scrolling.
5131         */
5132        public boolean isSmoothScrolling() {
5133            return mSmoothScroller != null && mSmoothScroller.isRunning();
5134        }
5135
5136
5137        /**
5138         * Returns the resolved layout direction for this RecyclerView.
5139         *
5140         * @return {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout
5141         * direction is RTL or returns
5142         * {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction
5143         * is not RTL.
5144         */
5145        public int getLayoutDirection() {
5146            return ViewCompat.getLayoutDirection(mRecyclerView);
5147        }
5148
5149        /**
5150         * Ends all animations on the view created by the {@link ItemAnimator}.
5151         *
5152         * @param view The View for which the animations should be ended.
5153         * @see RecyclerView.ItemAnimator#endAnimations()
5154         */
5155        public void endAnimation(View view) {
5156            if (mRecyclerView.mItemAnimator != null) {
5157                mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
5158            }
5159        }
5160
5161        /**
5162         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
5163         * to the layout that is known to be going away, either because it has been
5164         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
5165         * visible portion of the container but is being laid out in order to inform RecyclerView
5166         * in how to animate the item out of view.
5167         * <p>
5168         * Views added via this method are going to be invisible to LayoutManager after the
5169         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
5170         * or won't be included in {@link #getChildCount()} method.
5171         *
5172         * @param child View to add and then remove with animation.
5173         */
5174        public void addDisappearingView(View child) {
5175            addDisappearingView(child, -1);
5176        }
5177
5178        /**
5179         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
5180         * to the layout that is known to be going away, either because it has been
5181         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
5182         * visible portion of the container but is being laid out in order to inform RecyclerView
5183         * in how to animate the item out of view.
5184         * <p>
5185         * Views added via this method are going to be invisible to LayoutManager after the
5186         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
5187         * or won't be included in {@link #getChildCount()} method.
5188         *
5189         * @param child View to add and then remove with animation.
5190         * @param index Index of the view.
5191         */
5192        public void addDisappearingView(View child, int index) {
5193            addViewInt(child, index, true);
5194        }
5195
5196        /**
5197         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
5198         * use this method to add views obtained from a {@link Recycler} using
5199         * {@link Recycler#getViewForPosition(int)}.
5200         *
5201         * @param child View to add
5202         */
5203        public void addView(View child) {
5204            addView(child, -1);
5205        }
5206
5207        /**
5208         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
5209         * use this method to add views obtained from a {@link Recycler} using
5210         * {@link Recycler#getViewForPosition(int)}.
5211         *
5212         * @param child View to add
5213         * @param index Index to add child at
5214         */
5215        public void addView(View child, int index) {
5216            addViewInt(child, index, false);
5217        }
5218
5219        private void addViewInt(View child, int index, boolean disappearing) {
5220            final ViewHolder holder = getChildViewHolderInt(child);
5221            if (disappearing || holder.isRemoved()) {
5222                // these views will be hidden at the end of the layout pass.
5223                mRecyclerView.addToDisappearingList(child);
5224            } else {
5225                // This may look like unnecessary but may happen if layout manager supports
5226                // predictive layouts and adapter removed then re-added the same item.
5227                // In this case, added version will be visible in the post layout (because add is
5228                // deferred) but RV will still bind it to the same View.
5229                // So if a View re-appears in post layout pass, remove it from disappearing list.
5230                mRecyclerView.removeFromDisappearingList(child);
5231            }
5232            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
5233            if (holder.wasReturnedFromScrap() || holder.isScrap()) {
5234                if (holder.isScrap()) {
5235                    holder.unScrap();
5236                } else {
5237                    holder.clearReturnedFromScrapFlag();
5238                }
5239                mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
5240                if (DISPATCH_TEMP_DETACH) {
5241                    ViewCompat.dispatchFinishTemporaryDetach(child);
5242                }
5243            } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
5244                // ensure in correct position
5245                int currentIndex = mChildHelper.indexOfChild(child);
5246                if (index == -1) {
5247                    index = mChildHelper.getChildCount();
5248                }
5249                if (currentIndex == -1) {
5250                    throw new IllegalStateException("Added View has RecyclerView as parent but"
5251                            + " view is not a real child. Unfiltered index:"
5252                            + mRecyclerView.indexOfChild(child));
5253                }
5254                if (currentIndex != index) {
5255                    mRecyclerView.mLayout.moveView(currentIndex, index);
5256                }
5257            } else {
5258                mChildHelper.addView(child, index, false);
5259                lp.mInsetsDirty = true;
5260                if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
5261                    mSmoothScroller.onChildAttachedToWindow(child);
5262                }
5263            }
5264            if (lp.mPendingInvalidate) {
5265                if (DEBUG) {
5266                    Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
5267                }
5268                holder.itemView.invalidate();
5269                lp.mPendingInvalidate = false;
5270            }
5271        }
5272
5273        /**
5274         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
5275         * use this method to completely remove a child view that is no longer needed.
5276         * LayoutManagers should strongly consider recycling removed views using
5277         * {@link Recycler#recycleView(android.view.View)}.
5278         *
5279         * @param child View to remove
5280         */
5281        public void removeView(View child) {
5282            mChildHelper.removeView(child);
5283        }
5284
5285        /**
5286         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
5287         * use this method to completely remove a child view that is no longer needed.
5288         * LayoutManagers should strongly consider recycling removed views using
5289         * {@link Recycler#recycleView(android.view.View)}.
5290         *
5291         * @param index Index of the child view to remove
5292         */
5293        public void removeViewAt(int index) {
5294            final View child = getChildAt(index);
5295            if (child != null) {
5296                mChildHelper.removeViewAt(index);
5297            }
5298        }
5299
5300        /**
5301         * Remove all views from the currently attached RecyclerView. This will not recycle
5302         * any of the affected views; the LayoutManager is responsible for doing so if desired.
5303         */
5304        public void removeAllViews() {
5305            // Only remove non-animating views
5306            final int childCount = getChildCount();
5307            for (int i = childCount - 1; i >= 0; i--) {
5308                mChildHelper.removeViewAt(i);
5309            }
5310        }
5311
5312        /**
5313         * Returns the adapter position of the item represented by the given View. This does not
5314         * contain any adapter changes that might have happened after the last layout.
5315         *
5316         * @param view The view to query
5317         * @return The adapter position of the item which is rendered by this View.
5318         */
5319        public int getPosition(View view) {
5320            return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
5321        }
5322
5323        /**
5324         * Returns the View type defined by the adapter.
5325         *
5326         * @param view The view to query
5327         * @return The type of the view assigned by the adapter.
5328         */
5329        public int getItemViewType(View view) {
5330            return getChildViewHolderInt(view).getItemViewType();
5331        }
5332
5333        /**
5334         * Finds the view which represents the given adapter position.
5335         * <p>
5336         * This method traverses each child since it has no information about child order.
5337         * Override this method to improve performance if your LayoutManager keeps data about
5338         * child views.
5339         * <p>
5340         * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
5341         *
5342         * @param position Position of the item in adapter
5343         * @return The child view that represents the given position or null if the position is not
5344         * laid out
5345         */
5346        public View findViewByPosition(int position) {
5347            final int childCount = getChildCount();
5348            for (int i = 0; i < childCount; i++) {
5349                View child = getChildAt(i);
5350                ViewHolder vh = getChildViewHolderInt(child);
5351                if (vh == null) {
5352                    continue;
5353                }
5354                if (vh.getLayoutPosition() == position && !vh.shouldIgnore() &&
5355                        (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
5356                    return child;
5357                }
5358            }
5359            return null;
5360        }
5361
5362        /**
5363         * Temporarily detach a child view.
5364         *
5365         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
5366         * views currently attached to the RecyclerView. Generally LayoutManager implementations
5367         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
5368         * so that the detached view may be rebound and reused.</p>
5369         *
5370         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
5371         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
5372         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
5373         * before the LayoutManager entry point method called by RecyclerView returns.</p>
5374         *
5375         * @param child Child to detach
5376         */
5377        public void detachView(View child) {
5378            final int ind = mChildHelper.indexOfChild(child);
5379            if (ind >= 0) {
5380                detachViewInternal(ind, child);
5381            }
5382        }
5383
5384        /**
5385         * Temporarily detach a child view.
5386         *
5387         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
5388         * views currently attached to the RecyclerView. Generally LayoutManager implementations
5389         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
5390         * so that the detached view may be rebound and reused.</p>
5391         *
5392         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
5393         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
5394         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
5395         * before the LayoutManager entry point method called by RecyclerView returns.</p>
5396         *
5397         * @param index Index of the child to detach
5398         */
5399        public void detachViewAt(int index) {
5400            detachViewInternal(index, getChildAt(index));
5401        }
5402
5403        private void detachViewInternal(int index, View view) {
5404            if (DISPATCH_TEMP_DETACH) {
5405                ViewCompat.dispatchStartTemporaryDetach(view);
5406            }
5407            mChildHelper.detachViewFromParent(index);
5408        }
5409
5410        /**
5411         * Reattach a previously {@link #detachView(android.view.View) detached} view.
5412         * This method should not be used to reattach views that were previously
5413         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
5414         *
5415         * @param child Child to reattach
5416         * @param index Intended child index for child
5417         * @param lp LayoutParams for child
5418         */
5419        public void attachView(View child, int index, LayoutParams lp) {
5420            ViewHolder vh = getChildViewHolderInt(child);
5421            if (vh.isRemoved()) {
5422                mRecyclerView.addToDisappearingList(child);
5423            } else {
5424                mRecyclerView.removeFromDisappearingList(child);
5425            }
5426            mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
5427            if (DISPATCH_TEMP_DETACH)  {
5428                ViewCompat.dispatchFinishTemporaryDetach(child);
5429            }
5430        }
5431
5432        /**
5433         * Reattach a previously {@link #detachView(android.view.View) detached} view.
5434         * This method should not be used to reattach views that were previously
5435         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
5436         *
5437         * @param child Child to reattach
5438         * @param index Intended child index for child
5439         */
5440        public void attachView(View child, int index) {
5441            attachView(child, index, (LayoutParams) child.getLayoutParams());
5442        }
5443
5444        /**
5445         * Reattach a previously {@link #detachView(android.view.View) detached} view.
5446         * This method should not be used to reattach views that were previously
5447         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
5448         *
5449         * @param child Child to reattach
5450         */
5451        public void attachView(View child) {
5452            attachView(child, -1);
5453        }
5454
5455        /**
5456         * Finish removing a view that was previously temporarily
5457         * {@link #detachView(android.view.View) detached}.
5458         *
5459         * @param child Detached child to remove
5460         */
5461        public void removeDetachedView(View child) {
5462            mRecyclerView.removeDetachedView(child, false);
5463        }
5464
5465        /**
5466         * Moves a View from one position to another.
5467         *
5468         * @param fromIndex The View's initial index
5469         * @param toIndex The View's target index
5470         */
5471        public void moveView(int fromIndex, int toIndex) {
5472            View view = getChildAt(fromIndex);
5473            if (view == null) {
5474                throw new IllegalArgumentException("Cannot move a child from non-existing index:"
5475                        + fromIndex);
5476            }
5477            detachViewAt(fromIndex);
5478            attachView(view, toIndex);
5479        }
5480
5481        /**
5482         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
5483         *
5484         * <p>Scrapping a view allows it to be rebound and reused to show updated or
5485         * different data.</p>
5486         *
5487         * @param child Child to detach and scrap
5488         * @param recycler Recycler to deposit the new scrap view into
5489         */
5490        public void detachAndScrapView(View child, Recycler recycler) {
5491            int index = mChildHelper.indexOfChild(child);
5492            scrapOrRecycleView(recycler, index, child);
5493        }
5494
5495        /**
5496         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
5497         *
5498         * <p>Scrapping a view allows it to be rebound and reused to show updated or
5499         * different data.</p>
5500         *
5501         * @param index Index of child to detach and scrap
5502         * @param recycler Recycler to deposit the new scrap view into
5503         */
5504        public void detachAndScrapViewAt(int index, Recycler recycler) {
5505            final View child = getChildAt(index);
5506            scrapOrRecycleView(recycler, index, child);
5507        }
5508
5509        /**
5510         * Remove a child view and recycle it using the given Recycler.
5511         *
5512         * @param child Child to remove and recycle
5513         * @param recycler Recycler to use to recycle child
5514         */
5515        public void removeAndRecycleView(View child, Recycler recycler) {
5516            removeView(child);
5517            recycler.recycleView(child);
5518        }
5519
5520        /**
5521         * Remove a child view and recycle it using the given Recycler.
5522         *
5523         * @param index Index of child to remove and recycle
5524         * @param recycler Recycler to use to recycle child
5525         */
5526        public void removeAndRecycleViewAt(int index, Recycler recycler) {
5527            final View view = getChildAt(index);
5528            removeViewAt(index);
5529            recycler.recycleView(view);
5530        }
5531
5532        /**
5533         * Return the current number of child views attached to the parent RecyclerView.
5534         * This does not include child views that were temporarily detached and/or scrapped.
5535         *
5536         * @return Number of attached children
5537         */
5538        public int getChildCount() {
5539            return mChildHelper != null ? mChildHelper.getChildCount() : 0;
5540        }
5541
5542        /**
5543         * Return the child view at the given index
5544         * @param index Index of child to return
5545         * @return Child view at index
5546         */
5547        public View getChildAt(int index) {
5548            return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
5549        }
5550
5551        /**
5552         * Return the width of the parent RecyclerView
5553         *
5554         * @return Width in pixels
5555         */
5556        public int getWidth() {
5557            return mRecyclerView != null ? mRecyclerView.getWidth() : 0;
5558        }
5559
5560        /**
5561         * Return the height of the parent RecyclerView
5562         *
5563         * @return Height in pixels
5564         */
5565        public int getHeight() {
5566            return mRecyclerView != null ? mRecyclerView.getHeight() : 0;
5567        }
5568
5569        /**
5570         * Return the left padding of the parent RecyclerView
5571         *
5572         * @return Padding in pixels
5573         */
5574        public int getPaddingLeft() {
5575            return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
5576        }
5577
5578        /**
5579         * Return the top padding of the parent RecyclerView
5580         *
5581         * @return Padding in pixels
5582         */
5583        public int getPaddingTop() {
5584            return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
5585        }
5586
5587        /**
5588         * Return the right padding of the parent RecyclerView
5589         *
5590         * @return Padding in pixels
5591         */
5592        public int getPaddingRight() {
5593            return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
5594        }
5595
5596        /**
5597         * Return the bottom padding of the parent RecyclerView
5598         *
5599         * @return Padding in pixels
5600         */
5601        public int getPaddingBottom() {
5602            return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
5603        }
5604
5605        /**
5606         * Return the start padding of the parent RecyclerView
5607         *
5608         * @return Padding in pixels
5609         */
5610        public int getPaddingStart() {
5611            return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;
5612        }
5613
5614        /**
5615         * Return the end padding of the parent RecyclerView
5616         *
5617         * @return Padding in pixels
5618         */
5619        public int getPaddingEnd() {
5620            return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;
5621        }
5622
5623        /**
5624         * Returns true if the RecyclerView this LayoutManager is bound to has focus.
5625         *
5626         * @return True if the RecyclerView has focus, false otherwise.
5627         * @see View#isFocused()
5628         */
5629        public boolean isFocused() {
5630            return mRecyclerView != null && mRecyclerView.isFocused();
5631        }
5632
5633        /**
5634         * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
5635         *
5636         * @return true if the RecyclerView has or contains focus
5637         * @see View#hasFocus()
5638         */
5639        public boolean hasFocus() {
5640            return mRecyclerView != null && mRecyclerView.hasFocus();
5641        }
5642
5643        /**
5644         * Returns the item View which has or contains focus.
5645         *
5646         * @return A direct child of RecyclerView which has focus or contains the focused child.
5647         */
5648        public View getFocusedChild() {
5649            if (mRecyclerView == null) {
5650                return null;
5651            }
5652            final View focused = mRecyclerView.getFocusedChild();
5653            if (focused == null || mChildHelper.isHidden(focused)) {
5654                return null;
5655            }
5656            return focused;
5657        }
5658
5659        /**
5660         * Returns the number of items in the adapter bound to the parent RecyclerView.
5661         * <p>
5662         * Note that this number is not necessarily equal to {@link State#getItemCount()}. In
5663         * methods where State is available, you should use {@link State#getItemCount()} instead.
5664         * For more details, check the documentation for {@link State#getItemCount()}.
5665         *
5666         * @return The number of items in the bound adapter
5667         * @see State#getItemCount()
5668         */
5669        public int getItemCount() {
5670            final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
5671            return a != null ? a.getItemCount() : 0;
5672        }
5673
5674        /**
5675         * Offset all child views attached to the parent RecyclerView by dx pixels along
5676         * the horizontal axis.
5677         *
5678         * @param dx Pixels to offset by
5679         */
5680        public void offsetChildrenHorizontal(int dx) {
5681            if (mRecyclerView != null) {
5682                mRecyclerView.offsetChildrenHorizontal(dx);
5683            }
5684        }
5685
5686        /**
5687         * Offset all child views attached to the parent RecyclerView by dy pixels along
5688         * the vertical axis.
5689         *
5690         * @param dy Pixels to offset by
5691         */
5692        public void offsetChildrenVertical(int dy) {
5693            if (mRecyclerView != null) {
5694                mRecyclerView.offsetChildrenVertical(dy);
5695            }
5696        }
5697
5698        /**
5699         * Flags a view so that it will not be scrapped or recycled.
5700         * <p>
5701         * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
5702         * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
5703         * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
5704         * ignore the child.
5705         * <p>
5706         * Before this child can be recycled again, you have to call
5707         * {@link #stopIgnoringView(View)}.
5708         * <p>
5709         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
5710         *
5711         * @param view View to ignore.
5712         * @see #stopIgnoringView(View)
5713         */
5714        public void ignoreView(View view) {
5715            if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
5716                // checking this because calling this method on a recycled or detached view may
5717                // cause loss of state.
5718                throw new IllegalArgumentException("View should be fully attached to be ignored");
5719            }
5720            final ViewHolder vh = getChildViewHolderInt(view);
5721            vh.addFlags(ViewHolder.FLAG_IGNORE);
5722            mRecyclerView.mState.onViewIgnored(vh);
5723        }
5724
5725        /**
5726         * View can be scrapped and recycled again.
5727         * <p>
5728         * Note that calling this method removes all information in the view holder.
5729         * <p>
5730         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
5731         *
5732         * @param view View to ignore.
5733         */
5734        public void stopIgnoringView(View view) {
5735            final ViewHolder vh = getChildViewHolderInt(view);
5736            vh.stopIgnoring();
5737            vh.resetInternal();
5738            vh.addFlags(ViewHolder.FLAG_INVALID);
5739        }
5740
5741        /**
5742         * Temporarily detach and scrap all currently attached child views. Views will be scrapped
5743         * into the given Recycler. The Recycler may prefer to reuse scrap views before
5744         * other views that were previously recycled.
5745         *
5746         * @param recycler Recycler to scrap views into
5747         */
5748        public void detachAndScrapAttachedViews(Recycler recycler) {
5749            final int childCount = getChildCount();
5750            for (int i = childCount - 1; i >= 0; i--) {
5751                final View v = getChildAt(i);
5752                scrapOrRecycleView(recycler, i, v);
5753            }
5754        }
5755
5756        private void scrapOrRecycleView(Recycler recycler, int index, View view) {
5757            final ViewHolder viewHolder = getChildViewHolderInt(view);
5758            if (viewHolder.shouldIgnore()) {
5759                if (DEBUG) {
5760                    Log.d(TAG, "ignoring view " + viewHolder);
5761                }
5762                return;
5763            }
5764            if (viewHolder.isInvalid() && !viewHolder.isRemoved() && !viewHolder.isChanged() &&
5765                    !mRecyclerView.mAdapter.hasStableIds()) {
5766                removeViewAt(index);
5767                recycler.recycleViewHolderInternal(viewHolder);
5768            } else {
5769                detachViewAt(index);
5770                recycler.scrapView(view);
5771            }
5772        }
5773
5774        /**
5775         * Recycles the scrapped views.
5776         * <p>
5777         * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
5778         * the expected behavior if scrapped views are used for animations. Otherwise, we need to
5779         * call remove and invalidate RecyclerView to ensure UI update.
5780         *
5781         * @param recycler Recycler
5782         */
5783        void removeAndRecycleScrapInt(Recycler recycler) {
5784            final int scrapCount = recycler.getScrapCount();
5785            for (int i = 0; i < scrapCount; i++) {
5786                final View scrap = recycler.getScrapViewAt(i);
5787                final ViewHolder vh = getChildViewHolderInt(scrap);
5788                if (vh.shouldIgnore()) {
5789                    continue;
5790                }
5791                if (vh.isTmpDetached()) {
5792                    mRecyclerView.removeDetachedView(scrap, false);
5793                }
5794                recycler.quickRecycleScrapView(scrap);
5795            }
5796            recycler.clearScrap();
5797            if (scrapCount > 0) {
5798                mRecyclerView.invalidate();
5799            }
5800        }
5801
5802
5803        /**
5804         * Measure a child view using standard measurement policy, taking the padding
5805         * of the parent RecyclerView and any added item decorations into account.
5806         *
5807         * <p>If the RecyclerView can be scrolled in either dimension the caller may
5808         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
5809         *
5810         * @param child Child view to measure
5811         * @param widthUsed Width in pixels currently consumed by other views, if relevant
5812         * @param heightUsed Height in pixels currently consumed by other views, if relevant
5813         */
5814        public void measureChild(View child, int widthUsed, int heightUsed) {
5815            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
5816
5817            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
5818            widthUsed += insets.left + insets.right;
5819            heightUsed += insets.top + insets.bottom;
5820
5821            final int widthSpec = getChildMeasureSpec(getWidth(),
5822                    getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
5823                    canScrollHorizontally());
5824            final int heightSpec = getChildMeasureSpec(getHeight(),
5825                    getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
5826                    canScrollVertically());
5827            child.measure(widthSpec, heightSpec);
5828        }
5829
5830        /**
5831         * Measure a child view using standard measurement policy, taking the padding
5832         * of the parent RecyclerView, any added item decorations and the child margins
5833         * into account.
5834         *
5835         * <p>If the RecyclerView can be scrolled in either dimension the caller may
5836         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
5837         *
5838         * @param child Child view to measure
5839         * @param widthUsed Width in pixels currently consumed by other views, if relevant
5840         * @param heightUsed Height in pixels currently consumed by other views, if relevant
5841         */
5842        public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
5843            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
5844
5845            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
5846            widthUsed += insets.left + insets.right;
5847            heightUsed += insets.top + insets.bottom;
5848
5849            final int widthSpec = getChildMeasureSpec(getWidth(),
5850                    getPaddingLeft() + getPaddingRight() +
5851                            lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
5852                    canScrollHorizontally());
5853            final int heightSpec = getChildMeasureSpec(getHeight(),
5854                    getPaddingTop() + getPaddingBottom() +
5855                            lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
5856                    canScrollVertically());
5857            child.measure(widthSpec, heightSpec);
5858        }
5859
5860        /**
5861         * Calculate a MeasureSpec value for measuring a child view in one dimension.
5862         *
5863         * @param parentSize Size of the parent view where the child will be placed
5864         * @param padding Total space currently consumed by other elements of parent
5865         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
5866         *                       Generally obtained from the child view's LayoutParams
5867         * @param canScroll true if the parent RecyclerView can scroll in this dimension
5868         *
5869         * @return a MeasureSpec value for the child view
5870         */
5871        public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
5872                boolean canScroll) {
5873            int size = Math.max(0, parentSize - padding);
5874            int resultSize = 0;
5875            int resultMode = 0;
5876
5877            if (canScroll) {
5878                if (childDimension >= 0) {
5879                    resultSize = childDimension;
5880                    resultMode = MeasureSpec.EXACTLY;
5881                } else {
5882                    // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
5883                    // instead using UNSPECIFIED.
5884                    resultSize = 0;
5885                    resultMode = MeasureSpec.UNSPECIFIED;
5886                }
5887            } else {
5888                if (childDimension >= 0) {
5889                    resultSize = childDimension;
5890                    resultMode = MeasureSpec.EXACTLY;
5891                } else if (childDimension == LayoutParams.FILL_PARENT) {
5892                    resultSize = size;
5893                    resultMode = MeasureSpec.EXACTLY;
5894                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
5895                    resultSize = size;
5896                    resultMode = MeasureSpec.AT_MOST;
5897                }
5898            }
5899            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
5900        }
5901
5902        /**
5903         * Returns the measured width of the given child, plus the additional size of
5904         * any insets applied by {@link ItemDecoration ItemDecorations}.
5905         *
5906         * @param child Child view to query
5907         * @return child's measured width plus <code>ItemDecoration</code> insets
5908         *
5909         * @see View#getMeasuredWidth()
5910         */
5911        public int getDecoratedMeasuredWidth(View child) {
5912            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
5913            return child.getMeasuredWidth() + insets.left + insets.right;
5914        }
5915
5916        /**
5917         * Returns the measured height of the given child, plus the additional size of
5918         * any insets applied by {@link ItemDecoration ItemDecorations}.
5919         *
5920         * @param child Child view to query
5921         * @return child's measured height plus <code>ItemDecoration</code> insets
5922         *
5923         * @see View#getMeasuredHeight()
5924         */
5925        public int getDecoratedMeasuredHeight(View child) {
5926            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
5927            return child.getMeasuredHeight() + insets.top + insets.bottom;
5928        }
5929
5930        /**
5931         * Lay out the given child view within the RecyclerView using coordinates that
5932         * include any current {@link ItemDecoration ItemDecorations}.
5933         *
5934         * <p>LayoutManagers should prefer working in sizes and coordinates that include
5935         * item decoration insets whenever possible. This allows the LayoutManager to effectively
5936         * ignore decoration insets within measurement and layout code. See the following
5937         * methods:</p>
5938         * <ul>
5939         *     <li>{@link #measureChild(View, int, int)}</li>
5940         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
5941         *     <li>{@link #getDecoratedLeft(View)}</li>
5942         *     <li>{@link #getDecoratedTop(View)}</li>
5943         *     <li>{@link #getDecoratedRight(View)}</li>
5944         *     <li>{@link #getDecoratedBottom(View)}</li>
5945         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
5946         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
5947         * </ul>
5948         *
5949         * @param child Child to lay out
5950         * @param left Left edge, with item decoration insets included
5951         * @param top Top edge, with item decoration insets included
5952         * @param right Right edge, with item decoration insets included
5953         * @param bottom Bottom edge, with item decoration insets included
5954         *
5955         * @see View#layout(int, int, int, int)
5956         */
5957        public void layoutDecorated(View child, int left, int top, int right, int bottom) {
5958            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
5959            child.layout(left + insets.left, top + insets.top, right - insets.right,
5960                    bottom - insets.bottom);
5961        }
5962
5963        /**
5964         * Returns the left edge of the given child view within its parent, offset by any applied
5965         * {@link ItemDecoration ItemDecorations}.
5966         *
5967         * @param child Child to query
5968         * @return Child left edge with offsets applied
5969         * @see #getLeftDecorationWidth(View)
5970         */
5971        public int getDecoratedLeft(View child) {
5972            return child.getLeft() - getLeftDecorationWidth(child);
5973        }
5974
5975        /**
5976         * Returns the top edge of the given child view within its parent, offset by any applied
5977         * {@link ItemDecoration ItemDecorations}.
5978         *
5979         * @param child Child to query
5980         * @return Child top edge with offsets applied
5981         * @see #getTopDecorationHeight(View)
5982         */
5983        public int getDecoratedTop(View child) {
5984            return child.getTop() - getTopDecorationHeight(child);
5985        }
5986
5987        /**
5988         * Returns the right edge of the given child view within its parent, offset by any applied
5989         * {@link ItemDecoration ItemDecorations}.
5990         *
5991         * @param child Child to query
5992         * @return Child right edge with offsets applied
5993         * @see #getRightDecorationWidth(View)
5994         */
5995        public int getDecoratedRight(View child) {
5996            return child.getRight() + getRightDecorationWidth(child);
5997        }
5998
5999        /**
6000         * Returns the bottom edge of the given child view within its parent, offset by any applied
6001         * {@link ItemDecoration ItemDecorations}.
6002         *
6003         * @param child Child to query
6004         * @return Child bottom edge with offsets applied
6005         * @see #getBottomDecorationHeight(View)
6006         */
6007        public int getDecoratedBottom(View child) {
6008            return child.getBottom() + getBottomDecorationHeight(child);
6009        }
6010
6011        /**
6012         * Calculates the item decor insets applied to the given child and updates the provided
6013         * Rect instance with the inset values.
6014         * <ul>
6015         *     <li>The Rect's left is set to the total width of left decorations.</li>
6016         *     <li>The Rect's top is set to the total height of top decorations.</li>
6017         *     <li>The Rect's right is set to the total width of right decorations.</li>
6018         *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
6019         * </ul>
6020         * <p>
6021         * Note that item decorations are automatically calculated when one of the LayoutManager's
6022         * measure child methods is called. If you need to measure the child with custom specs via
6023         * {@link View#measure(int, int)}, you can use this method to get decorations.
6024         *
6025         * @param child The child view whose decorations should be calculated
6026         * @param outRect The Rect to hold result values
6027         */
6028        public void calculateItemDecorationsForChild(View child, Rect outRect) {
6029            if (mRecyclerView == null) {
6030                outRect.set(0, 0, 0, 0);
6031                return;
6032            }
6033            Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
6034            outRect.set(insets);
6035        }
6036
6037        /**
6038         * Returns the total height of item decorations applied to child's top.
6039         * <p>
6040         * Note that this value is not updated until the View is measured or
6041         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
6042         *
6043         * @param child Child to query
6044         * @return The total height of item decorations applied to the child's top.
6045         * @see #getDecoratedTop(View)
6046         * @see #calculateItemDecorationsForChild(View, Rect)
6047         */
6048        public int getTopDecorationHeight(View child) {
6049            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
6050        }
6051
6052        /**
6053         * Returns the total height of item decorations applied to child's bottom.
6054         * <p>
6055         * Note that this value is not updated until the View is measured or
6056         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
6057         *
6058         * @param child Child to query
6059         * @return The total height of item decorations applied to the child's bottom.
6060         * @see #getDecoratedBottom(View)
6061         * @see #calculateItemDecorationsForChild(View, Rect)
6062         */
6063        public int getBottomDecorationHeight(View child) {
6064            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
6065        }
6066
6067        /**
6068         * Returns the total width of item decorations applied to child's left.
6069         * <p>
6070         * Note that this value is not updated until the View is measured or
6071         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
6072         *
6073         * @param child Child to query
6074         * @return The total width of item decorations applied to the child's left.
6075         * @see #getDecoratedLeft(View)
6076         * @see #calculateItemDecorationsForChild(View, Rect)
6077         */
6078        public int getLeftDecorationWidth(View child) {
6079            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
6080        }
6081
6082        /**
6083         * Returns the total width of item decorations applied to child's right.
6084         * <p>
6085         * Note that this value is not updated until the View is measured or
6086         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
6087         *
6088         * @param child Child to query
6089         * @return The total width of item decorations applied to the child's right.
6090         * @see #getDecoratedRight(View)
6091         * @see #calculateItemDecorationsForChild(View, Rect)
6092         */
6093        public int getRightDecorationWidth(View child) {
6094            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
6095        }
6096
6097        /**
6098         * Called when searching for a focusable view in the given direction has failed
6099         * for the current content of the RecyclerView.
6100         *
6101         * <p>This is the LayoutManager's opportunity to populate views in the given direction
6102         * to fulfill the request if it can. The LayoutManager should attach and return
6103         * the view to be focused. The default implementation returns null.</p>
6104         *
6105         * @param focused   The currently focused view
6106         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
6107         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
6108         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
6109         *                  or 0 for not applicable
6110         * @param recycler  The recycler to use for obtaining views for currently offscreen items
6111         * @param state     Transient state of RecyclerView
6112         * @return The chosen view to be focused
6113         */
6114        public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
6115                State state) {
6116            return null;
6117        }
6118
6119        /**
6120         * This method gives a LayoutManager an opportunity to intercept the initial focus search
6121         * before the default behavior of {@link FocusFinder} is used. If this method returns
6122         * null FocusFinder will attempt to find a focusable child view. If it fails
6123         * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
6124         * will be called to give the LayoutManager an opportunity to add new views for items
6125         * that did not have attached views representing them. The LayoutManager should not add
6126         * or remove views from this method.
6127         *
6128         * @param focused The currently focused view
6129         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
6130         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
6131         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
6132         * @return A descendant view to focus or null to fall back to default behavior.
6133         *         The default implementation returns null.
6134         */
6135        public View onInterceptFocusSearch(View focused, int direction) {
6136            return null;
6137        }
6138
6139        /**
6140         * Called when a child of the RecyclerView wants a particular rectangle to be positioned
6141         * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
6142         * android.graphics.Rect, boolean)} for more details.
6143         *
6144         * <p>The base implementation will attempt to perform a standard programmatic scroll
6145         * to bring the given rect into view, within the padded area of the RecyclerView.</p>
6146         *
6147         * @param child The direct child making the request.
6148         * @param rect  The rectangle in the child's coordinates the child
6149         *              wishes to be on the screen.
6150         * @param immediate True to forbid animated or delayed scrolling,
6151         *                  false otherwise
6152         * @return Whether the group scrolled to handle the operation
6153         */
6154        public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
6155                boolean immediate) {
6156            final int parentLeft = getPaddingLeft();
6157            final int parentTop = getPaddingTop();
6158            final int parentRight = getWidth() - getPaddingRight();
6159            final int parentBottom = getHeight() - getPaddingBottom();
6160            final int childLeft = child.getLeft() + rect.left;
6161            final int childTop = child.getTop() + rect.top;
6162            final int childRight = childLeft + rect.width();
6163            final int childBottom = childTop + rect.height();
6164
6165            final int offScreenLeft = Math.min(0, childLeft - parentLeft);
6166            final int offScreenTop = Math.min(0, childTop - parentTop);
6167            final int offScreenRight = Math.max(0, childRight - parentRight);
6168            final int offScreenBottom = Math.max(0, childBottom - parentBottom);
6169
6170            // Favor the "start" layout direction over the end when bringing one side or the other
6171            // of a large rect into view.
6172            final int dx;
6173            if (ViewCompat.getLayoutDirection(parent) == ViewCompat.LAYOUT_DIRECTION_RTL) {
6174                dx = offScreenRight != 0 ? offScreenRight : offScreenLeft;
6175            } else {
6176                dx = offScreenLeft != 0 ? offScreenLeft : offScreenRight;
6177            }
6178
6179            // Favor bringing the top into view over the bottom
6180            final int dy = offScreenTop != 0 ? offScreenTop : offScreenBottom;
6181
6182            if (dx != 0 || dy != 0) {
6183                if (immediate) {
6184                    parent.scrollBy(dx, dy);
6185                } else {
6186                    parent.smoothScrollBy(dx, dy);
6187                }
6188                return true;
6189            }
6190            return false;
6191        }
6192
6193        /**
6194         * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
6195         */
6196        @Deprecated
6197        public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
6198            // eat the request if we are in the middle of a scroll or layout
6199            return isSmoothScrolling() || parent.mRunningLayoutOrScroll;
6200        }
6201
6202        /**
6203         * Called when a descendant view of the RecyclerView requests focus.
6204         *
6205         * <p>A LayoutManager wishing to keep focused views aligned in a specific
6206         * portion of the view may implement that behavior in an override of this method.</p>
6207         *
6208         * <p>If the LayoutManager executes different behavior that should override the default
6209         * behavior of scrolling the focused child on screen instead of running alongside it,
6210         * this method should return true.</p>
6211         *
6212         * @param parent  The RecyclerView hosting this LayoutManager
6213         * @param state   Current state of RecyclerView
6214         * @param child   Direct child of the RecyclerView containing the newly focused view
6215         * @param focused The newly focused view. This may be the same view as child or it may be
6216         *                null
6217         * @return true if the default scroll behavior should be suppressed
6218         */
6219        public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
6220                View focused) {
6221            return onRequestChildFocus(parent, child, focused);
6222        }
6223
6224        /**
6225         * Called if the RecyclerView this LayoutManager is bound to has a different adapter set.
6226         * The LayoutManager may use this opportunity to clear caches and configure state such
6227         * that it can relayout appropriately with the new data and potentially new view types.
6228         *
6229         * <p>The default implementation removes all currently attached views.</p>
6230         *
6231         * @param oldAdapter The previous adapter instance. Will be null if there was previously no
6232         *                   adapter.
6233         * @param newAdapter The new adapter instance. Might be null if
6234         *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
6235         */
6236        public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
6237        }
6238
6239        /**
6240         * Called to populate focusable views within the RecyclerView.
6241         *
6242         * <p>The LayoutManager implementation should return <code>true</code> if the default
6243         * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
6244         * suppressed.</p>
6245         *
6246         * <p>The default implementation returns <code>false</code> to trigger RecyclerView
6247         * to fall back to the default ViewGroup behavior.</p>
6248         *
6249         * @param recyclerView The RecyclerView hosting this LayoutManager
6250         * @param views List of output views. This method should add valid focusable views
6251         *              to this list.
6252         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
6253         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
6254         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
6255         * @param focusableMode The type of focusables to be added.
6256         *
6257         * @return true to suppress the default behavior, false to add default focusables after
6258         *         this method returns.
6259         *
6260         * @see #FOCUSABLES_ALL
6261         * @see #FOCUSABLES_TOUCH_MODE
6262         */
6263        public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,
6264                int direction, int focusableMode) {
6265            return false;
6266        }
6267
6268        /**
6269         * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving
6270         * detailed information on what has actually changed.
6271         *
6272         * @param recyclerView
6273         */
6274        public void onItemsChanged(RecyclerView recyclerView) {
6275        }
6276
6277        /**
6278         * Called when items have been added to the adapter. The LayoutManager may choose to
6279         * requestLayout if the inserted items would require refreshing the currently visible set
6280         * of child views. (e.g. currently empty space would be filled by appended items, etc.)
6281         *
6282         * @param recyclerView
6283         * @param positionStart
6284         * @param itemCount
6285         */
6286        public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
6287        }
6288
6289        /**
6290         * Called when items have been removed from the adapter.
6291         *
6292         * @param recyclerView
6293         * @param positionStart
6294         * @param itemCount
6295         */
6296        public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
6297        }
6298
6299        /**
6300         * Called when items have been changed in the adapter.
6301         *
6302         * @param recyclerView
6303         * @param positionStart
6304         * @param itemCount
6305         */
6306        public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
6307        }
6308
6309        /**
6310         * Called when an item is moved withing the adapter.
6311         * <p>
6312         * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
6313         * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
6314         * is called.
6315         *
6316         * @param recyclerView
6317         * @param from
6318         * @param to
6319         * @param itemCount
6320         */
6321        public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
6322
6323        }
6324
6325
6326        /**
6327         * <p>Override this method if you want to support scroll bars.</p>
6328         *
6329         * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
6330         *
6331         * <p>Default implementation returns 0.</p>
6332         *
6333         * @param state Current state of RecyclerView
6334         * @return The horizontal extent of the scrollbar's thumb
6335         * @see RecyclerView#computeHorizontalScrollExtent()
6336         */
6337        public int computeHorizontalScrollExtent(State state) {
6338            return 0;
6339        }
6340
6341        /**
6342         * <p>Override this method if you want to support scroll bars.</p>
6343         *
6344         * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
6345         *
6346         * <p>Default implementation returns 0.</p>
6347         *
6348         * @param state Current State of RecyclerView where you can find total item count
6349         * @return The horizontal offset of the scrollbar's thumb
6350         * @see RecyclerView#computeHorizontalScrollOffset()
6351         */
6352        public int computeHorizontalScrollOffset(State state) {
6353            return 0;
6354        }
6355
6356        /**
6357         * <p>Override this method if you want to support scroll bars.</p>
6358         *
6359         * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
6360         *
6361         * <p>Default implementation returns 0.</p>
6362         *
6363         * @param state Current State of RecyclerView where you can find total item count
6364         * @return The total horizontal range represented by the vertical scrollbar
6365         * @see RecyclerView#computeHorizontalScrollRange()
6366         */
6367        public int computeHorizontalScrollRange(State state) {
6368            return 0;
6369        }
6370
6371        /**
6372         * <p>Override this method if you want to support scroll bars.</p>
6373         *
6374         * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
6375         *
6376         * <p>Default implementation returns 0.</p>
6377         *
6378         * @param state Current state of RecyclerView
6379         * @return The vertical extent of the scrollbar's thumb
6380         * @see RecyclerView#computeVerticalScrollExtent()
6381         */
6382        public int computeVerticalScrollExtent(State state) {
6383            return 0;
6384        }
6385
6386        /**
6387         * <p>Override this method if you want to support scroll bars.</p>
6388         *
6389         * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
6390         *
6391         * <p>Default implementation returns 0.</p>
6392         *
6393         * @param state Current State of RecyclerView where you can find total item count
6394         * @return The vertical offset of the scrollbar's thumb
6395         * @see RecyclerView#computeVerticalScrollOffset()
6396         */
6397        public int computeVerticalScrollOffset(State state) {
6398            return 0;
6399        }
6400
6401        /**
6402         * <p>Override this method if you want to support scroll bars.</p>
6403         *
6404         * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
6405         *
6406         * <p>Default implementation returns 0.</p>
6407         *
6408         * @param state Current State of RecyclerView where you can find total item count
6409         * @return The total vertical range represented by the vertical scrollbar
6410         * @see RecyclerView#computeVerticalScrollRange()
6411         */
6412        public int computeVerticalScrollRange(State state) {
6413            return 0;
6414        }
6415
6416        /**
6417         * Measure the attached RecyclerView. Implementations must call
6418         * {@link #setMeasuredDimension(int, int)} before returning.
6419         *
6420         * <p>The default implementation will handle EXACTLY measurements and respect
6421         * the minimum width and height properties of the host RecyclerView if measured
6422         * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
6423         * will consume all available space.</p>
6424         *
6425         * @param recycler Recycler
6426         * @param state Transient state of RecyclerView
6427         * @param widthSpec Width {@link android.view.View.MeasureSpec}
6428         * @param heightSpec Height {@link android.view.View.MeasureSpec}
6429         */
6430        public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
6431            mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
6432        }
6433
6434        /**
6435         * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
6436         * host RecyclerView.
6437         *
6438         * @param widthSize Measured width
6439         * @param heightSize Measured height
6440         */
6441        public void setMeasuredDimension(int widthSize, int heightSize) {
6442            mRecyclerView.setMeasuredDimension(widthSize, heightSize);
6443        }
6444
6445        /**
6446         * @return The host RecyclerView's {@link View#getMinimumWidth()}
6447         */
6448        public int getMinimumWidth() {
6449            return ViewCompat.getMinimumWidth(mRecyclerView);
6450        }
6451
6452        /**
6453         * @return The host RecyclerView's {@link View#getMinimumHeight()}
6454         */
6455        public int getMinimumHeight() {
6456            return ViewCompat.getMinimumHeight(mRecyclerView);
6457        }
6458        /**
6459         * <p>Called when the LayoutManager should save its state. This is a good time to save your
6460         * scroll position, configuration and anything else that may be required to restore the same
6461         * layout state if the LayoutManager is recreated.</p>
6462         * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
6463         * restore. This will let you share information between your LayoutManagers but it is also
6464         * your responsibility to make sure they use the same parcelable class.</p>
6465         *
6466         * @return Necessary information for LayoutManager to be able to restore its state
6467         */
6468        public Parcelable onSaveInstanceState() {
6469            return null;
6470        }
6471
6472
6473        public void onRestoreInstanceState(Parcelable state) {
6474
6475        }
6476
6477        void stopSmoothScroller() {
6478            if (mSmoothScroller != null) {
6479                mSmoothScroller.stop();
6480            }
6481        }
6482
6483        private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
6484            if (mSmoothScroller == smoothScroller) {
6485                mSmoothScroller = null;
6486            }
6487        }
6488
6489        /**
6490         * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
6491         *
6492         * @param state The new scroll state for RecyclerView
6493         */
6494        public void onScrollStateChanged(int state) {
6495        }
6496
6497        /**
6498         * Removes all views and recycles them using the given recycler.
6499         * <p>
6500         * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
6501         * <p>
6502         * If a View is marked as "ignored", it is not removed nor recycled.
6503         *
6504         * @param recycler Recycler to use to recycle children
6505         * @see #removeAndRecycleView(View, Recycler)
6506         * @see #removeAndRecycleViewAt(int, Recycler)
6507         * @see #ignoreView(View)
6508         */
6509        public void removeAndRecycleAllViews(Recycler recycler) {
6510            for (int i = getChildCount() - 1; i >= 0; i--) {
6511                final View view = getChildAt(i);
6512                if (!getChildViewHolderInt(view).shouldIgnore()) {
6513                    removeAndRecycleViewAt(i, recycler);
6514                }
6515            }
6516        }
6517
6518        // called by accessibility delegate
6519        void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
6520            onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState,
6521                    info);
6522        }
6523
6524        /**
6525         * Called by the AccessibilityDelegate when the information about the current layout should
6526         * be populated.
6527         * <p>
6528         * Default implementation adds a {@link
6529         * android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}.
6530         * <p>
6531         * You should override
6532         * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
6533         * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
6534         * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
6535         * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
6536         * more accurate accessibility information.
6537         *
6538         * @param recycler The Recycler that can be used to convert view positions into adapter
6539         *                 positions
6540         * @param state    The current state of RecyclerView
6541         * @param info     The info that should be filled by the LayoutManager
6542         * @see View#onInitializeAccessibilityNodeInfo(
6543         *android.view.accessibility.AccessibilityNodeInfo)
6544         * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
6545         * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
6546         * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
6547         * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
6548         */
6549        public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
6550                AccessibilityNodeInfoCompat info) {
6551            info.setClassName(RecyclerView.class.getName());
6552            if (ViewCompat.canScrollVertically(mRecyclerView, -1) ||
6553                    ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
6554                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
6555                info.setScrollable(true);
6556            }
6557            if (ViewCompat.canScrollVertically(mRecyclerView, 1) ||
6558                    ViewCompat.canScrollHorizontally(mRecyclerView, 1)) {
6559                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
6560                info.setScrollable(true);
6561            }
6562            final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo
6563                    = AccessibilityNodeInfoCompat.CollectionInfoCompat
6564                    .obtain(getRowCountForAccessibility(recycler, state),
6565                            getColumnCountForAccessibility(recycler, state),
6566                            isLayoutHierarchical(recycler, state),
6567                            getSelectionModeForAccessibility(recycler, state));
6568            info.setCollectionInfo(collectionInfo);
6569        }
6570
6571        // called by accessibility delegate
6572        public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
6573            onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
6574        }
6575
6576        /**
6577         * Called by the accessibility delegate to initialize an accessibility event.
6578         * <p>
6579         * Default implementation adds item count and scroll information to the event.
6580         *
6581         * @param recycler The Recycler that can be used to convert view positions into adapter
6582         *                 positions
6583         * @param state    The current state of RecyclerView
6584         * @param event    The event instance to initialize
6585         * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
6586         */
6587        public void onInitializeAccessibilityEvent(Recycler recycler, State state,
6588                AccessibilityEvent event) {
6589            final AccessibilityRecordCompat record = AccessibilityEventCompat
6590                    .asRecord(event);
6591            if (mRecyclerView == null || record == null) {
6592                return;
6593            }
6594            record.setScrollable(ViewCompat.canScrollVertically(mRecyclerView, 1)
6595                    || ViewCompat.canScrollVertically(mRecyclerView, -1)
6596                    || ViewCompat.canScrollHorizontally(mRecyclerView, -1)
6597                    || ViewCompat.canScrollHorizontally(mRecyclerView, 1));
6598
6599            if (mRecyclerView.mAdapter != null) {
6600                record.setItemCount(mRecyclerView.mAdapter.getItemCount());
6601            }
6602        }
6603
6604        // called by accessibility delegate
6605        void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) {
6606            final ViewHolder vh = getChildViewHolderInt(host);
6607            // avoid trying to create accessibility node info for removed children
6608            if (vh != null && !vh.isRemoved()) {
6609                onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
6610                        mRecyclerView.mState, host, info);
6611            }
6612        }
6613
6614        /**
6615         * Called by the AccessibilityDelegate when the accessibility information for a specific
6616         * item should be populated.
6617         * <p>
6618         * Default implementation adds basic positioning information about the item.
6619         *
6620         * @param recycler The Recycler that can be used to convert view positions into adapter
6621         *                 positions
6622         * @param state    The current state of RecyclerView
6623         * @param host     The child for which accessibility node info should be populated
6624         * @param info     The info to fill out about the item
6625         * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
6626         * android.view.accessibility.AccessibilityNodeInfo)
6627         */
6628        public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
6629                View host, AccessibilityNodeInfoCompat info) {
6630            int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
6631            int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
6632            final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo
6633                    = AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
6634                    columnIndexGuess, 1, false, false);
6635            info.setCollectionItemInfo(itemInfo);
6636        }
6637
6638        /**
6639         * A LayoutManager can call this method to force RecyclerView to run simple animations in
6640         * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
6641         * change).
6642         * <p>
6643         * Note that, calling this method will not guarantee that RecyclerView will run animations
6644         * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
6645         * not run any animations but will still clear this flag after the layout is complete.
6646         *
6647         */
6648        public void requestSimpleAnimationsInNextLayout() {
6649            mRequestedSimpleAnimations = true;
6650        }
6651
6652        /**
6653         * Returns the selection mode for accessibility. Should be
6654         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE},
6655         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or
6656         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}.
6657         * <p>
6658         * Default implementation returns
6659         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
6660         *
6661         * @param recycler The Recycler that can be used to convert view positions into adapter
6662         *                 positions
6663         * @param state    The current state of RecyclerView
6664         * @return Selection mode for accessibility. Default implementation returns
6665         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
6666         */
6667        public int getSelectionModeForAccessibility(Recycler recycler, State state) {
6668            return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
6669        }
6670
6671        /**
6672         * Returns the number of rows for accessibility.
6673         * <p>
6674         * Default implementation returns the number of items in the adapter if LayoutManager
6675         * supports vertical scrolling or 1 if LayoutManager does not support vertical
6676         * scrolling.
6677         *
6678         * @param recycler The Recycler that can be used to convert view positions into adapter
6679         *                 positions
6680         * @param state    The current state of RecyclerView
6681         * @return The number of rows in LayoutManager for accessibility.
6682         */
6683        public int getRowCountForAccessibility(Recycler recycler, State state) {
6684            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
6685                return 1;
6686            }
6687            return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
6688        }
6689
6690        /**
6691         * Returns the number of columns for accessibility.
6692         * <p>
6693         * Default implementation returns the number of items in the adapter if LayoutManager
6694         * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
6695         * scrolling.
6696         *
6697         * @param recycler The Recycler that can be used to convert view positions into adapter
6698         *                 positions
6699         * @param state    The current state of RecyclerView
6700         * @return The number of rows in LayoutManager for accessibility.
6701         */
6702        public int getColumnCountForAccessibility(Recycler recycler, State state) {
6703            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
6704                return 1;
6705            }
6706            return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
6707        }
6708
6709        /**
6710         * Returns whether layout is hierarchical or not to be used for accessibility.
6711         * <p>
6712         * Default implementation returns false.
6713         *
6714         * @param recycler The Recycler that can be used to convert view positions into adapter
6715         *                 positions
6716         * @param state    The current state of RecyclerView
6717         * @return True if layout is hierarchical.
6718         */
6719        public boolean isLayoutHierarchical(Recycler recycler, State state) {
6720            return false;
6721        }
6722
6723        // called by accessibility delegate
6724        boolean performAccessibilityAction(int action, Bundle args) {
6725            return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
6726                    action, args);
6727        }
6728
6729        /**
6730         * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
6731         *
6732         * @param recycler  The Recycler that can be used to convert view positions into adapter
6733         *                  positions
6734         * @param state     The current state of RecyclerView
6735         * @param action    The action to perform
6736         * @param args      Optional action arguments
6737         * @see View#performAccessibilityAction(int, android.os.Bundle)
6738         */
6739        public boolean performAccessibilityAction(Recycler recycler, State state, int action,
6740                Bundle args) {
6741            if (mRecyclerView == null) {
6742                return false;
6743            }
6744            int vScroll = 0, hScroll = 0;
6745            switch (action) {
6746                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
6747                    if (ViewCompat.canScrollVertically(mRecyclerView, -1)) {
6748                        vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
6749                    }
6750                    if (ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
6751                        hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
6752                    }
6753                    break;
6754                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
6755                    if (ViewCompat.canScrollVertically(mRecyclerView, 1)) {
6756                        vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
6757                    }
6758                    if (ViewCompat.canScrollHorizontally(mRecyclerView, 1)) {
6759                        hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
6760                    }
6761                    break;
6762            }
6763            if (vScroll == 0 && hScroll == 0) {
6764                return false;
6765            }
6766            mRecyclerView.scrollBy(hScroll, vScroll);
6767            return true;
6768        }
6769
6770        // called by accessibility delegate
6771        boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
6772            return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
6773                    view, action, args);
6774        }
6775
6776        /**
6777         * Called by AccessibilityDelegate when an accessibility action is requested on one of the
6778         * children of LayoutManager.
6779         * <p>
6780         * Default implementation does not do anything.
6781         *
6782         * @param recycler The Recycler that can be used to convert view positions into adapter
6783         *                 positions
6784         * @param state    The current state of RecyclerView
6785         * @param view     The child view on which the action is performed
6786         * @param action   The action to perform
6787         * @param args     Optional action arguments
6788         * @return true if action is handled
6789         * @see View#performAccessibilityAction(int, android.os.Bundle)
6790         */
6791        public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
6792                int action, Bundle args) {
6793            return false;
6794        }
6795    }
6796
6797    private void removeFromDisappearingList(View child) {
6798        mDisappearingViewsInLayoutPass.remove(child);
6799    }
6800
6801    private void addToDisappearingList(View child) {
6802        if (!mDisappearingViewsInLayoutPass.contains(child)) {
6803            mDisappearingViewsInLayoutPass.add(child);
6804        }
6805    }
6806
6807    /**
6808     * An ItemDecoration allows the application to add a special drawing and layout offset
6809     * to specific item views from the adapter's data set. This can be useful for drawing dividers
6810     * between items, highlights, visual grouping boundaries and more.
6811     *
6812     * <p>All ItemDecorations are drawn in the order they were added, before the item
6813     * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
6814     * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
6815     * RecyclerView.State)}.</p>
6816     */
6817    public static abstract class ItemDecoration {
6818        /**
6819         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
6820         * Any content drawn by this method will be drawn before the item views are drawn,
6821         * and will thus appear underneath the views.
6822         *
6823         * @param c Canvas to draw into
6824         * @param parent RecyclerView this ItemDecoration is drawing into
6825         * @param state The current state of RecyclerView
6826         */
6827        public void onDraw(Canvas c, RecyclerView parent, State state) {
6828            onDraw(c, parent);
6829        }
6830
6831        /**
6832         * @deprecated
6833         * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
6834         */
6835        @Deprecated
6836        public void onDraw(Canvas c, RecyclerView parent) {
6837        }
6838
6839        /**
6840         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
6841         * Any content drawn by this method will be drawn after the item views are drawn
6842         * and will thus appear over the views.
6843         *
6844         * @param c Canvas to draw into
6845         * @param parent RecyclerView this ItemDecoration is drawing into
6846         * @param state The current state of RecyclerView.
6847         */
6848        public void onDrawOver(Canvas c, RecyclerView parent, State state) {
6849            onDrawOver(c, parent);
6850        }
6851
6852        /**
6853         * @deprecated
6854         * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
6855         */
6856        @Deprecated
6857        public void onDrawOver(Canvas c, RecyclerView parent) {
6858        }
6859
6860
6861        /**
6862         * @deprecated
6863         * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
6864         */
6865        @Deprecated
6866        public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
6867            outRect.set(0, 0, 0, 0);
6868        }
6869
6870        /**
6871         * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
6872         * the number of pixels that the item view should be inset by, similar to padding or margin.
6873         * The default implementation sets the bounds of outRect to 0 and returns.
6874         *
6875         * <p>
6876         * If this ItemDecoration does not affect the positioning of item views, it should set
6877         * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
6878         * before returning.
6879         *
6880         * <p>
6881         * If you need to access Adapter for additional data, you can call
6882         * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
6883         * View.
6884         *
6885         * @param outRect Rect to receive the output.
6886         * @param view    The child view to decorate
6887         * @param parent  RecyclerView this ItemDecoration is decorating
6888         * @param state   The current state of RecyclerView.
6889         */
6890        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
6891            getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
6892                    parent);
6893        }
6894    }
6895
6896    /**
6897     * An OnItemTouchListener allows the application to intercept touch events in progress at the
6898     * view hierarchy level of the RecyclerView before those touch events are considered for
6899     * RecyclerView's own scrolling behavior.
6900     *
6901     * <p>This can be useful for applications that wish to implement various forms of gestural
6902     * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
6903     * a touch interaction already in progress even if the RecyclerView is already handling that
6904     * gesture stream itself for the purposes of scrolling.</p>
6905     */
6906    public interface OnItemTouchListener {
6907        /**
6908         * Silently observe and/or take over touch events sent to the RecyclerView
6909         * before they are handled by either the RecyclerView itself or its child views.
6910         *
6911         * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
6912         * in the order in which each listener was added, before any other touch processing
6913         * by the RecyclerView itself or child views occurs.</p>
6914         *
6915         * @param e MotionEvent describing the touch event. All coordinates are in
6916         *          the RecyclerView's coordinate system.
6917         * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
6918         *         to continue with the current behavior and continue observing future events in
6919         *         the gesture.
6920         */
6921        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
6922
6923        /**
6924         * Process a touch event as part of a gesture that was claimed by returning true from
6925         * a previous call to {@link #onInterceptTouchEvent}.
6926         *
6927         * @param e MotionEvent describing the touch event. All coordinates are in
6928         *          the RecyclerView's coordinate system.
6929         */
6930        public void onTouchEvent(RecyclerView rv, MotionEvent e);
6931    }
6932
6933    /**
6934     * An OnScrollListener can be set on a RecyclerView to receive messages
6935     * when a scrolling event has occurred on that RecyclerView.
6936     *
6937     * @see RecyclerView#setOnScrollListener(OnScrollListener)
6938     */
6939    abstract static public class OnScrollListener {
6940        /**
6941         * Callback method to be invoked when RecyclerView's scroll state changes.
6942         *
6943         * @param recyclerView The RecyclerView whose scroll state has changed.
6944         * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
6945         *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
6946         */
6947        public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
6948
6949        /**
6950         * Callback method to be invoked when the RecyclerView has been scrolled. This will be
6951         * called after the scroll has completed.
6952         * <p>
6953         * This callback will also be called if visible item range changes after a layout
6954         * calculation. In that case, dx and dy will be 0.
6955         *
6956         * @param recyclerView The RecyclerView which scrolled.
6957         * @param dx The amount of horizontal scroll.
6958         * @param dy The amount of vertical scroll.
6959         */
6960        public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
6961    }
6962
6963    /**
6964     * A RecyclerListener can be set on a RecyclerView to receive messages whenever
6965     * a view is recycled.
6966     *
6967     * @see RecyclerView#setRecyclerListener(RecyclerListener)
6968     */
6969    public interface RecyclerListener {
6970
6971        /**
6972         * This method is called whenever the view in the ViewHolder is recycled.
6973         *
6974         * @param holder The ViewHolder containing the view that was recycled
6975         */
6976        public void onViewRecycled(ViewHolder holder);
6977    }
6978
6979    /**
6980     * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
6981     *
6982     * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
6983     * potentially expensive {@link View#findViewById(int)} results.</p>
6984     *
6985     * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
6986     * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
6987     * their own custom ViewHolder implementations to store data that makes binding view contents
6988     * easier. Implementations should assume that individual item views will hold strong references
6989     * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
6990     * strong references to extra off-screen item views for caching purposes</p>
6991     */
6992    public static abstract class ViewHolder {
6993        public final View itemView;
6994        int mPosition = NO_POSITION;
6995        int mOldPosition = NO_POSITION;
6996        long mItemId = NO_ID;
6997        int mItemViewType = INVALID_TYPE;
6998        int mPreLayoutPosition = NO_POSITION;
6999
7000        // The item that this holder is shadowing during an item change event/animation
7001        ViewHolder mShadowedHolder = null;
7002        // The item that is shadowing this holder during an item change event/animation
7003        ViewHolder mShadowingHolder = null;
7004
7005        /**
7006         * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
7007         * are all valid.
7008         */
7009        static final int FLAG_BOUND = 1 << 0;
7010
7011        /**
7012         * The data this ViewHolder's view reflects is stale and needs to be rebound
7013         * by the adapter. mPosition and mItemId are consistent.
7014         */
7015        static final int FLAG_UPDATE = 1 << 1;
7016
7017        /**
7018         * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
7019         * are not to be trusted and may no longer match the item view type.
7020         * This ViewHolder must be fully rebound to different data.
7021         */
7022        static final int FLAG_INVALID = 1 << 2;
7023
7024        /**
7025         * This ViewHolder points at data that represents an item previously removed from the
7026         * data set. Its view may still be used for things like outgoing animations.
7027         */
7028        static final int FLAG_REMOVED = 1 << 3;
7029
7030        /**
7031         * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
7032         * and is intended to keep views around during animations.
7033         */
7034        static final int FLAG_NOT_RECYCLABLE = 1 << 4;
7035
7036        /**
7037         * This ViewHolder is returned from scrap which means we are expecting an addView call
7038         * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
7039         * the end of the layout pass and then recycled by RecyclerView if it is not added back to
7040         * the RecyclerView.
7041         */
7042        static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
7043
7044        /**
7045         * This ViewHolder's contents have changed. This flag is used as an indication that
7046         * change animations may be used, if supported by the ItemAnimator.
7047         */
7048        static final int FLAG_CHANGED = 1 << 6;
7049
7050        /**
7051         * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
7052         * it unless LayoutManager is replaced.
7053         * It is still fully visible to the LayoutManager.
7054         */
7055        static final int FLAG_IGNORE = 1 << 7;
7056
7057        /**
7058         * When the View is detached form the parent, we set this flag so that we can take correct
7059         * action when we need to remove it or add it back.
7060         */
7061        static final int FLAG_TMP_DETACHED = 1 << 8;
7062
7063        /**
7064         * Set when we can no longer determine the adapter position of this ViewHolder until it is
7065         * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
7066         * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
7067         * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
7068         * re-calculated.
7069         */
7070        static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
7071
7072        private int mFlags;
7073
7074        private int mIsRecyclableCount = 0;
7075
7076        // If non-null, view is currently considered scrap and may be reused for other data by the
7077        // scrap container.
7078        private Recycler mScrapContainer = null;
7079
7080        public ViewHolder(View itemView) {
7081            if (itemView == null) {
7082                throw new IllegalArgumentException("itemView may not be null");
7083            }
7084            this.itemView = itemView;
7085        }
7086
7087        void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
7088            addFlags(ViewHolder.FLAG_REMOVED);
7089            offsetPosition(offset, applyToPreLayout);
7090            mPosition = mNewPosition;
7091        }
7092
7093        void offsetPosition(int offset, boolean applyToPreLayout) {
7094            if (mOldPosition == NO_POSITION) {
7095                mOldPosition = mPosition;
7096            }
7097            if (mPreLayoutPosition == NO_POSITION) {
7098                mPreLayoutPosition = mPosition;
7099            }
7100            if (applyToPreLayout) {
7101                mPreLayoutPosition += offset;
7102            }
7103            mPosition += offset;
7104            if (itemView.getLayoutParams() != null) {
7105                ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
7106            }
7107        }
7108
7109        void clearOldPosition() {
7110            mOldPosition = NO_POSITION;
7111            mPreLayoutPosition = NO_POSITION;
7112        }
7113
7114        void saveOldPosition() {
7115            if (mOldPosition == NO_POSITION) {
7116                mOldPosition = mPosition;
7117            }
7118        }
7119
7120        boolean shouldIgnore() {
7121            return (mFlags & FLAG_IGNORE) != 0;
7122        }
7123
7124        /**
7125         * @deprecated This method is deprecated because its meaning is ambiguous due to the async
7126         * handling of adapter updates. Please use {@link #getLayoutPosition()} or
7127         * {@link #getAdapterPosition()} depending on your use case.
7128         *
7129         * @see #getLayoutPosition()
7130         * @see #getAdapterPosition()
7131         */
7132        @Deprecated
7133        public final int getPosition() {
7134            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
7135        }
7136
7137        /**
7138         * Returns the position of the ViewHolder in terms of the latest layout pass.
7139         * <p>
7140         * This position is mostly used by RecyclerView components to be consistent while
7141         * RecyclerView lazily processes adapter updates.
7142         * <p>
7143         * For performance and animation reasons, RecyclerView batches all adapter updates until the
7144         * next layout pass. This may cause mismatches between the Adapter position of the item and
7145         * the position it had in the latest layout calculations.
7146         * <p>
7147         * LayoutManagers should always call this method while doing calculations based on item
7148         * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
7149         * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
7150         * of the item.
7151         * <p>
7152         * If LayoutManager needs to call an external method that requires the adapter position of
7153         * the item, it can use {@link #getAdapterPosition()} or
7154         * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
7155         *
7156         * @return Returns the adapter position of the ViewHolder in the latest layout pass.
7157         * @see #getAdapterPosition()
7158         */
7159        public final int getLayoutPosition() {
7160            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
7161        }
7162
7163        /**
7164         * Returns the Adapter position of the item represented by this ViewHolder.
7165         * <p>
7166         * Note that this might be different than the {@link #getLayoutPosition()} if there are
7167         * pending adapter updates but a new layout pass has not happened yet.
7168         * <p>
7169         * RecyclerView does not handle any adapter updates until the next layout traversal. This
7170         * may create temporary inconsistencies between what user sees on the screen and what
7171         * adapter contents have. This inconsistency is not important since it will be less than
7172         * 16ms but it might be a problem if you want to use ViewHolder position to access the
7173         * adapter. Sometimes, you may need to get the exact adapter position to do
7174         * some actions in response to user events. In that case, you should use this method which
7175         * will calculate the Adapter position of the ViewHolder.
7176         * <p>
7177         * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
7178         * next layout pass, the return value of this method will be {@link #NO_POSITION}.
7179         *
7180         * @return The adapter position of the item if it still exists in the adapter.
7181         * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
7182         * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
7183         * layout pass or the ViewHolder has been removed from the RecyclerView.
7184         */
7185        public final int getAdapterPosition() {
7186            final ViewParent parent = itemView.getParent();
7187            if (!(parent instanceof RecyclerView)) {
7188                return -1;
7189            }
7190            final RecyclerView rv = (RecyclerView) parent;
7191            return rv.getAdapterPositionFor(this);
7192        }
7193
7194        /**
7195         * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
7196         * to perform animations.
7197         * <p>
7198         * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
7199         * adapter index in the previous layout.
7200         *
7201         * @return The previous adapter index of the Item represented by this ViewHolder or
7202         * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
7203         * complete).
7204         */
7205        public final int getOldPosition() {
7206            return mOldPosition;
7207        }
7208
7209        /**
7210         * Returns The itemId represented by this ViewHolder.
7211         *
7212         * @return The the item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
7213         * otherwise
7214         */
7215        public final long getItemId() {
7216            return mItemId;
7217        }
7218
7219        /**
7220         * @return The view type of this ViewHolder.
7221         */
7222        public final int getItemViewType() {
7223            return mItemViewType;
7224        }
7225
7226        boolean isScrap() {
7227            return mScrapContainer != null;
7228        }
7229
7230        void unScrap() {
7231            mScrapContainer.unscrapView(this);
7232        }
7233
7234        boolean wasReturnedFromScrap() {
7235            return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
7236        }
7237
7238        void clearReturnedFromScrapFlag() {
7239            mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
7240        }
7241
7242        void clearTmpDetachFlag() {
7243            mFlags = mFlags & ~FLAG_TMP_DETACHED;
7244        }
7245
7246        void stopIgnoring() {
7247            mFlags = mFlags & ~FLAG_IGNORE;
7248        }
7249
7250        void setScrapContainer(Recycler recycler) {
7251            mScrapContainer = recycler;
7252        }
7253
7254        boolean isInvalid() {
7255            return (mFlags & FLAG_INVALID) != 0;
7256        }
7257
7258        boolean needsUpdate() {
7259            return (mFlags & FLAG_UPDATE) != 0;
7260        }
7261
7262        boolean isChanged() {
7263            return (mFlags & FLAG_CHANGED) != 0;
7264        }
7265
7266        boolean isBound() {
7267            return (mFlags & FLAG_BOUND) != 0;
7268        }
7269
7270        boolean isRemoved() {
7271            return (mFlags & FLAG_REMOVED) != 0;
7272        }
7273
7274        boolean hasAnyOfTheFlags(int flags) {
7275            return (mFlags & flags) != 0;
7276        }
7277
7278        boolean isTmpDetached() {
7279            return (mFlags & FLAG_TMP_DETACHED) != 0;
7280        }
7281
7282        boolean isAdapterPositionUnknown() {
7283            return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0;
7284        }
7285
7286        void setFlags(int flags, int mask) {
7287            mFlags = (mFlags & ~mask) | (flags & mask);
7288        }
7289
7290        void addFlags(int flags) {
7291            mFlags |= flags;
7292        }
7293
7294        void resetInternal() {
7295            mFlags = 0;
7296            mPosition = NO_POSITION;
7297            mOldPosition = NO_POSITION;
7298            mItemId = NO_ID;
7299            mPreLayoutPosition = NO_POSITION;
7300            mIsRecyclableCount = 0;
7301            mShadowedHolder = null;
7302            mShadowingHolder = null;
7303        }
7304
7305        @Override
7306        public String toString() {
7307            final StringBuilder sb = new StringBuilder("ViewHolder{" +
7308                    Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId +
7309                    ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
7310            if (isScrap()) sb.append(" scrap");
7311            if (isInvalid()) sb.append(" invalid");
7312            if (!isBound()) sb.append(" unbound");
7313            if (needsUpdate()) sb.append(" update");
7314            if (isRemoved()) sb.append(" removed");
7315            if (shouldIgnore()) sb.append(" ignored");
7316            if (isChanged()) sb.append(" changed");
7317            if (isTmpDetached()) sb.append(" tmpDetached");
7318            if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
7319            if (!isAdapterPositionUnknown()) sb.append("undefined adapter position");
7320
7321            if (itemView.getParent() == null) sb.append(" no parent");
7322            sb.append("}");
7323            return sb.toString();
7324        }
7325
7326        /**
7327         * Informs the recycler whether this item can be recycled. Views which are not
7328         * recyclable will not be reused for other items until setIsRecyclable() is
7329         * later set to true. Calls to setIsRecyclable() should always be paired (one
7330         * call to setIsRecyclabe(false) should always be matched with a later call to
7331         * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
7332         * reference-counted.
7333         *
7334         * @param recyclable Whether this item is available to be recycled. Default value
7335         * is true.
7336         */
7337        public final void setIsRecyclable(boolean recyclable) {
7338            mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
7339            if (mIsRecyclableCount < 0) {
7340                mIsRecyclableCount = 0;
7341                if (DEBUG) {
7342                    throw new RuntimeException("isRecyclable decremented below 0: " +
7343                            "unmatched pair of setIsRecyable() calls for " + this);
7344                }
7345                Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: " +
7346                        "unmatched pair of setIsRecyable() calls for " + this);
7347            } else if (!recyclable && mIsRecyclableCount == 1) {
7348                mFlags |= FLAG_NOT_RECYCLABLE;
7349            } else if (recyclable && mIsRecyclableCount == 0) {
7350                mFlags &= ~FLAG_NOT_RECYCLABLE;
7351            }
7352            if (DEBUG) {
7353                Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
7354            }
7355        }
7356
7357        /**
7358         * @see {@link #setIsRecyclable(boolean)}
7359         *
7360         * @return true if this item is available to be recycled, false otherwise.
7361         */
7362        public final boolean isRecyclable() {
7363            return (mFlags & FLAG_NOT_RECYCLABLE) == 0 &&
7364                    !ViewCompat.hasTransientState(itemView);
7365        }
7366
7367        /**
7368         * Returns whether we have animations referring to this view holder or not.
7369         * This is similar to isRecyclable flag but does not check transient state.
7370         */
7371        private boolean shouldBeKeptAsChild() {
7372            return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
7373        }
7374
7375        /**
7376         * @return True if ViewHolder is not refenrenced by RecyclerView animations but has
7377         * transient state which will prevent it from being recycled.
7378         */
7379        private boolean doesTransientStatePreventRecycling() {
7380            return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView);
7381        }
7382    }
7383
7384    private int getAdapterPositionFor(ViewHolder viewHolder) {
7385        if (viewHolder.hasAnyOfTheFlags(
7386                ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
7387            return RecyclerView.NO_POSITION;
7388        }
7389        return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
7390    }
7391
7392    /**
7393     * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
7394     * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
7395     * to create their own subclass of this <code>LayoutParams</code> class
7396     * to store any additional required per-child view metadata about the layout.
7397     */
7398    public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
7399        ViewHolder mViewHolder;
7400        final Rect mDecorInsets = new Rect();
7401        boolean mInsetsDirty = true;
7402        // Flag is set to true if the view is bound while it is detached from RV.
7403        // In this case, we need to manually call invalidate after view is added to guarantee that
7404        // invalidation is populated through the View hierarchy
7405        boolean mPendingInvalidate = false;
7406
7407        public LayoutParams(Context c, AttributeSet attrs) {
7408            super(c, attrs);
7409        }
7410
7411        public LayoutParams(int width, int height) {
7412            super(width, height);
7413        }
7414
7415        public LayoutParams(MarginLayoutParams source) {
7416            super(source);
7417        }
7418
7419        public LayoutParams(ViewGroup.LayoutParams source) {
7420            super(source);
7421        }
7422
7423        public LayoutParams(LayoutParams source) {
7424            super((ViewGroup.LayoutParams) source);
7425        }
7426
7427        /**
7428         * Returns true if the view this LayoutParams is attached to needs to have its content
7429         * updated from the corresponding adapter.
7430         *
7431         * @return true if the view should have its content updated
7432         */
7433        public boolean viewNeedsUpdate() {
7434            return mViewHolder.needsUpdate();
7435        }
7436
7437        /**
7438         * Returns true if the view this LayoutParams is attached to is now representing
7439         * potentially invalid data. A LayoutManager should scrap/recycle it.
7440         *
7441         * @return true if the view is invalid
7442         */
7443        public boolean isViewInvalid() {
7444            return mViewHolder.isInvalid();
7445        }
7446
7447        /**
7448         * Returns true if the adapter data item corresponding to the view this LayoutParams
7449         * is attached to has been removed from the data set. A LayoutManager may choose to
7450         * treat it differently in order to animate its outgoing or disappearing state.
7451         *
7452         * @return true if the item the view corresponds to was removed from the data set
7453         */
7454        public boolean isItemRemoved() {
7455            return mViewHolder.isRemoved();
7456        }
7457
7458        /**
7459         * Returns true if the adapter data item corresponding to the view this LayoutParams
7460         * is attached to has been changed in the data set. A LayoutManager may choose to
7461         * treat it differently in order to animate its changing state.
7462         *
7463         * @return true if the item the view corresponds to was changed in the data set
7464         */
7465        public boolean isItemChanged() {
7466            return mViewHolder.isChanged();
7467        }
7468
7469        /**
7470         * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
7471         */
7472        public int getViewPosition() {
7473            return mViewHolder.getPosition();
7474        }
7475
7476        /**
7477         * Returns the adapter position that the view this LayoutParams is attached to corresponds
7478         * to as of latest layout calculation.
7479         *
7480         * @return the adapter position this view as of latest layout pass
7481         */
7482        public int getViewLayoutPosition() {
7483            return mViewHolder.getLayoutPosition();
7484        }
7485
7486        /**
7487         * Returns the up-to-date adapter position that the view this LayoutParams is attached to
7488         * corresponds to.
7489         *
7490         * @return the up-to-date adapter position this view. It may return
7491         * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
7492         * its up-to-date position cannot be calculated.
7493         */
7494        public int getViewAdapterPosition() {
7495            return mViewHolder.getAdapterPosition();
7496        }
7497    }
7498
7499    /**
7500     * Observer base class for watching changes to an {@link Adapter}.
7501     * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
7502     */
7503    public static abstract class AdapterDataObserver {
7504        public void onChanged() {
7505            // Do nothing
7506        }
7507
7508        public void onItemRangeChanged(int positionStart, int itemCount) {
7509            // do nothing
7510        }
7511
7512        public void onItemRangeInserted(int positionStart, int itemCount) {
7513            // do nothing
7514        }
7515
7516        public void onItemRangeRemoved(int positionStart, int itemCount) {
7517            // do nothing
7518        }
7519
7520        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
7521            // do nothing
7522        }
7523    }
7524
7525    /**
7526     * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
7527     * provides methods to trigger a programmatic scroll.</p>
7528     *
7529     * @see LinearSmoothScroller
7530     */
7531    public static abstract class SmoothScroller {
7532
7533        private int mTargetPosition = RecyclerView.NO_POSITION;
7534
7535        private RecyclerView mRecyclerView;
7536
7537        private LayoutManager mLayoutManager;
7538
7539        private boolean mPendingInitialRun;
7540
7541        private boolean mRunning;
7542
7543        private View mTargetView;
7544
7545        private final Action mRecyclingAction;
7546
7547        public SmoothScroller() {
7548            mRecyclingAction = new Action(0, 0);
7549        }
7550
7551        /**
7552         * Starts a smooth scroll for the given target position.
7553         * <p>In each animation step, {@link RecyclerView} will check
7554         * for the target view and call either
7555         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
7556         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
7557         * SmoothScroller is stopped.</p>
7558         *
7559         * <p>Note that if RecyclerView finds the target view, it will automatically stop the
7560         * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
7561         * stop calling SmoothScroller in each animation step.</p>
7562         */
7563        void start(RecyclerView recyclerView, LayoutManager layoutManager) {
7564            mRecyclerView = recyclerView;
7565            mLayoutManager = layoutManager;
7566            if (mTargetPosition == RecyclerView.NO_POSITION) {
7567                throw new IllegalArgumentException("Invalid target position");
7568            }
7569            mRecyclerView.mState.mTargetPosition = mTargetPosition;
7570            mRunning = true;
7571            mPendingInitialRun = true;
7572            mTargetView = findViewByPosition(getTargetPosition());
7573            onStart();
7574            mRecyclerView.mViewFlinger.postOnAnimation();
7575        }
7576
7577        public void setTargetPosition(int targetPosition) {
7578            mTargetPosition = targetPosition;
7579        }
7580
7581        /**
7582         * @return The LayoutManager to which this SmoothScroller is attached
7583         */
7584        public LayoutManager getLayoutManager() {
7585            return mLayoutManager;
7586        }
7587
7588        /**
7589         * Stops running the SmoothScroller in each animation callback. Note that this does not
7590         * cancel any existing {@link Action} updated by
7591         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
7592         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
7593         */
7594        final protected void stop() {
7595            if (!mRunning) {
7596                return;
7597            }
7598            onStop();
7599            mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
7600            mTargetView = null;
7601            mTargetPosition = RecyclerView.NO_POSITION;
7602            mPendingInitialRun = false;
7603            mRunning = false;
7604            // trigger a cleanup
7605            mLayoutManager.onSmoothScrollerStopped(this);
7606            // clear references to avoid any potential leak by a custom smooth scroller
7607            mLayoutManager = null;
7608            mRecyclerView = null;
7609        }
7610
7611        /**
7612         * Returns true if SmoothScroller has been started but has not received the first
7613         * animation
7614         * callback yet.
7615         *
7616         * @return True if this SmoothScroller is waiting to start
7617         */
7618        public boolean isPendingInitialRun() {
7619            return mPendingInitialRun;
7620        }
7621
7622
7623        /**
7624         * @return True if SmoothScroller is currently active
7625         */
7626        public boolean isRunning() {
7627            return mRunning;
7628        }
7629
7630        /**
7631         * Returns the adapter position of the target item
7632         *
7633         * @return Adapter position of the target item or
7634         * {@link RecyclerView#NO_POSITION} if no target view is set.
7635         */
7636        public int getTargetPosition() {
7637            return mTargetPosition;
7638        }
7639
7640        private void onAnimation(int dx, int dy) {
7641            if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION) {
7642                stop();
7643            }
7644            mPendingInitialRun = false;
7645            if (mTargetView != null) {
7646                // verify target position
7647                if (getChildPosition(mTargetView) == mTargetPosition) {
7648                    onTargetFound(mTargetView, mRecyclerView.mState, mRecyclingAction);
7649                    mRecyclingAction.runIfNecessary(mRecyclerView);
7650                    stop();
7651                } else {
7652                    Log.e(TAG, "Passed over target position while smooth scrolling.");
7653                    mTargetView = null;
7654                }
7655            }
7656            if (mRunning) {
7657                onSeekTargetStep(dx, dy, mRecyclerView.mState, mRecyclingAction);
7658                mRecyclingAction.runIfNecessary(mRecyclerView);
7659            }
7660        }
7661
7662        /**
7663         * @see RecyclerView#getChildLayoutPosition(android.view.View)
7664         */
7665        public int getChildPosition(View view) {
7666            return mRecyclerView.getChildLayoutPosition(view);
7667        }
7668
7669        /**
7670         * @see RecyclerView.LayoutManager#getChildCount()
7671         */
7672        public int getChildCount() {
7673            return mRecyclerView.mLayout.getChildCount();
7674        }
7675
7676        /**
7677         * @see RecyclerView.LayoutManager#findViewByPosition(int)
7678         */
7679        public View findViewByPosition(int position) {
7680            return mRecyclerView.mLayout.findViewByPosition(position);
7681        }
7682
7683        /**
7684         * @see RecyclerView#scrollToPosition(int)
7685         */
7686        public void instantScrollToPosition(int position) {
7687            mRecyclerView.scrollToPosition(position);
7688        }
7689
7690        protected void onChildAttachedToWindow(View child) {
7691            if (getChildPosition(child) == getTargetPosition()) {
7692                mTargetView = child;
7693                if (DEBUG) {
7694                    Log.d(TAG, "smooth scroll target view has been attached");
7695                }
7696            }
7697        }
7698
7699        /**
7700         * Normalizes the vector.
7701         * @param scrollVector The vector that points to the target scroll position
7702         */
7703        protected void normalize(PointF scrollVector) {
7704            final double magnitute = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y *
7705                    scrollVector.y);
7706            scrollVector.x /= magnitute;
7707            scrollVector.y /= magnitute;
7708        }
7709
7710        /**
7711         * Called when smooth scroll is started. This might be a good time to do setup.
7712         */
7713        abstract protected void onStart();
7714
7715        /**
7716         * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
7717         * @see #stop()
7718         */
7719        abstract protected void onStop();
7720
7721        /**
7722         * <p>RecyclerView will call this method each time it scrolls until it can find the target
7723         * position in the layout.</p>
7724         * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
7725         * provided {@link Action} to define the next scroll.</p>
7726         *
7727         * @param dx        Last scroll amount horizontally
7728         * @param dy        Last scroll amount verticaully
7729         * @param state     Transient state of RecyclerView
7730         * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
7731         *                  update this object.
7732         */
7733        abstract protected void onSeekTargetStep(int dx, int dy, State state, Action action);
7734
7735        /**
7736         * Called when the target position is laid out. This is the last callback SmoothScroller
7737         * will receive and it should update the provided {@link Action} to define the scroll
7738         * details towards the target view.
7739         * @param targetView    The view element which render the target position.
7740         * @param state         Transient state of RecyclerView
7741         * @param action        Action instance that you should update to define final scroll action
7742         *                      towards the targetView
7743         */
7744        abstract protected void onTargetFound(View targetView, State state, Action action);
7745
7746        /**
7747         * Holds information about a smooth scroll request by a {@link SmoothScroller}.
7748         */
7749        public static class Action {
7750
7751            public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
7752
7753            private int mDx;
7754
7755            private int mDy;
7756
7757            private int mDuration;
7758
7759            private Interpolator mInterpolator;
7760
7761            private boolean changed = false;
7762
7763            // we track this variable to inform custom implementer if they are updating the action
7764            // in every animation callback
7765            private int consecutiveUpdates = 0;
7766
7767            /**
7768             * @param dx Pixels to scroll horizontally
7769             * @param dy Pixels to scroll vertically
7770             */
7771            public Action(int dx, int dy) {
7772                this(dx, dy, UNDEFINED_DURATION, null);
7773            }
7774
7775            /**
7776             * @param dx       Pixels to scroll horizontally
7777             * @param dy       Pixels to scroll vertically
7778             * @param duration Duration of the animation in milliseconds
7779             */
7780            public Action(int dx, int dy, int duration) {
7781                this(dx, dy, duration, null);
7782            }
7783
7784            /**
7785             * @param dx           Pixels to scroll horizontally
7786             * @param dy           Pixels to scroll vertically
7787             * @param duration     Duration of the animation in milliseconds
7788             * @param interpolator Interpolator to be used when calculating scroll position in each
7789             *                     animation step
7790             */
7791            public Action(int dx, int dy, int duration, Interpolator interpolator) {
7792                mDx = dx;
7793                mDy = dy;
7794                mDuration = duration;
7795                mInterpolator = interpolator;
7796            }
7797            private void runIfNecessary(RecyclerView recyclerView) {
7798                if (changed) {
7799                    validate();
7800                    if (mInterpolator == null) {
7801                        if (mDuration == UNDEFINED_DURATION) {
7802                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
7803                        } else {
7804                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
7805                        }
7806                    } else {
7807                        recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration, mInterpolator);
7808                    }
7809                    consecutiveUpdates ++;
7810                    if (consecutiveUpdates > 10) {
7811                        // A new action is being set in every animation step. This looks like a bad
7812                        // implementation. Inform developer.
7813                        Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
7814                                + " you are not changing it unless necessary");
7815                    }
7816                    changed = false;
7817                } else {
7818                    consecutiveUpdates = 0;
7819                }
7820            }
7821
7822            private void validate() {
7823                if (mInterpolator != null && mDuration < 1) {
7824                    throw new IllegalStateException("If you provide an interpolator, you must"
7825                            + " set a positive duration");
7826                } else if (mDuration < 1) {
7827                    throw new IllegalStateException("Scroll duration must be a positive number");
7828                }
7829            }
7830
7831            public int getDx() {
7832                return mDx;
7833            }
7834
7835            public void setDx(int dx) {
7836                changed = true;
7837                mDx = dx;
7838            }
7839
7840            public int getDy() {
7841                return mDy;
7842            }
7843
7844            public void setDy(int dy) {
7845                changed = true;
7846                mDy = dy;
7847            }
7848
7849            public int getDuration() {
7850                return mDuration;
7851            }
7852
7853            public void setDuration(int duration) {
7854                changed = true;
7855                mDuration = duration;
7856            }
7857
7858            public Interpolator getInterpolator() {
7859                return mInterpolator;
7860            }
7861
7862            /**
7863             * Sets the interpolator to calculate scroll steps
7864             * @param interpolator The interpolator to use. If you specify an interpolator, you must
7865             *                     also set the duration.
7866             * @see #setDuration(int)
7867             */
7868            public void setInterpolator(Interpolator interpolator) {
7869                changed = true;
7870                mInterpolator = interpolator;
7871            }
7872
7873            /**
7874             * Updates the action with given parameters.
7875             * @param dx Pixels to scroll horizontally
7876             * @param dy Pixels to scroll vertically
7877             * @param duration Duration of the animation in milliseconds
7878             * @param interpolator Interpolator to be used when calculating scroll position in each
7879             *                     animation step
7880             */
7881            public void update(int dx, int dy, int duration, Interpolator interpolator) {
7882                mDx = dx;
7883                mDy = dy;
7884                mDuration = duration;
7885                mInterpolator = interpolator;
7886                changed = true;
7887            }
7888        }
7889    }
7890
7891    static class AdapterDataObservable extends Observable<AdapterDataObserver> {
7892        public boolean hasObservers() {
7893            return !mObservers.isEmpty();
7894        }
7895
7896        public void notifyChanged() {
7897            // since onChanged() is implemented by the app, it could do anything, including
7898            // removing itself from {@link mObservers} - and that could cause problems if
7899            // an iterator is used on the ArrayList {@link mObservers}.
7900            // to avoid such problems, just march thru the list in the reverse order.
7901            for (int i = mObservers.size() - 1; i >= 0; i--) {
7902                mObservers.get(i).onChanged();
7903            }
7904        }
7905
7906        public void notifyItemRangeChanged(int positionStart, int itemCount) {
7907            // since onItemRangeChanged() is implemented by the app, it could do anything, including
7908            // removing itself from {@link mObservers} - and that could cause problems if
7909            // an iterator is used on the ArrayList {@link mObservers}.
7910            // to avoid such problems, just march thru the list in the reverse order.
7911            for (int i = mObservers.size() - 1; i >= 0; i--) {
7912                mObservers.get(i).onItemRangeChanged(positionStart, itemCount);
7913            }
7914        }
7915
7916        public void notifyItemRangeInserted(int positionStart, int itemCount) {
7917            // since onItemRangeInserted() is implemented by the app, it could do anything,
7918            // including removing itself from {@link mObservers} - and that could cause problems if
7919            // an iterator is used on the ArrayList {@link mObservers}.
7920            // to avoid such problems, just march thru the list in the reverse order.
7921            for (int i = mObservers.size() - 1; i >= 0; i--) {
7922                mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
7923            }
7924        }
7925
7926        public void notifyItemRangeRemoved(int positionStart, int itemCount) {
7927            // since onItemRangeRemoved() is implemented by the app, it could do anything, including
7928            // removing itself from {@link mObservers} - and that could cause problems if
7929            // an iterator is used on the ArrayList {@link mObservers}.
7930            // to avoid such problems, just march thru the list in the reverse order.
7931            for (int i = mObservers.size() - 1; i >= 0; i--) {
7932                mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
7933            }
7934        }
7935
7936        public void notifyItemMoved(int fromPosition, int toPosition) {
7937            for (int i = mObservers.size() - 1; i >= 0; i--) {
7938                mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
7939            }
7940        }
7941    }
7942
7943    static class SavedState extends android.view.View.BaseSavedState {
7944
7945        Parcelable mLayoutState;
7946
7947        /**
7948         * called by CREATOR
7949         */
7950        SavedState(Parcel in) {
7951            super(in);
7952            mLayoutState = in.readParcelable(LayoutManager.class.getClassLoader());
7953        }
7954
7955        /**
7956         * Called by onSaveInstanceState
7957         */
7958        SavedState(Parcelable superState) {
7959            super(superState);
7960        }
7961
7962        @Override
7963        public void writeToParcel(Parcel dest, int flags) {
7964            super.writeToParcel(dest, flags);
7965            dest.writeParcelable(mLayoutState, 0);
7966        }
7967
7968        private void copyFrom(SavedState other) {
7969            mLayoutState = other.mLayoutState;
7970        }
7971
7972        public static final Parcelable.Creator<SavedState> CREATOR
7973                = new Parcelable.Creator<SavedState>() {
7974            @Override
7975            public SavedState createFromParcel(Parcel in) {
7976                return new SavedState(in);
7977            }
7978
7979            @Override
7980            public SavedState[] newArray(int size) {
7981                return new SavedState[size];
7982            }
7983        };
7984    }
7985    /**
7986     * <p>Contains useful information about the current RecyclerView state like target scroll
7987     * position or view focus. State object can also keep arbitrary data, identified by resource
7988     * ids.</p>
7989     * <p>Often times, RecyclerView components will need to pass information between each other.
7990     * To provide a well defined data bus between components, RecyclerView passes the same State
7991     * object to component callbacks and these components can use it to exchange data.</p>
7992     * <p>If you implement custom components, you can use State's put/get/remove methods to pass
7993     * data between your components without needing to manage their lifecycles.</p>
7994     */
7995    public static class State {
7996
7997        private int mTargetPosition = RecyclerView.NO_POSITION;
7998        ArrayMap<ViewHolder, ItemHolderInfo> mPreLayoutHolderMap =
7999                new ArrayMap<ViewHolder, ItemHolderInfo>();
8000        ArrayMap<ViewHolder, ItemHolderInfo> mPostLayoutHolderMap =
8001                new ArrayMap<ViewHolder, ItemHolderInfo>();
8002        // nullable
8003        ArrayMap<Long, ViewHolder> mOldChangedHolders = new ArrayMap<Long, ViewHolder>();
8004
8005        private SparseArray<Object> mData;
8006
8007        /**
8008         * Number of items adapter has.
8009         */
8010        int mItemCount = 0;
8011
8012        /**
8013         * Number of items adapter had in the previous layout.
8014         */
8015        private int mPreviousLayoutItemCount = 0;
8016
8017        /**
8018         * Number of items that were NOT laid out but has been deleted from the adapter after the
8019         * previous layout.
8020         */
8021        private int mDeletedInvisibleItemCountSincePreviousLayout = 0;
8022
8023        private boolean mStructureChanged = false;
8024
8025        private boolean mInPreLayout = false;
8026
8027        private boolean mRunSimpleAnimations = false;
8028
8029        private boolean mRunPredictiveAnimations = false;
8030
8031        State reset() {
8032            mTargetPosition = RecyclerView.NO_POSITION;
8033            if (mData != null) {
8034                mData.clear();
8035            }
8036            mItemCount = 0;
8037            mStructureChanged = false;
8038            return this;
8039        }
8040
8041        public boolean isPreLayout() {
8042            return mInPreLayout;
8043        }
8044
8045        /**
8046         * Returns whether RecyclerView will run predictive animations in this layout pass
8047         * or not.
8048         *
8049         * @return true if RecyclerView is calculating predictive animations to be run at the end
8050         *         of the layout pass.
8051         */
8052        public boolean willRunPredictiveAnimations() {
8053            return mRunPredictiveAnimations;
8054        }
8055
8056        /**
8057         * Returns whether RecyclerView will run simple animations in this layout pass
8058         * or not.
8059         *
8060         * @return true if RecyclerView is calculating simple animations to be run at the end of
8061         *         the layout pass.
8062         */
8063        public boolean willRunSimpleAnimations() {
8064            return mRunSimpleAnimations;
8065        }
8066
8067        /**
8068         * Removes the mapping from the specified id, if there was any.
8069         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
8070         *                   preserve cross functionality and avoid conflicts.
8071         */
8072        public void remove(int resourceId) {
8073            if (mData == null) {
8074                return;
8075            }
8076            mData.remove(resourceId);
8077        }
8078
8079        /**
8080         * Gets the Object mapped from the specified id, or <code>null</code>
8081         * if no such data exists.
8082         *
8083         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
8084         *                   to
8085         *                   preserve cross functionality and avoid conflicts.
8086         */
8087        public <T> T get(int resourceId) {
8088            if (mData == null) {
8089                return null;
8090            }
8091            return (T) mData.get(resourceId);
8092        }
8093
8094        /**
8095         * Adds a mapping from the specified id to the specified value, replacing the previous
8096         * mapping from the specified key if there was one.
8097         *
8098         * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
8099         *                   preserve cross functionality and avoid conflicts.
8100         * @param data       The data you want to associate with the resourceId.
8101         */
8102        public void put(int resourceId, Object data) {
8103            if (mData == null) {
8104                mData = new SparseArray<Object>();
8105            }
8106            mData.put(resourceId, data);
8107        }
8108
8109        /**
8110         * If scroll is triggered to make a certain item visible, this value will return the
8111         * adapter index of that item.
8112         * @return Adapter index of the target item or
8113         * {@link RecyclerView#NO_POSITION} if there is no target
8114         * position.
8115         */
8116        public int getTargetScrollPosition() {
8117            return mTargetPosition;
8118        }
8119
8120        /**
8121         * Returns if current scroll has a target position.
8122         * @return true if scroll is being triggered to make a certain position visible
8123         * @see #getTargetScrollPosition()
8124         */
8125        public boolean hasTargetScrollPosition() {
8126            return mTargetPosition != RecyclerView.NO_POSITION;
8127        }
8128
8129        /**
8130         * @return true if the structure of the data set has changed since the last call to
8131         *         onLayoutChildren, false otherwise
8132         */
8133        public boolean didStructureChange() {
8134            return mStructureChanged;
8135        }
8136
8137        /**
8138         * Returns the total number of items that can be laid out. Note that this number is not
8139         * necessarily equal to the number of items in the adapter, so you should always use this
8140         * number for your position calculations and never access the adapter directly.
8141         * <p>
8142         * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
8143         * data changes on existing Views. These calculations are used to decide which animations
8144         * should be run.
8145         * <p>
8146         * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
8147         * present the correct state to LayoutManager in pre-layout pass.
8148         * <p>
8149         * For example, a newly added item is not included in pre-layout item count because
8150         * pre-layout reflects the contents of the adapter before the item is added. Behind the
8151         * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
8152         * LayoutManager does not know about the new item's existence in pre-layout. The item will
8153         * be available in second layout pass and will be included in the item count. Similar
8154         * adjustments are made for moved and removed items as well.
8155         * <p>
8156         * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
8157         *
8158         * @return The number of items currently available
8159         * @see LayoutManager#getItemCount()
8160         */
8161        public int getItemCount() {
8162            return mInPreLayout ?
8163                    (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout) :
8164                    mItemCount;
8165        }
8166
8167        public void onViewRecycled(ViewHolder holder) {
8168            mPreLayoutHolderMap.remove(holder);
8169            mPostLayoutHolderMap.remove(holder);
8170            if (mOldChangedHolders != null) {
8171                removeFrom(mOldChangedHolders, holder);
8172            }
8173            // holder cannot be in new list.
8174        }
8175
8176        public void onViewIgnored(ViewHolder holder) {
8177            onViewRecycled(holder);
8178        }
8179
8180        private void removeFrom(ArrayMap<Long, ViewHolder> holderMap, ViewHolder holder) {
8181            for (int i = holderMap.size() - 1; i >= 0; i --) {
8182                if (holder == holderMap.valueAt(i)) {
8183                    holderMap.removeAt(i);
8184                    return;
8185                }
8186            }
8187        }
8188
8189        @Override
8190        public String toString() {
8191            return "State{" +
8192                    "mTargetPosition=" + mTargetPosition +
8193                    ", mPreLayoutHolderMap=" + mPreLayoutHolderMap +
8194                    ", mPostLayoutHolderMap=" + mPostLayoutHolderMap +
8195                    ", mData=" + mData +
8196                    ", mItemCount=" + mItemCount +
8197                    ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount +
8198                    ", mDeletedInvisibleItemCountSincePreviousLayout="
8199                    + mDeletedInvisibleItemCountSincePreviousLayout +
8200                    ", mStructureChanged=" + mStructureChanged +
8201                    ", mInPreLayout=" + mInPreLayout +
8202                    ", mRunSimpleAnimations=" + mRunSimpleAnimations +
8203                    ", mRunPredictiveAnimations=" + mRunPredictiveAnimations +
8204                    '}';
8205        }
8206    }
8207
8208    /**
8209     * Internal listener that manages items after animations finish. This is how items are
8210     * retained (not recycled) during animations, but allowed to be recycled afterwards.
8211     * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
8212     * method on the animator's listener when it is done animating any item.
8213     */
8214    private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
8215
8216        @Override
8217        public void onRemoveFinished(ViewHolder item) {
8218            item.setIsRecyclable(true);
8219            if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
8220                removeDetachedView(item.itemView, false);
8221            }
8222        }
8223
8224        @Override
8225        public void onAddFinished(ViewHolder item) {
8226            item.setIsRecyclable(true);
8227            if (!item.shouldBeKeptAsChild()) {
8228                removeAnimatingView(item.itemView);
8229            }
8230        }
8231
8232        @Override
8233        public void onMoveFinished(ViewHolder item) {
8234            item.setIsRecyclable(true);
8235            if (!item.shouldBeKeptAsChild()) {
8236                removeAnimatingView(item.itemView);
8237            }
8238        }
8239
8240        @Override
8241        public void onChangeFinished(ViewHolder item) {
8242            item.setIsRecyclable(true);
8243            /**
8244             * We check both shadowed and shadowing because a ViewHolder may get both roles at the
8245             * same time.
8246             *
8247             * Assume this flow:
8248             * item X is represented by VH_1. Then itemX changes, so we create VH_2 .
8249             * RV sets the following and calls item animator:
8250             * VH_1.shadowed = VH_2;
8251             * VH_1.mChanged = true;
8252             * VH_2.shadowing =VH_1;
8253             *
8254             * Then, before the first change finishes, item changes again so we create VH_3.
8255             * RV sets the following and calls item animator:
8256             * VH_2.shadowed = VH_3
8257             * VH_2.mChanged = true
8258             * VH_3.shadowing = VH_2
8259             *
8260             * Because VH_2 already has an animation, it will be cancelled. At this point VH_2 has
8261             * both shadowing and shadowed fields set. Shadowing information is obsolete now
8262             * because the first animation where VH_2 is newViewHolder is not valid anymore.
8263             * We ended up in this case because VH_2 played both roles. On the other hand,
8264             * we DO NOT want to clear its changed flag.
8265             *
8266             * If second change was simply reverting first change, we would find VH_1 in
8267             * {@link Recycler#getScrapViewForPosition(int, int, boolean)} and recycle it before
8268             * re-using
8269             */
8270            if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
8271                item.mShadowedHolder = null;
8272                item.setFlags(~ViewHolder.FLAG_CHANGED, item.mFlags);
8273            }
8274            // always null this because an OldViewHolder can never become NewViewHolder w/o being
8275            // recycled.
8276            item.mShadowingHolder = null;
8277            if (!item.shouldBeKeptAsChild()) {
8278                removeAnimatingView(item.itemView);
8279            }
8280        }
8281    };
8282
8283    /**
8284     * This class defines the animations that take place on items as changes are made
8285     * to the adapter.
8286     *
8287     * Subclasses of ItemAnimator can be used to implement custom animations for actions on
8288     * ViewHolder items. The RecyclerView will manage retaining these items while they
8289     * are being animated, but implementors must call the appropriate "Starting"
8290     * ({@link #dispatchRemoveStarting(ViewHolder)}, {@link #dispatchMoveStarting(ViewHolder)},
8291     * {@link #dispatchChangeStarting(ViewHolder, boolean)}, or
8292     * {@link #dispatchAddStarting(ViewHolder)})
8293     * and "Finished" ({@link #dispatchRemoveFinished(ViewHolder)},
8294     * {@link #dispatchMoveFinished(ViewHolder)},
8295     * {@link #dispatchChangeFinished(ViewHolder, boolean)},
8296     * or {@link #dispatchAddFinished(ViewHolder)}) methods when each item animation is
8297     * being started and ended.
8298     *
8299     * <p>By default, RecyclerView uses {@link DefaultItemAnimator}</p>
8300     *
8301     * @see #setItemAnimator(ItemAnimator)
8302     */
8303    public static abstract class ItemAnimator {
8304
8305        private ItemAnimatorListener mListener = null;
8306        private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
8307                new ArrayList<ItemAnimatorFinishedListener>();
8308
8309        private long mAddDuration = 120;
8310        private long mRemoveDuration = 120;
8311        private long mMoveDuration = 250;
8312        private long mChangeDuration = 250;
8313
8314        private boolean mSupportsChangeAnimations = true;
8315
8316        /**
8317         * Gets the current duration for which all move animations will run.
8318         *
8319         * @return The current move duration
8320         */
8321        public long getMoveDuration() {
8322            return mMoveDuration;
8323        }
8324
8325        /**
8326         * Sets the duration for which all move animations will run.
8327         *
8328         * @param moveDuration The move duration
8329         */
8330        public void setMoveDuration(long moveDuration) {
8331            mMoveDuration = moveDuration;
8332        }
8333
8334        /**
8335         * Gets the current duration for which all add animations will run.
8336         *
8337         * @return The current add duration
8338         */
8339        public long getAddDuration() {
8340            return mAddDuration;
8341        }
8342
8343        /**
8344         * Sets the duration for which all add animations will run.
8345         *
8346         * @param addDuration The add duration
8347         */
8348        public void setAddDuration(long addDuration) {
8349            mAddDuration = addDuration;
8350        }
8351
8352        /**
8353         * Gets the current duration for which all remove animations will run.
8354         *
8355         * @return The current remove duration
8356         */
8357        public long getRemoveDuration() {
8358            return mRemoveDuration;
8359        }
8360
8361        /**
8362         * Sets the duration for which all remove animations will run.
8363         *
8364         * @param removeDuration The remove duration
8365         */
8366        public void setRemoveDuration(long removeDuration) {
8367            mRemoveDuration = removeDuration;
8368        }
8369
8370        /**
8371         * Gets the current duration for which all change animations will run.
8372         *
8373         * @return The current change duration
8374         */
8375        public long getChangeDuration() {
8376            return mChangeDuration;
8377        }
8378
8379        /**
8380         * Sets the duration for which all change animations will run.
8381         *
8382         * @param changeDuration The change duration
8383         */
8384        public void setChangeDuration(long changeDuration) {
8385            mChangeDuration = changeDuration;
8386        }
8387
8388        /**
8389         * Returns whether this ItemAnimator supports animations of change events.
8390         *
8391         * @return true if change animations are supported, false otherwise
8392         */
8393        public boolean getSupportsChangeAnimations() {
8394            return mSupportsChangeAnimations;
8395        }
8396
8397        /**
8398         * Sets whether this ItemAnimator supports animations of item change events.
8399         * If you set this property to false, actions on the data set which change the
8400         * contents of items will not be animated. What those animations are is left
8401         * up to the discretion of the ItemAnimator subclass, in its
8402         * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} implementation.
8403         * The value of this property is true by default.
8404         *
8405         * @see Adapter#notifyItemChanged(int)
8406         * @see Adapter#notifyItemRangeChanged(int, int)
8407         *
8408         * @param supportsChangeAnimations true if change animations are supported by
8409         * this ItemAnimator, false otherwise. If the property is false, the ItemAnimator
8410         * will not receive a call to
8411         * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} when changes occur.
8412         */
8413        public void setSupportsChangeAnimations(boolean supportsChangeAnimations) {
8414            mSupportsChangeAnimations = supportsChangeAnimations;
8415        }
8416
8417        /**
8418         * Internal only:
8419         * Sets the listener that must be called when the animator is finished
8420         * animating the item (or immediately if no animation happens). This is set
8421         * internally and is not intended to be set by external code.
8422         *
8423         * @param listener The listener that must be called.
8424         */
8425        void setListener(ItemAnimatorListener listener) {
8426            mListener = listener;
8427        }
8428
8429        /**
8430         * Called when there are pending animations waiting to be started. This state
8431         * is governed by the return values from {@link #animateAdd(ViewHolder) animateAdd()},
8432         * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()}, and
8433         * {@link #animateRemove(ViewHolder) animateRemove()}, which inform the
8434         * RecyclerView that the ItemAnimator wants to be called later to start the
8435         * associated animations. runPendingAnimations() will be scheduled to be run
8436         * on the next frame.
8437         */
8438        abstract public void runPendingAnimations();
8439
8440        /**
8441         * Called when an item is removed from the RecyclerView. Implementors can choose
8442         * whether and how to animate that change, but must always call
8443         * {@link #dispatchRemoveFinished(ViewHolder)} when done, either
8444         * immediately (if no animation will occur) or after the animation actually finishes.
8445         * The return value indicates whether an animation has been set up and whether the
8446         * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
8447         * next opportunity. This mechanism allows ItemAnimator to set up individual animations
8448         * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
8449         * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
8450         * {@link #animateRemove(ViewHolder) animateRemove()}, and
8451         * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
8452         * then start the animations together in the later call to {@link #runPendingAnimations()}.
8453         *
8454         * <p>This method may also be called for disappearing items which continue to exist in the
8455         * RecyclerView, but for which the system does not have enough information to animate
8456         * them out of view. In that case, the default animation for removing items is run
8457         * on those items as well.</p>
8458         *
8459         * @param holder The item that is being removed.
8460         * @return true if a later call to {@link #runPendingAnimations()} is requested,
8461         * false otherwise.
8462         */
8463        abstract public boolean animateRemove(ViewHolder holder);
8464
8465        /**
8466         * Called when an item is added to the RecyclerView. Implementors can choose
8467         * whether and how to animate that change, but must always call
8468         * {@link #dispatchAddFinished(ViewHolder)} when done, either
8469         * immediately (if no animation will occur) or after the animation actually finishes.
8470         * The return value indicates whether an animation has been set up and whether the
8471         * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
8472         * next opportunity. This mechanism allows ItemAnimator to set up individual animations
8473         * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
8474         * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
8475         * {@link #animateRemove(ViewHolder) animateRemove()}, and
8476         * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
8477         * then start the animations together in the later call to {@link #runPendingAnimations()}.
8478         *
8479         * <p>This method may also be called for appearing items which were already in the
8480         * RecyclerView, but for which the system does not have enough information to animate
8481         * them into view. In that case, the default animation for adding items is run
8482         * on those items as well.</p>
8483         *
8484         * @param holder The item that is being added.
8485         * @return true if a later call to {@link #runPendingAnimations()} is requested,
8486         * false otherwise.
8487         */
8488        abstract public boolean animateAdd(ViewHolder holder);
8489
8490        /**
8491         * Called when an item is moved in the RecyclerView. Implementors can choose
8492         * whether and how to animate that change, but must always call
8493         * {@link #dispatchMoveFinished(ViewHolder)} when done, either
8494         * immediately (if no animation will occur) or after the animation actually finishes.
8495         * The return value indicates whether an animation has been set up and whether the
8496         * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
8497         * next opportunity. This mechanism allows ItemAnimator to set up individual animations
8498         * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
8499         * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
8500         * {@link #animateRemove(ViewHolder) animateRemove()}, and
8501         * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
8502         * then start the animations together in the later call to {@link #runPendingAnimations()}.
8503         *
8504         * @param holder The item that is being moved.
8505         * @return true if a later call to {@link #runPendingAnimations()} is requested,
8506         * false otherwise.
8507         */
8508        abstract public boolean animateMove(ViewHolder holder, int fromX, int fromY,
8509                int toX, int toY);
8510
8511        /**
8512         * Called when an item is changed in the RecyclerView, as indicated by a call to
8513         * {@link Adapter#notifyItemChanged(int)} or
8514         * {@link Adapter#notifyItemRangeChanged(int, int)}.
8515         * <p>
8516         * Implementers can choose whether and how to animate changes, but must always call
8517         * {@link #dispatchChangeFinished(ViewHolder, boolean)} for each non-null ViewHolder,
8518         * either immediately (if no animation will occur) or after the animation actually finishes.
8519         * The return value indicates whether an animation has been set up and whether the
8520         * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
8521         * next opportunity. This mechanism allows ItemAnimator to set up individual animations
8522         * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
8523         * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
8524         * {@link #animateRemove(ViewHolder) animateRemove()}, and
8525         * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
8526         * then start the animations together in the later call to {@link #runPendingAnimations()}.
8527         *
8528         * @param oldHolder The original item that changed.
8529         * @param newHolder The new item that was created with the changed content. Might be null
8530         * @param fromLeft  Left of the old view holder
8531         * @param fromTop   Top of the old view holder
8532         * @param toLeft    Left of the new view holder
8533         * @param toTop     Top of the new view holder
8534         * @return true if a later call to {@link #runPendingAnimations()} is requested,
8535         * false otherwise.
8536         */
8537        abstract public boolean animateChange(ViewHolder oldHolder,
8538                ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop);
8539
8540
8541        /**
8542         * Method to be called by subclasses when a remove animation is done.
8543         *
8544         * @param item The item which has been removed
8545         */
8546        public final void dispatchRemoveFinished(ViewHolder item) {
8547            onRemoveFinished(item);
8548            if (mListener != null) {
8549                mListener.onRemoveFinished(item);
8550            }
8551        }
8552
8553        /**
8554         * Method to be called by subclasses when a move animation is done.
8555         *
8556         * @param item The item which has been moved
8557         */
8558        public final void dispatchMoveFinished(ViewHolder item) {
8559            onMoveFinished(item);
8560            if (mListener != null) {
8561                mListener.onMoveFinished(item);
8562            }
8563        }
8564
8565        /**
8566         * Method to be called by subclasses when an add animation is done.
8567         *
8568         * @param item The item which has been added
8569         */
8570        public final void dispatchAddFinished(ViewHolder item) {
8571            onAddFinished(item);
8572            if (mListener != null) {
8573                mListener.onAddFinished(item);
8574            }
8575        }
8576
8577        /**
8578         * Method to be called by subclasses when a change animation is done.
8579         *
8580         * @see #animateChange(ViewHolder, ViewHolder, int, int, int, int)
8581         * @param item The item which has been changed (this method must be called for
8582         * each non-null ViewHolder passed into
8583         * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}).
8584         * @param oldItem true if this is the old item that was changed, false if
8585         * it is the new item that replaced the old item.
8586         */
8587        public final void dispatchChangeFinished(ViewHolder item, boolean oldItem) {
8588            onChangeFinished(item, oldItem);
8589            if (mListener != null) {
8590                mListener.onChangeFinished(item);
8591            }
8592        }
8593
8594        /**
8595         * Method to be called by subclasses when a remove animation is being started.
8596         *
8597         * @param item The item being removed
8598         */
8599        public final void dispatchRemoveStarting(ViewHolder item) {
8600            onRemoveStarting(item);
8601        }
8602
8603        /**
8604         * Method to be called by subclasses when a move animation is being started.
8605         *
8606         * @param item The item being moved
8607         */
8608        public final void dispatchMoveStarting(ViewHolder item) {
8609            onMoveStarting(item);
8610        }
8611
8612        /**
8613         * Method to be called by subclasses when an add animation is being started.
8614         *
8615         * @param item The item being added
8616         */
8617        public final void dispatchAddStarting(ViewHolder item) {
8618            onAddStarting(item);
8619        }
8620
8621        /**
8622         * Method to be called by subclasses when a change animation is being started.
8623         *
8624         * @param item The item which has been changed (this method must be called for
8625         * each non-null ViewHolder passed into
8626         * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}).
8627         * @param oldItem true if this is the old item that was changed, false if
8628         * it is the new item that replaced the old item.
8629         */
8630        public final void dispatchChangeStarting(ViewHolder item, boolean oldItem) {
8631            onChangeStarting(item, oldItem);
8632        }
8633
8634        /**
8635         * Method called when an animation on a view should be ended immediately.
8636         * This could happen when other events, like scrolling, occur, so that
8637         * animating views can be quickly put into their proper end locations.
8638         * Implementations should ensure that any animations running on the item
8639         * are canceled and affected properties are set to their end values.
8640         * Also, appropriate dispatch methods (e.g., {@link #dispatchAddFinished(ViewHolder)}
8641         * should be called since the animations are effectively done when this
8642         * method is called.
8643         *
8644         * @param item The item for which an animation should be stopped.
8645         */
8646        abstract public void endAnimation(ViewHolder item);
8647
8648        /**
8649         * Method called when all item animations should be ended immediately.
8650         * This could happen when other events, like scrolling, occur, so that
8651         * animating views can be quickly put into their proper end locations.
8652         * Implementations should ensure that any animations running on any items
8653         * are canceled and affected properties are set to their end values.
8654         * Also, appropriate dispatch methods (e.g., {@link #dispatchAddFinished(ViewHolder)}
8655         * should be called since the animations are effectively done when this
8656         * method is called.
8657         */
8658        abstract public void endAnimations();
8659
8660        /**
8661         * Method which returns whether there are any item animations currently running.
8662         * This method can be used to determine whether to delay other actions until
8663         * animations end.
8664         *
8665         * @return true if there are any item animations currently running, false otherwise.
8666         */
8667        abstract public boolean isRunning();
8668
8669        /**
8670         * Like {@link #isRunning()}, this method returns whether there are any item
8671         * animations currently running. Addtionally, the listener passed in will be called
8672         * when there are no item animations running, either immediately (before the method
8673         * returns) if no animations are currently running, or when the currently running
8674         * animations are {@link #dispatchAnimationsFinished() finished}.
8675         *
8676         * <p>Note that the listener is transient - it is either called immediately and not
8677         * stored at all, or stored only until it is called when running animations
8678         * are finished sometime later.</p>
8679         *
8680         * @param listener A listener to be called immediately if no animations are running
8681         * or later when currently-running animations have finished. A null listener is
8682         * equivalent to calling {@link #isRunning()}.
8683         * @return true if there are any item animations currently running, false otherwise.
8684         */
8685        public final boolean isRunning(ItemAnimatorFinishedListener listener) {
8686            boolean running = isRunning();
8687            if (listener != null) {
8688                if (!running) {
8689                    listener.onAnimationsFinished();
8690                } else {
8691                    mFinishedListeners.add(listener);
8692                }
8693            }
8694            return running;
8695        }
8696
8697        /**
8698         * The interface to be implemented by listeners to animation events from this
8699         * ItemAnimator. This is used internally and is not intended for developers to
8700         * create directly.
8701         */
8702        interface ItemAnimatorListener {
8703            void onRemoveFinished(ViewHolder item);
8704            void onAddFinished(ViewHolder item);
8705            void onMoveFinished(ViewHolder item);
8706            void onChangeFinished(ViewHolder item);
8707        }
8708
8709        /**
8710         * This method should be called by ItemAnimator implementations to notify
8711         * any listeners that all pending and active item animations are finished.
8712         */
8713        public final void dispatchAnimationsFinished() {
8714            final int count = mFinishedListeners.size();
8715            for (int i = 0; i < count; ++i) {
8716                mFinishedListeners.get(i).onAnimationsFinished();
8717            }
8718            mFinishedListeners.clear();
8719        }
8720
8721        /**
8722         * This interface is used to inform listeners when all pending or running animations
8723         * in an ItemAnimator are finished. This can be used, for example, to delay an action
8724         * in a data set until currently-running animations are complete.
8725         *
8726         * @see #isRunning(ItemAnimatorFinishedListener)
8727         */
8728        public interface ItemAnimatorFinishedListener {
8729            void onAnimationsFinished();
8730        }
8731
8732        /**
8733         * Called when a remove animation is being started on the given ViewHolder.
8734         * The default implementation does nothing. Subclasses may wish to override
8735         * this method to handle any ViewHolder-specific operations linked to animation
8736         * lifecycles.
8737         *
8738         * @param item The ViewHolder being animated.
8739         */
8740        public void onRemoveStarting(ViewHolder item) {}
8741
8742        /**
8743         * Called when a remove animation has ended on the given ViewHolder.
8744         * The default implementation does nothing. Subclasses may wish to override
8745         * this method to handle any ViewHolder-specific operations linked to animation
8746         * lifecycles.
8747         *
8748         * @param item The ViewHolder being animated.
8749         */
8750        public void onRemoveFinished(ViewHolder item) {}
8751
8752        /**
8753         * Called when an add animation is being started on the given ViewHolder.
8754         * The default implementation does nothing. Subclasses may wish to override
8755         * this method to handle any ViewHolder-specific operations linked to animation
8756         * lifecycles.
8757         *
8758         * @param item The ViewHolder being animated.
8759         */
8760        public void onAddStarting(ViewHolder item) {}
8761
8762        /**
8763         * Called when an add animation has ended on the given ViewHolder.
8764         * The default implementation does nothing. Subclasses may wish to override
8765         * this method to handle any ViewHolder-specific operations linked to animation
8766         * lifecycles.
8767         *
8768         * @param item The ViewHolder being animated.
8769         */
8770        public void onAddFinished(ViewHolder item) {}
8771
8772        /**
8773         * Called when a move animation is being started on the given ViewHolder.
8774         * The default implementation does nothing. Subclasses may wish to override
8775         * this method to handle any ViewHolder-specific operations linked to animation
8776         * lifecycles.
8777         *
8778         * @param item The ViewHolder being animated.
8779         */
8780        public void onMoveStarting(ViewHolder item) {}
8781
8782        /**
8783         * Called when a move animation has ended on the given ViewHolder.
8784         * The default implementation does nothing. Subclasses may wish to override
8785         * this method to handle any ViewHolder-specific operations linked to animation
8786         * lifecycles.
8787         *
8788         * @param item The ViewHolder being animated.
8789         */
8790        public void onMoveFinished(ViewHolder item) {}
8791
8792        /**
8793         * Called when a change animation is being started on the given ViewHolder.
8794         * The default implementation does nothing. Subclasses may wish to override
8795         * this method to handle any ViewHolder-specific operations linked to animation
8796         * lifecycles.
8797         *
8798         * @param item The ViewHolder being animated.
8799         * @param oldItem true if this is the old item that was changed, false if
8800         * it is the new item that replaced the old item.
8801         */
8802        public void onChangeStarting(ViewHolder item, boolean oldItem) {}
8803
8804        /**
8805         * Called when a change animation has ended on the given ViewHolder.
8806         * The default implementation does nothing. Subclasses may wish to override
8807         * this method to handle any ViewHolder-specific operations linked to animation
8808         * lifecycles.
8809         *
8810         * @param item The ViewHolder being animated.
8811         * @param oldItem true if this is the old item that was changed, false if
8812         * it is the new item that replaced the old item.
8813         */
8814        public void onChangeFinished(ViewHolder item, boolean oldItem) {}
8815
8816    }
8817
8818    /**
8819     * Internal data structure that holds information about an item's bounds.
8820     * This information is used in calculating item animations.
8821     */
8822    private static class ItemHolderInfo {
8823        ViewHolder holder;
8824        int left, top, right, bottom;
8825
8826        ItemHolderInfo(ViewHolder holder, int left, int top, int right, int bottom) {
8827            this.holder = holder;
8828            this.left = left;
8829            this.top = top;
8830            this.right = right;
8831            this.bottom = bottom;
8832        }
8833    }
8834}
8835