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