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