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