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