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