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.Matrix;
25 import android.graphics.PointF;
26 import android.graphics.Rect;
27 import android.graphics.RectF;
28 import android.os.Build;
29 import android.os.Bundle;
30 import android.os.Parcel;
31 import android.os.Parcelable;
32 import android.os.SystemClock;
33 import android.support.annotation.CallSuper;
34 import android.support.annotation.IntDef;
35 import android.support.annotation.NonNull;
36 import android.support.annotation.Nullable;
37 import android.support.annotation.VisibleForTesting;
38 import android.support.v4.os.ParcelableCompat;
39 import android.support.v4.os.ParcelableCompatCreatorCallbacks;
40 import android.support.v4.os.TraceCompat;
41 import android.support.v4.view.AbsSavedState;
42 import android.support.v4.view.InputDeviceCompat;
43 import android.support.v4.view.MotionEventCompat;
44 import android.support.v4.view.NestedScrollingChild;
45 import android.support.v4.view.NestedScrollingChildHelper;
46 import android.support.v4.view.ScrollingView;
47 import android.support.v4.view.VelocityTrackerCompat;
48 import android.support.v4.view.ViewCompat;
49 import android.support.v4.view.ViewConfigurationCompat;
50 import android.support.v4.view.accessibility.AccessibilityEventCompat;
51 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
52 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
53 import android.support.v4.widget.EdgeEffectCompat;
54 import android.support.v4.widget.ScrollerCompat;
55 import android.support.v7.recyclerview.R;
56 import android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
57 import android.util.AttributeSet;
58 import android.util.Log;
59 import android.util.SparseArray;
60 import android.util.SparseIntArray;
61 import android.util.TypedValue;
62 import android.view.FocusFinder;
63 import android.view.MotionEvent;
64 import android.view.VelocityTracker;
65 import android.view.View;
66 import android.view.ViewConfiguration;
67 import android.view.ViewGroup;
68 import android.view.ViewParent;
69 import android.view.accessibility.AccessibilityEvent;
70 import android.view.accessibility.AccessibilityManager;
71 import android.view.animation.Interpolator;
72 
73 import java.lang.annotation.Retention;
74 import java.lang.annotation.RetentionPolicy;
75 import java.lang.reflect.Constructor;
76 import java.lang.reflect.InvocationTargetException;
77 import java.util.ArrayList;
78 import java.util.Collections;
79 import java.util.List;
80 
81 import static android.support.v7.widget.AdapterHelper.Callback;
82 import static android.support.v7.widget.AdapterHelper.UpdateOp;
83 
84 /**
85  * A flexible view for providing a limited window into a large data set.
86  *
87  * <h3>Glossary of terms:</h3>
88  *
89  * <ul>
90  *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
91  *     that represent items in a data set.</li>
92  *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
93  *     <li><em>Index:</em> The index of an attached child view as used in a call to
94  *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
95  *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
96  *     to a <em>position</em> within the adapter.</li>
97  *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
98  *     position may be placed in a cache for later reuse to display the same type of data again
99  *     later. This can drastically improve performance by skipping initial layout inflation
100  *     or construction.</li>
101  *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
102  *     state during layout. Scrap views may be reused without becoming fully detached
103  *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
104  *     by the adapter if the view was considered <em>dirty</em>.</li>
105  *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
106  *     being displayed.</li>
107  * </ul>
108  *
109  * <h4>Positions in RecyclerView:</h4>
110  * <p>
111  * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
112  * {@link LayoutManager} to be able to detect data set changes in batches during a layout
113  * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
114  * It also helps with performance because all view bindings happen at the same time and unnecessary
115  * bindings are avoided.
116  * <p>
117  * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
118  * <ul>
119  *     <li>layout position: Position of an item in the latest layout calculation. This is the
120  *     position from the LayoutManager's perspective.</li>
121  *     <li>adapter position: Position of an item in the adapter. This is the position from
122  *     the Adapter's perspective.</li>
123  * </ul>
124  * <p>
125  * These two positions are the same except the time between dispatching <code>adapter.notify*
126  * </code> events and calculating the updated layout.
127  * <p>
128  * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
129  * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
130  * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
131  * last layout calculation. You can rely on these positions to be consistent with what user is
132  * currently seeing on the screen. For example, if you have a list of items on the screen and user
133  * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
134  * is seeing.
135  * <p>
136  * The other set of position related methods are in the form of
137  * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
138  * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
139  * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
140  * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
141  * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
142  * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
143  * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
144  * <code>null</code> results from these methods.
145  * <p>
146  * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
147  * writing an {@link Adapter}, you probably want to use adapter positions.
148  *
149  * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_layoutManager
150  */
151 public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
152 
153     private static final String TAG = "RecyclerView";
154 
155     private static final boolean DEBUG = false;
156 
157     private static final int[]  NESTED_SCROLLING_ATTRS
158             = {16843830 /* android.R.attr.nestedScrollingEnabled */};
159 
160     private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
161 
162     /**
163      * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
164      * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
165      * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
166      * recursively traverses itemView and invalidates display list for each ViewGroup that matches
167      * this criteria.
168      */
169     private static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
170             || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
171     /**
172      * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
173      * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
174      * 0 when mode is unspecified.
175      */
176     static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
177 
178     static final boolean DISPATCH_TEMP_DETACH = false;
179     public static final int HORIZONTAL = 0;
180     public static final int VERTICAL = 1;
181 
182     public static final int NO_POSITION = -1;
183     public static final long NO_ID = -1;
184     public static final int INVALID_TYPE = -1;
185 
186     /**
187      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
188      * that the RecyclerView should use the standard touch slop for smooth,
189      * continuous scrolling.
190      */
191     public static final int TOUCH_SLOP_DEFAULT = 0;
192 
193     /**
194      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
195      * that the RecyclerView should use the standard touch slop for scrolling
196      * widgets that snap to a page or other coarse-grained barrier.
197      */
198     public static final int TOUCH_SLOP_PAGING = 1;
199 
200     private static final int MAX_SCROLL_DURATION = 2000;
201 
202     /**
203      * RecyclerView is calculating a scroll.
204      * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
205      * it. Try to avoid using EditText, focusable views or handle them with care.
206      */
207     private static final String TRACE_SCROLL_TAG = "RV Scroll";
208 
209     /**
210      * OnLayout has been called by the View system.
211      * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
212      * update themselves directly. This will cause a full re-layout but when it happens via the
213      * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
214      */
215     private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
216 
217     /**
218      * NotifyDataSetChanged or equal has been called.
219      * If this is taking a long time, try sending granular notify adapter changes instead of just
220      * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
221      * might help.
222      */
223     private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
224 
225     /**
226      * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
227      * If this is taking a long time, you may have dispatched too many Adapter updates causing too
228      * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
229      * methods.
230      */
231     private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
232 
233     /**
234      * RecyclerView is rebinding a View.
235      * If this is taking a lot of time, consider optimizing your layout or make sure you are not
236      * doing extra operations in onBindViewHolder call.
237      */
238     private static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
239 
240     /**
241      * RecyclerView is creating a new View.
242      * If too many of these present in Systrace:
243      * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
244      * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
245      * > Adapter#onFailedToRecycleView(ViewHolder)})
246      *
247      * - There might be too many item view types.
248      * > Try merging them
249      *
250      * - There might be too many itemChange animations and not enough space in RecyclerPool.
251      * >Try increasing your pool size and item cache size.
252      */
253     private static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
254     private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
255             new Class[]{Context.class, AttributeSet.class, int.class, int.class};
256 
257     private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
258 
259     final Recycler mRecycler = new Recycler();
260 
261     private SavedState mPendingSavedState;
262 
263     /**
264      * Handles adapter updates
265      */
266     AdapterHelper mAdapterHelper;
267 
268     /**
269      * Handles abstraction between LayoutManager children and RecyclerView children
270      */
271     ChildHelper mChildHelper;
272 
273     /**
274      * Keeps data about views to be used for animations
275      */
276     final ViewInfoStore mViewInfoStore = new ViewInfoStore();
277 
278     /**
279      * Prior to L, there is no way to query this variable which is why we override the setter and
280      * track it here.
281      */
282     private boolean mClipToPadding;
283 
284     /**
285      * Note: this Runnable is only ever posted if:
286      * 1) We've been through first layout
287      * 2) We know we have a fixed size (mHasFixedSize)
288      * 3) We're attached
289      */
290     private final Runnable mUpdateChildViewsRunnable = new Runnable() {
291         public void run() {
292             if (!mFirstLayoutComplete || isLayoutRequested()) {
293                 // a layout request will happen, we should not do layout here.
294                 return;
295             }
296             if (!mIsAttached) {
297                 requestLayout();
298                 // if we are not attached yet, mark us as requiring layout and skip
299                 return;
300             }
301             if (mLayoutFrozen) {
302                 mLayoutRequestEaten = true;
303                 return; //we'll process updates when ice age ends.
304             }
305             consumePendingUpdateOperations();
306         }
307     };
308 
309     private final Rect mTempRect = new Rect();
310     private final Rect mTempRect2 = new Rect();
311     private final RectF mTempRectF = new RectF();
312     private Adapter mAdapter;
313     @VisibleForTesting LayoutManager mLayout;
314     private RecyclerListener mRecyclerListener;
315     private final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
316     private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
317             new ArrayList<>();
318     private OnItemTouchListener mActiveOnItemTouchListener;
319     private boolean mIsAttached;
320     private boolean mHasFixedSize;
321     @VisibleForTesting boolean mFirstLayoutComplete;
322 
323     // Counting lock to control whether we should ignore requestLayout calls from children or not.
324     private int mEatRequestLayout = 0;
325 
326     private boolean mLayoutRequestEaten;
327     private boolean mLayoutFrozen;
328     private boolean mIgnoreMotionEventTillDown;
329 
330     // binary OR of change events that were eaten during a layout or scroll.
331     private int mEatenAccessibilityChangeFlags;
332     private boolean mAdapterUpdateDuringMeasure;
333     private final boolean mPostUpdatesOnAnimation;
334     private final AccessibilityManager mAccessibilityManager;
335     private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
336 
337     /**
338      * Set to true when an adapter data set changed notification is received.
339      * In that case, we cannot run any animations since we don't know what happened.
340      */
341     private boolean mDataSetHasChangedAfterLayout = false;
342 
343     /**
344      * This variable is incremented during a dispatchLayout and/or scroll.
345      * Some methods should not be called during these periods (e.g. adapter data change).
346      * Doing so will create hard to find bugs so we better check it and throw an exception.
347      *
348      * @see #assertInLayoutOrScroll(String)
349      * @see #assertNotInLayoutOrScroll(String)
350      */
351     private int mLayoutOrScrollCounter = 0;
352 
353     private EdgeEffectCompat mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
354 
355     ItemAnimator mItemAnimator = new DefaultItemAnimator();
356 
357     private static final int INVALID_POINTER = -1;
358 
359     /**
360      * The RecyclerView is not currently scrolling.
361      * @see #getScrollState()
362      */
363     public static final int SCROLL_STATE_IDLE = 0;
364 
365     /**
366      * The RecyclerView is currently being dragged by outside input such as user touch input.
367      * @see #getScrollState()
368      */
369     public static final int SCROLL_STATE_DRAGGING = 1;
370 
371     /**
372      * The RecyclerView is currently animating to a final position while not under
373      * outside control.
374      * @see #getScrollState()
375      */
376     public static final int SCROLL_STATE_SETTLING = 2;
377 
378     // Touch/scrolling handling
379 
380     private int mScrollState = SCROLL_STATE_IDLE;
381     private int mScrollPointerId = INVALID_POINTER;
382     private VelocityTracker mVelocityTracker;
383     private int mInitialTouchX;
384     private int mInitialTouchY;
385     private int mLastTouchX;
386     private int mLastTouchY;
387     private int mTouchSlop;
388     private final int mMinFlingVelocity;
389     private final int mMaxFlingVelocity;
390     // This value is used when handling generic motion events.
391     private float mScrollFactor = Float.MIN_VALUE;
392     private boolean mPreserveFocusAfterLayout = true;
393 
394     private final ViewFlinger mViewFlinger = new ViewFlinger();
395 
396     final State mState = new State();
397 
398     private OnScrollListener mScrollListener;
399     private List<OnScrollListener> mScrollListeners;
400 
401     // For use in item animations
402     boolean mItemsAddedOrRemoved = false;
403     boolean mItemsChanged = false;
404     private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
405             new ItemAnimatorRestoreListener();
406     private boolean mPostedAnimatorRunner = false;
407     private RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
408     private ChildDrawingOrderCallback mChildDrawingOrderCallback;
409 
410     // simple array to keep min and max child position during a layout calculation
411     // preserved not to create a new one in each layout pass
412     private final int[] mMinMaxLayoutPositions = new int[2];
413 
414     private NestedScrollingChildHelper mScrollingChildHelper;
415     private final int[] mScrollOffset = new int[2];
416     private final int[] mScrollConsumed = new int[2];
417     private final int[] mNestedOffsets = new int[2];
418 
419     private Runnable mItemAnimatorRunner = new Runnable() {
420         @Override
421         public void run() {
422             if (mItemAnimator != null) {
423                 mItemAnimator.runPendingAnimations();
424             }
425             mPostedAnimatorRunner = false;
426         }
427     };
428 
429     private static final Interpolator sQuinticInterpolator = new Interpolator() {
430         public float getInterpolation(float t) {
431             t -= 1.0f;
432             return t * t * t * t * t + 1.0f;
433         }
434     };
435 
436     /**
437      * The callback to convert view info diffs into animations.
438      */
439     private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
440             new ViewInfoStore.ProcessCallback() {
441         @Override
442         public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
443                 @Nullable ItemHolderInfo postInfo) {
444             mRecycler.unscrapView(viewHolder);
445             animateDisappearance(viewHolder, info, postInfo);
446         }
447         @Override
448         public void processAppeared(ViewHolder viewHolder,
449                 ItemHolderInfo preInfo, ItemHolderInfo info) {
450             animateAppearance(viewHolder, preInfo, info);
451         }
452 
453         @Override
454         public void processPersistent(ViewHolder viewHolder,
455                 @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
456             viewHolder.setIsRecyclable(false);
457             if (mDataSetHasChangedAfterLayout) {
458                 // since it was rebound, use change instead as we'll be mapping them from
459                 // stable ids. If stable ids were false, we would not be running any
460                 // animations
461                 if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo, postInfo)) {
462                     postAnimationRunner();
463                 }
464             } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
465                 postAnimationRunner();
466             }
467         }
468         @Override
469         public void unused(ViewHolder viewHolder) {
470             mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
471         }
472     };
473 
RecyclerView(Context context)474     public RecyclerView(Context context) {
475         this(context, null);
476     }
477 
RecyclerView(Context context, @Nullable AttributeSet attrs)478     public RecyclerView(Context context, @Nullable AttributeSet attrs) {
479         this(context, attrs, 0);
480     }
481 
RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle)482     public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
483         super(context, attrs, defStyle);
484         if (attrs != null) {
485             TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
486             mClipToPadding = a.getBoolean(0, true);
487             a.recycle();
488         } else {
489             mClipToPadding = true;
490         }
491         setScrollContainer(true);
492         setFocusableInTouchMode(true);
493         final int version = Build.VERSION.SDK_INT;
494         mPostUpdatesOnAnimation = version >= 16;
495 
496         final ViewConfiguration vc = ViewConfiguration.get(context);
497         mTouchSlop = vc.getScaledTouchSlop();
498         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
499         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
500         setWillNotDraw(ViewCompat.getOverScrollMode(this) == ViewCompat.OVER_SCROLL_NEVER);
501 
502         mItemAnimator.setListener(mItemAnimatorListener);
503         initAdapterManager();
504         initChildrenHelper();
505         // If not explicitly specified this view is important for accessibility.
506         if (ViewCompat.getImportantForAccessibility(this)
507                 == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
508             ViewCompat.setImportantForAccessibility(this,
509                     ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
510         }
511         mAccessibilityManager = (AccessibilityManager) getContext()
512                 .getSystemService(Context.ACCESSIBILITY_SERVICE);
513         setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
514         // Create the layoutManager if specified.
515 
516         boolean nestedScrollingEnabled = true;
517 
518         if (attrs != null) {
519             int defStyleRes = 0;
520             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
521                     defStyle, defStyleRes);
522             String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
523             int descendantFocusability = a.getInt(
524                     R.styleable.RecyclerView_android_descendantFocusability, -1);
525             if (descendantFocusability == -1) {
526                 setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
527             }
528             a.recycle();
529             createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
530 
531             if (Build.VERSION.SDK_INT >= 21) {
532                 a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
533                         defStyle, defStyleRes);
534                 nestedScrollingEnabled = a.getBoolean(0, true);
535                 a.recycle();
536             }
537         } else {
538             setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
539         }
540 
541         // Re-set whether nested scrolling is enabled so that it is set on all API levels
542         setNestedScrollingEnabled(nestedScrollingEnabled);
543     }
544 
545     /**
546      * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
547      * @return An instance of AccessibilityDelegateCompat used by RecyclerView
548      */
getCompatAccessibilityDelegate()549     public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
550         return mAccessibilityDelegate;
551     }
552 
553     /**
554      * Sets the accessibility delegate compatibility implementation used by RecyclerView.
555      * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
556      */
setAccessibilityDelegateCompat( RecyclerViewAccessibilityDelegate accessibilityDelegate)557     public void setAccessibilityDelegateCompat(
558             RecyclerViewAccessibilityDelegate accessibilityDelegate) {
559         mAccessibilityDelegate = accessibilityDelegate;
560         ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
561     }
562 
563     /**
564      * Instantiate and set a LayoutManager, if specified in the attributes.
565      */
createLayoutManager(Context context, String className, AttributeSet attrs, int defStyleAttr, int defStyleRes)566     private void createLayoutManager(Context context, String className, AttributeSet attrs,
567             int defStyleAttr, int defStyleRes) {
568         if (className != null) {
569             className = className.trim();
570             if (className.length() != 0) {  // Can't use isEmpty since it was added in API 9.
571                 className = getFullClassName(context, className);
572                 try {
573                     ClassLoader classLoader;
574                     if (isInEditMode()) {
575                         // Stupid layoutlib cannot handle simple class loaders.
576                         classLoader = this.getClass().getClassLoader();
577                     } else {
578                         classLoader = context.getClassLoader();
579                     }
580                     Class<? extends LayoutManager> layoutManagerClass =
581                             classLoader.loadClass(className).asSubclass(LayoutManager.class);
582                     Constructor<? extends LayoutManager> constructor;
583                     Object[] constructorArgs = null;
584                     try {
585                         constructor = layoutManagerClass
586                                 .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
587                         constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
588                     } catch (NoSuchMethodException e) {
589                         try {
590                             constructor = layoutManagerClass.getConstructor();
591                         } catch (NoSuchMethodException e1) {
592                             e1.initCause(e);
593                             throw new IllegalStateException(attrs.getPositionDescription() +
594                                     ": Error creating LayoutManager " + className, e1);
595                         }
596                     }
597                     constructor.setAccessible(true);
598                     setLayoutManager(constructor.newInstance(constructorArgs));
599                 } catch (ClassNotFoundException e) {
600                     throw new IllegalStateException(attrs.getPositionDescription()
601                             + ": Unable to find LayoutManager " + className, e);
602                 } catch (InvocationTargetException e) {
603                     throw new IllegalStateException(attrs.getPositionDescription()
604                             + ": Could not instantiate the LayoutManager: " + className, e);
605                 } catch (InstantiationException e) {
606                     throw new IllegalStateException(attrs.getPositionDescription()
607                             + ": Could not instantiate the LayoutManager: " + className, e);
608                 } catch (IllegalAccessException e) {
609                     throw new IllegalStateException(attrs.getPositionDescription()
610                             + ": Cannot access non-public constructor " + className, e);
611                 } catch (ClassCastException e) {
612                     throw new IllegalStateException(attrs.getPositionDescription()
613                             + ": Class is not a LayoutManager " + className, e);
614                 }
615             }
616         }
617     }
618 
getFullClassName(Context context, String className)619     private String getFullClassName(Context context, String className) {
620         if (className.charAt(0) == '.') {
621             return context.getPackageName() + className;
622         }
623         if (className.contains(".")) {
624             return className;
625         }
626         return RecyclerView.class.getPackage().getName() + '.' + className;
627     }
628 
initChildrenHelper()629     private void initChildrenHelper() {
630         mChildHelper = new ChildHelper(new ChildHelper.Callback() {
631             @Override
632             public int getChildCount() {
633                 return RecyclerView.this.getChildCount();
634             }
635 
636             @Override
637             public void addView(View child, int index) {
638                 RecyclerView.this.addView(child, index);
639                 dispatchChildAttached(child);
640             }
641 
642             @Override
643             public int indexOfChild(View view) {
644                 return RecyclerView.this.indexOfChild(view);
645             }
646 
647             @Override
648             public void removeViewAt(int index) {
649                 final View child = RecyclerView.this.getChildAt(index);
650                 if (child != null) {
651                     dispatchChildDetached(child);
652                 }
653                 RecyclerView.this.removeViewAt(index);
654             }
655 
656             @Override
657             public View getChildAt(int offset) {
658                 return RecyclerView.this.getChildAt(offset);
659             }
660 
661             @Override
662             public void removeAllViews() {
663                 final int count = getChildCount();
664                 for (int i = 0; i < count; i ++) {
665                     dispatchChildDetached(getChildAt(i));
666                 }
667                 RecyclerView.this.removeAllViews();
668             }
669 
670             @Override
671             public ViewHolder getChildViewHolder(View view) {
672                 return getChildViewHolderInt(view);
673             }
674 
675             @Override
676             public void attachViewToParent(View child, int index,
677                     ViewGroup.LayoutParams layoutParams) {
678                 final ViewHolder vh = getChildViewHolderInt(child);
679                 if (vh != null) {
680                     if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
681                         throw new IllegalArgumentException("Called attach on a child which is not"
682                                 + " detached: " + vh);
683                     }
684                     if (DEBUG) {
685                         Log.d(TAG, "reAttach " + vh);
686                     }
687                     vh.clearTmpDetachFlag();
688                 }
689                 RecyclerView.this.attachViewToParent(child, index, layoutParams);
690             }
691 
692             @Override
693             public void detachViewFromParent(int offset) {
694                 final View view = getChildAt(offset);
695                 if (view != null) {
696                     final ViewHolder vh = getChildViewHolderInt(view);
697                     if (vh != null) {
698                         if (vh.isTmpDetached() && !vh.shouldIgnore()) {
699                             throw new IllegalArgumentException("called detach on an already"
700                                     + " detached child " + vh);
701                         }
702                         if (DEBUG) {
703                             Log.d(TAG, "tmpDetach " + vh);
704                         }
705                         vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
706                     }
707                 }
708                 RecyclerView.this.detachViewFromParent(offset);
709             }
710 
711             @Override
712             public void onEnteredHiddenState(View child) {
713                 final ViewHolder vh = getChildViewHolderInt(child);
714                 if (vh != null) {
715                     vh.onEnteredHiddenState();
716                 }
717             }
718 
719             @Override
720             public void onLeftHiddenState(View child) {
721                 final ViewHolder vh = getChildViewHolderInt(child);
722                 if (vh != null) {
723                     vh.onLeftHiddenState();
724                 }
725             }
726         });
727     }
728 
initAdapterManager()729     void initAdapterManager() {
730         mAdapterHelper = new AdapterHelper(new Callback() {
731             @Override
732             public ViewHolder findViewHolder(int position) {
733                 final ViewHolder vh = findViewHolderForPosition(position, true);
734                 if (vh == null) {
735                     return null;
736                 }
737                 // ensure it is not hidden because for adapter helper, the only thing matter is that
738                 // LM thinks view is a child.
739                 if (mChildHelper.isHidden(vh.itemView)) {
740                     if (DEBUG) {
741                         Log.d(TAG, "assuming view holder cannot be find because it is hidden");
742                     }
743                     return null;
744                 }
745                 return vh;
746             }
747 
748             @Override
749             public void offsetPositionsForRemovingInvisible(int start, int count) {
750                 offsetPositionRecordsForRemove(start, count, true);
751                 mItemsAddedOrRemoved = true;
752                 mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
753             }
754 
755             @Override
756             public void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount) {
757                 offsetPositionRecordsForRemove(positionStart, itemCount, false);
758                 mItemsAddedOrRemoved = true;
759             }
760 
761             @Override
762             public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
763                 viewRangeUpdate(positionStart, itemCount, payload);
764                 mItemsChanged = true;
765             }
766 
767             @Override
768             public void onDispatchFirstPass(UpdateOp op) {
769                 dispatchUpdate(op);
770             }
771 
772             void dispatchUpdate(UpdateOp op) {
773                 switch (op.cmd) {
774                     case UpdateOp.ADD:
775                         mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
776                         break;
777                     case UpdateOp.REMOVE:
778                         mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
779                         break;
780                     case UpdateOp.UPDATE:
781                         mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
782                                 op.payload);
783                         break;
784                     case UpdateOp.MOVE:
785                         mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
786                         break;
787                 }
788             }
789 
790             @Override
791             public void onDispatchSecondPass(UpdateOp op) {
792                 dispatchUpdate(op);
793             }
794 
795             @Override
796             public void offsetPositionsForAdd(int positionStart, int itemCount) {
797                 offsetPositionRecordsForInsert(positionStart, itemCount);
798                 mItemsAddedOrRemoved = true;
799             }
800 
801             @Override
802             public void offsetPositionsForMove(int from, int to) {
803                 offsetPositionRecordsForMove(from, to);
804                 // should we create mItemsMoved ?
805                 mItemsAddedOrRemoved = true;
806             }
807         });
808     }
809 
810     /**
811      * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
812      * size is not affected by the adapter contents. RecyclerView can still change its size based
813      * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
814      * size of its children or contents of its adapter (except the number of items in the adapter).
815      * <p>
816      * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
817      * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
818      *
819      * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
820      */
setHasFixedSize(boolean hasFixedSize)821     public void setHasFixedSize(boolean hasFixedSize) {
822         mHasFixedSize = hasFixedSize;
823     }
824 
825     /**
826      * @return true if the app has specified that changes in adapter content cannot change
827      * the size of the RecyclerView itself.
828      */
hasFixedSize()829     public boolean hasFixedSize() {
830         return mHasFixedSize;
831     }
832 
833     @Override
setClipToPadding(boolean clipToPadding)834     public void setClipToPadding(boolean clipToPadding) {
835         if (clipToPadding != mClipToPadding) {
836             invalidateGlows();
837         }
838         mClipToPadding = clipToPadding;
839         super.setClipToPadding(clipToPadding);
840         if (mFirstLayoutComplete) {
841             requestLayout();
842         }
843     }
844 
845     /**
846      * Configure the scrolling touch slop for a specific use case.
847      *
848      * Set up the RecyclerView's scrolling motion threshold based on common usages.
849      * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
850      *
851      * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
852      *                     the intended usage of this RecyclerView
853      */
setScrollingTouchSlop(int slopConstant)854     public void setScrollingTouchSlop(int slopConstant) {
855         final ViewConfiguration vc = ViewConfiguration.get(getContext());
856         switch (slopConstant) {
857             default:
858                 Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
859                       + slopConstant + "; using default value");
860                 // fall-through
861             case TOUCH_SLOP_DEFAULT:
862                 mTouchSlop = vc.getScaledTouchSlop();
863                 break;
864 
865             case TOUCH_SLOP_PAGING:
866                 mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(vc);
867                 break;
868         }
869     }
870 
871     /**
872      * Swaps the current adapter with the provided one. It is similar to
873      * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
874      * {@link ViewHolder} and does not clear the RecycledViewPool.
875      * <p>
876      * Note that it still calls onAdapterChanged callbacks.
877      *
878      * @param adapter The new adapter to set, or null to set no adapter.
879      * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
880      *                                      Views. If adapters have stable ids and/or you want to
881      *                                      animate the disappearing views, you may prefer to set
882      *                                      this to false.
883      * @see #setAdapter(Adapter)
884      */
swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews)885     public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
886         // bail out if layout is frozen
887         setLayoutFrozen(false);
888         setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
889         setDataSetChangedAfterLayout();
890         requestLayout();
891     }
892     /**
893      * Set a new adapter to provide child views on demand.
894      * <p>
895      * When adapter is changed, all existing views are recycled back to the pool. If the pool has
896      * only one adapter, it will be cleared.
897      *
898      * @param adapter The new adapter to set, or null to set no adapter.
899      * @see #swapAdapter(Adapter, boolean)
900      */
setAdapter(Adapter adapter)901     public void setAdapter(Adapter adapter) {
902         // bail out if layout is frozen
903         setLayoutFrozen(false);
904         setAdapterInternal(adapter, false, true);
905         requestLayout();
906     }
907 
908     /**
909      * Replaces the current adapter with the new one and triggers listeners.
910      * @param adapter The new adapter
911      * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
912      *                               item types with the current adapter (helps us avoid cache
913      *                               invalidation).
914      * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
915      *                               compatibleWithPrevious is false, this parameter is ignored.
916      */
setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews)917     private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
918             boolean removeAndRecycleViews) {
919         if (mAdapter != null) {
920             mAdapter.unregisterAdapterDataObserver(mObserver);
921             mAdapter.onDetachedFromRecyclerView(this);
922         }
923         if (!compatibleWithPrevious || removeAndRecycleViews) {
924             // end all running animations
925             if (mItemAnimator != null) {
926                 mItemAnimator.endAnimations();
927             }
928             // Since animations are ended, mLayout.children should be equal to
929             // recyclerView.children. This may not be true if item animator's end does not work as
930             // expected. (e.g. not release children instantly). It is safer to use mLayout's child
931             // count.
932             if (mLayout != null) {
933                 mLayout.removeAndRecycleAllViews(mRecycler);
934                 mLayout.removeAndRecycleScrapInt(mRecycler);
935             }
936             // we should clear it here before adapters are swapped to ensure correct callbacks.
937             mRecycler.clear();
938         }
939         mAdapterHelper.reset();
940         final Adapter oldAdapter = mAdapter;
941         mAdapter = adapter;
942         if (adapter != null) {
943             adapter.registerAdapterDataObserver(mObserver);
944             adapter.onAttachedToRecyclerView(this);
945         }
946         if (mLayout != null) {
947             mLayout.onAdapterChanged(oldAdapter, mAdapter);
948         }
949         mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
950         mState.mStructureChanged = true;
951         markKnownViewsInvalid();
952     }
953 
954     /**
955      * Retrieves the previously set adapter or null if no adapter is set.
956      *
957      * @return The previously set adapter
958      * @see #setAdapter(Adapter)
959      */
getAdapter()960     public Adapter getAdapter() {
961         return mAdapter;
962     }
963 
964     /**
965      * Register a listener that will be notified whenever a child view is recycled.
966      *
967      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
968      * that a child view is no longer needed. If an application associates expensive
969      * or heavyweight data with item views, this may be a good place to release
970      * or free those resources.</p>
971      *
972      * @param listener Listener to register, or null to clear
973      */
setRecyclerListener(RecyclerListener listener)974     public void setRecyclerListener(RecyclerListener listener) {
975         mRecyclerListener = listener;
976     }
977 
978     /**
979      * <p>Return the offset of the RecyclerView's text baseline from the its top
980      * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
981      * this method returns -1.</p>
982      *
983      * @return the offset of the baseline within the RecyclerView's bounds or -1
984      *         if baseline alignment is not supported
985      */
986     @Override
getBaseline()987     public int getBaseline() {
988         if (mLayout != null) {
989             return mLayout.getBaseline();
990         } else {
991             return super.getBaseline();
992         }
993     }
994 
995     /**
996      * Register a listener that will be notified whenever a child view is attached to or detached
997      * from RecyclerView.
998      *
999      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1000      * that a child view is no longer needed. If an application associates expensive
1001      * or heavyweight data with item views, this may be a good place to release
1002      * or free those resources.</p>
1003      *
1004      * @param listener Listener to register
1005      */
addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener)1006     public void addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1007         if (mOnChildAttachStateListeners == null) {
1008             mOnChildAttachStateListeners = new ArrayList<>();
1009         }
1010         mOnChildAttachStateListeners.add(listener);
1011     }
1012 
1013     /**
1014      * Removes the provided listener from child attached state listeners list.
1015      *
1016      * @param listener Listener to unregister
1017      */
removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener)1018     public void removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1019         if (mOnChildAttachStateListeners == null) {
1020             return;
1021         }
1022         mOnChildAttachStateListeners.remove(listener);
1023     }
1024 
1025     /**
1026      * Removes all listeners that were added via
1027      * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
1028      */
clearOnChildAttachStateChangeListeners()1029     public void clearOnChildAttachStateChangeListeners() {
1030         if (mOnChildAttachStateListeners != null) {
1031             mOnChildAttachStateListeners.clear();
1032         }
1033     }
1034 
1035     /**
1036      * Set the {@link LayoutManager} that this RecyclerView will use.
1037      *
1038      * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
1039      * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
1040      * layout arrangements for child views. These arrangements are controlled by the
1041      * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
1042      *
1043      * <p>Several default strategies are provided for common uses such as lists and grids.</p>
1044      *
1045      * @param layout LayoutManager to use
1046      */
setLayoutManager(LayoutManager layout)1047     public void setLayoutManager(LayoutManager layout) {
1048         if (layout == mLayout) {
1049             return;
1050         }
1051         stopScroll();
1052         // TODO We should do this switch a dispachLayout pass and animate children. There is a good
1053         // chance that LayoutManagers will re-use views.
1054         if (mLayout != null) {
1055             if (mIsAttached) {
1056                 mLayout.dispatchDetachedFromWindow(this, mRecycler);
1057             }
1058             mLayout.setRecyclerView(null);
1059         }
1060         mRecycler.clear();
1061         mChildHelper.removeAllViewsUnfiltered();
1062         mLayout = layout;
1063         if (layout != null) {
1064             if (layout.mRecyclerView != null) {
1065                 throw new IllegalArgumentException("LayoutManager " + layout +
1066                         " is already attached to a RecyclerView: " + layout.mRecyclerView);
1067             }
1068             mLayout.setRecyclerView(this);
1069             if (mIsAttached) {
1070                 mLayout.dispatchAttachedToWindow(this);
1071             }
1072         }
1073         requestLayout();
1074     }
1075 
1076     @Override
onSaveInstanceState()1077     protected Parcelable onSaveInstanceState() {
1078         SavedState state = new SavedState(super.onSaveInstanceState());
1079         if (mPendingSavedState != null) {
1080             state.copyFrom(mPendingSavedState);
1081         } else if (mLayout != null) {
1082             state.mLayoutState = mLayout.onSaveInstanceState();
1083         } else {
1084             state.mLayoutState = null;
1085         }
1086 
1087         return state;
1088     }
1089 
1090     @Override
onRestoreInstanceState(Parcelable state)1091     protected void onRestoreInstanceState(Parcelable state) {
1092         if (!(state instanceof SavedState)) {
1093             super.onRestoreInstanceState(state);
1094             return;
1095         }
1096 
1097         mPendingSavedState = (SavedState) state;
1098         super.onRestoreInstanceState(mPendingSavedState.getSuperState());
1099         if (mLayout != null && mPendingSavedState.mLayoutState != null) {
1100             mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
1101         }
1102     }
1103 
1104     /**
1105      * Override to prevent freezing of any views created by the adapter.
1106      */
1107     @Override
dispatchSaveInstanceState(SparseArray<Parcelable> container)1108     protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1109         dispatchFreezeSelfOnly(container);
1110     }
1111 
1112     /**
1113      * Override to prevent thawing of any views created by the adapter.
1114      */
1115     @Override
dispatchRestoreInstanceState(SparseArray<Parcelable> container)1116     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1117         dispatchThawSelfOnly(container);
1118     }
1119 
1120     /**
1121      * Adds a view to the animatingViews list.
1122      * mAnimatingViews holds the child views that are currently being kept around
1123      * purely for the purpose of being animated out of view. They are drawn as a regular
1124      * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
1125      * as they are managed separately from the regular child views.
1126      * @param viewHolder The ViewHolder to be removed
1127      */
addAnimatingView(ViewHolder viewHolder)1128     private void addAnimatingView(ViewHolder viewHolder) {
1129         final View view = viewHolder.itemView;
1130         final boolean alreadyParented = view.getParent() == this;
1131         mRecycler.unscrapView(getChildViewHolder(view));
1132         if (viewHolder.isTmpDetached()) {
1133             // re-attach
1134             mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
1135         } else if(!alreadyParented) {
1136             mChildHelper.addView(view, true);
1137         } else {
1138             mChildHelper.hide(view);
1139         }
1140     }
1141 
1142     /**
1143      * Removes a view from the animatingViews list.
1144      * @param view The view to be removed
1145      * @see #addAnimatingView(RecyclerView.ViewHolder)
1146      * @return true if an animating view is removed
1147      */
removeAnimatingView(View view)1148     private boolean removeAnimatingView(View view) {
1149         eatRequestLayout();
1150         final boolean removed = mChildHelper.removeViewIfHidden(view);
1151         if (removed) {
1152             final ViewHolder viewHolder = getChildViewHolderInt(view);
1153             mRecycler.unscrapView(viewHolder);
1154             mRecycler.recycleViewHolderInternal(viewHolder);
1155             if (DEBUG) {
1156                 Log.d(TAG, "after removing animated view: " + view + ", " + this);
1157             }
1158         }
1159         // only clear request eaten flag if we removed the view.
1160         resumeRequestLayout(!removed);
1161         return removed;
1162     }
1163 
1164     /**
1165      * Return the {@link LayoutManager} currently responsible for
1166      * layout policy for this RecyclerView.
1167      *
1168      * @return The currently bound LayoutManager
1169      */
getLayoutManager()1170     public LayoutManager getLayoutManager() {
1171         return mLayout;
1172     }
1173 
1174     /**
1175      * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
1176      * if no pool is set for this view a new one will be created. See
1177      * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
1178      *
1179      * @return The pool used to store recycled item views for reuse.
1180      * @see #setRecycledViewPool(RecycledViewPool)
1181      */
getRecycledViewPool()1182     public RecycledViewPool getRecycledViewPool() {
1183         return mRecycler.getRecycledViewPool();
1184     }
1185 
1186     /**
1187      * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
1188      * This can be useful if you have multiple RecyclerViews with adapters that use the same
1189      * view types, for example if you have several data sets with the same kinds of item views
1190      * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
1191      *
1192      * @param pool Pool to set. If this parameter is null a new pool will be created and used.
1193      */
setRecycledViewPool(RecycledViewPool pool)1194     public void setRecycledViewPool(RecycledViewPool pool) {
1195         mRecycler.setRecycledViewPool(pool);
1196     }
1197 
1198     /**
1199      * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
1200      *
1201      * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
1202      *
1203      * @see {@link ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)}
1204      */
setViewCacheExtension(ViewCacheExtension extension)1205     public void setViewCacheExtension(ViewCacheExtension extension) {
1206         mRecycler.setViewCacheExtension(extension);
1207     }
1208 
1209     /**
1210      * Set the number of offscreen views to retain before adding them to the potentially shared
1211      * {@link #getRecycledViewPool() recycled view pool}.
1212      *
1213      * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
1214      * a LayoutManager to reuse those views unmodified without needing to return to the adapter
1215      * to rebind them.</p>
1216      *
1217      * @param size Number of views to cache offscreen before returning them to the general
1218      *             recycled view pool
1219      */
setItemViewCacheSize(int size)1220     public void setItemViewCacheSize(int size) {
1221         mRecycler.setViewCacheSize(size);
1222     }
1223 
1224     /**
1225      * Return the current scrolling state of the RecyclerView.
1226      *
1227      * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
1228      * {@link #SCROLL_STATE_SETTLING}
1229      */
getScrollState()1230     public int getScrollState() {
1231         return mScrollState;
1232     }
1233 
setScrollState(int state)1234     private void setScrollState(int state) {
1235         if (state == mScrollState) {
1236             return;
1237         }
1238         if (DEBUG) {
1239             Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
1240                     new Exception());
1241         }
1242         mScrollState = state;
1243         if (state != SCROLL_STATE_SETTLING) {
1244             stopScrollersInternal();
1245         }
1246         dispatchOnScrollStateChanged(state);
1247     }
1248 
1249     /**
1250      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1251      * affect both measurement and drawing of individual item views.
1252      *
1253      * <p>Item decorations are ordered. Decorations placed earlier in the list will
1254      * be run/queried/drawn first for their effects on item views. Padding added to views
1255      * will be nested; a padding added by an earlier decoration will mean further
1256      * item decorations in the list will be asked to draw/pad within the previous decoration's
1257      * given area.</p>
1258      *
1259      * @param decor Decoration to add
1260      * @param index Position in the decoration chain to insert this decoration at. If this value
1261      *              is negative the decoration will be added at the end.
1262      */
addItemDecoration(ItemDecoration decor, int index)1263     public void addItemDecoration(ItemDecoration decor, int index) {
1264         if (mLayout != null) {
1265             mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
1266                     + " layout");
1267         }
1268         if (mItemDecorations.isEmpty()) {
1269             setWillNotDraw(false);
1270         }
1271         if (index < 0) {
1272             mItemDecorations.add(decor);
1273         } else {
1274             mItemDecorations.add(index, decor);
1275         }
1276         markItemDecorInsetsDirty();
1277         requestLayout();
1278     }
1279 
1280     /**
1281      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1282      * affect both measurement and drawing of individual item views.
1283      *
1284      * <p>Item decorations are ordered. Decorations placed earlier in the list will
1285      * be run/queried/drawn first for their effects on item views. Padding added to views
1286      * will be nested; a padding added by an earlier decoration will mean further
1287      * item decorations in the list will be asked to draw/pad within the previous decoration's
1288      * given area.</p>
1289      *
1290      * @param decor Decoration to add
1291      */
addItemDecoration(ItemDecoration decor)1292     public void addItemDecoration(ItemDecoration decor) {
1293         addItemDecoration(decor, -1);
1294     }
1295 
1296     /**
1297      * Remove an {@link ItemDecoration} from this RecyclerView.
1298      *
1299      * <p>The given decoration will no longer impact the measurement and drawing of
1300      * item views.</p>
1301      *
1302      * @param decor Decoration to remove
1303      * @see #addItemDecoration(ItemDecoration)
1304      */
removeItemDecoration(ItemDecoration decor)1305     public void removeItemDecoration(ItemDecoration decor) {
1306         if (mLayout != null) {
1307             mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
1308                     + " layout");
1309         }
1310         mItemDecorations.remove(decor);
1311         if (mItemDecorations.isEmpty()) {
1312             setWillNotDraw(ViewCompat.getOverScrollMode(this) == ViewCompat.OVER_SCROLL_NEVER);
1313         }
1314         markItemDecorInsetsDirty();
1315         requestLayout();
1316     }
1317 
1318     /**
1319      * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
1320      * <p>
1321      * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
1322      * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
1323      * true if childDrawingOrderCallback is not null, false otherwise.
1324      * <p>
1325      * Note that child drawing order may be overridden by View's elevation.
1326      *
1327      * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
1328      *                                  system.
1329      */
setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback)1330     public void setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback) {
1331         if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
1332             return;
1333         }
1334         mChildDrawingOrderCallback = childDrawingOrderCallback;
1335         setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
1336     }
1337 
1338     /**
1339      * Set a listener that will be notified of any changes in scroll state or position.
1340      *
1341      * @param listener Listener to set or null to clear
1342      *
1343      * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
1344      *             {@link #removeOnScrollListener(OnScrollListener)}
1345      */
1346     @Deprecated
setOnScrollListener(OnScrollListener listener)1347     public void setOnScrollListener(OnScrollListener listener) {
1348         mScrollListener = listener;
1349     }
1350 
1351     /**
1352      * Add a listener that will be notified of any changes in scroll state or position.
1353      *
1354      * <p>Components that add a listener should take care to remove it when finished.
1355      * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
1356      * to remove all attached listeners.</p>
1357      *
1358      * @param listener listener to set or null to clear
1359      */
addOnScrollListener(OnScrollListener listener)1360     public void addOnScrollListener(OnScrollListener listener) {
1361         if (mScrollListeners == null) {
1362             mScrollListeners = new ArrayList<>();
1363         }
1364         mScrollListeners.add(listener);
1365     }
1366 
1367     /**
1368      * Remove a listener that was notified of any changes in scroll state or position.
1369      *
1370      * @param listener listener to set or null to clear
1371      */
removeOnScrollListener(OnScrollListener listener)1372     public void removeOnScrollListener(OnScrollListener listener) {
1373         if (mScrollListeners != null) {
1374             mScrollListeners.remove(listener);
1375         }
1376     }
1377 
1378     /**
1379      * Remove all secondary listener that were notified of any changes in scroll state or position.
1380      */
clearOnScrollListeners()1381     public void clearOnScrollListeners() {
1382         if (mScrollListeners != null) {
1383             mScrollListeners.clear();
1384         }
1385     }
1386 
1387     /**
1388      * Convenience method to scroll to a certain position.
1389      *
1390      * RecyclerView does not implement scrolling logic, rather forwards the call to
1391      * {@link android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
1392      * @param position Scroll to this adapter position
1393      * @see android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)
1394      */
scrollToPosition(int position)1395     public void scrollToPosition(int position) {
1396         if (mLayoutFrozen) {
1397             return;
1398         }
1399         stopScroll();
1400         if (mLayout == null) {
1401             Log.e(TAG, "Cannot scroll to position a LayoutManager set. " +
1402                     "Call setLayoutManager with a non-null argument.");
1403             return;
1404         }
1405         mLayout.scrollToPosition(position);
1406         awakenScrollBars();
1407     }
1408 
jumpToPositionForSmoothScroller(int position)1409     private void jumpToPositionForSmoothScroller(int position) {
1410         if (mLayout == null) {
1411             return;
1412         }
1413         mLayout.scrollToPosition(position);
1414         awakenScrollBars();
1415     }
1416 
1417     /**
1418      * Starts a smooth scroll to an adapter position.
1419      * <p>
1420      * To support smooth scrolling, you must override
1421      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
1422      * {@link SmoothScroller}.
1423      * <p>
1424      * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
1425      * provide a custom smooth scroll logic, override
1426      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
1427      * LayoutManager.
1428      *
1429      * @param position The adapter position to scroll to
1430      * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
1431      */
smoothScrollToPosition(int position)1432     public void smoothScrollToPosition(int position) {
1433         if (mLayoutFrozen) {
1434             return;
1435         }
1436         if (mLayout == null) {
1437             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
1438                     "Call setLayoutManager with a non-null argument.");
1439             return;
1440         }
1441         mLayout.smoothScrollToPosition(this, mState, position);
1442     }
1443 
1444     @Override
scrollTo(int x, int y)1445     public void scrollTo(int x, int y) {
1446         Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
1447                 + "Use scrollToPosition instead");
1448     }
1449 
1450     @Override
scrollBy(int x, int y)1451     public void scrollBy(int x, int y) {
1452         if (mLayout == null) {
1453             Log.e(TAG, "Cannot scroll without a LayoutManager set. " +
1454                     "Call setLayoutManager with a non-null argument.");
1455             return;
1456         }
1457         if (mLayoutFrozen) {
1458             return;
1459         }
1460         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1461         final boolean canScrollVertical = mLayout.canScrollVertically();
1462         if (canScrollHorizontal || canScrollVertical) {
1463             scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
1464         }
1465     }
1466 
1467     /**
1468      * Helper method reflect data changes to the state.
1469      * <p>
1470      * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
1471      * but data actually changed.
1472      * <p>
1473      * This method consumes all deferred changes to avoid that case.
1474      */
consumePendingUpdateOperations()1475     private void consumePendingUpdateOperations() {
1476         if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
1477             TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1478             dispatchLayout();
1479             TraceCompat.endSection();
1480             return;
1481         }
1482         if (!mAdapterHelper.hasPendingUpdates()) {
1483             return;
1484         }
1485 
1486         // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
1487         // of the visible items is affected and if not, just ignore the change.
1488         if (mAdapterHelper.hasAnyUpdateTypes(UpdateOp.UPDATE) && !mAdapterHelper
1489                 .hasAnyUpdateTypes(UpdateOp.ADD | UpdateOp.REMOVE | UpdateOp.MOVE)) {
1490             TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
1491             eatRequestLayout();
1492             mAdapterHelper.preProcess();
1493             if (!mLayoutRequestEaten) {
1494                 if (hasUpdatedView()) {
1495                     dispatchLayout();
1496                 } else {
1497                     // no need to layout, clean state
1498                     mAdapterHelper.consumePostponedUpdates();
1499                 }
1500             }
1501             resumeRequestLayout(true);
1502             TraceCompat.endSection();
1503         } else if (mAdapterHelper.hasPendingUpdates()) {
1504             TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1505             dispatchLayout();
1506             TraceCompat.endSection();
1507         }
1508     }
1509 
1510     /**
1511      * @return True if an existing view holder needs to be updated
1512      */
hasUpdatedView()1513     private boolean hasUpdatedView() {
1514         final int childCount = mChildHelper.getChildCount();
1515         for (int i = 0; i < childCount; i++) {
1516             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
1517             if (holder == null || holder.shouldIgnore()) {
1518                 continue;
1519             }
1520             if (holder.isUpdated()) {
1521                 return true;
1522             }
1523         }
1524         return false;
1525     }
1526 
1527     /**
1528      * Does not perform bounds checking. Used by internal methods that have already validated input.
1529      * <p>
1530      * It also reports any unused scroll request to the related EdgeEffect.
1531      *
1532      * @param x The amount of horizontal scroll request
1533      * @param y The amount of vertical scroll request
1534      * @param ev The originating MotionEvent, or null if not from a touch event.
1535      *
1536      * @return Whether any scroll was consumed in either direction.
1537      */
scrollByInternal(int x, int y, MotionEvent ev)1538     boolean scrollByInternal(int x, int y, MotionEvent ev) {
1539         int unconsumedX = 0, unconsumedY = 0;
1540         int consumedX = 0, consumedY = 0;
1541 
1542         consumePendingUpdateOperations();
1543         if (mAdapter != null) {
1544             eatRequestLayout();
1545             onEnterLayoutOrScroll();
1546             TraceCompat.beginSection(TRACE_SCROLL_TAG);
1547             if (x != 0) {
1548                 consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
1549                 unconsumedX = x - consumedX;
1550             }
1551             if (y != 0) {
1552                 consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
1553                 unconsumedY = y - consumedY;
1554             }
1555             TraceCompat.endSection();
1556             repositionShadowingViews();
1557             onExitLayoutOrScroll();
1558             resumeRequestLayout(false);
1559         }
1560         if (!mItemDecorations.isEmpty()) {
1561             invalidate();
1562         }
1563 
1564         if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
1565             // Update the last touch co-ords, taking any scroll offset into account
1566             mLastTouchX -= mScrollOffset[0];
1567             mLastTouchY -= mScrollOffset[1];
1568             if (ev != null) {
1569                 ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
1570             }
1571             mNestedOffsets[0] += mScrollOffset[0];
1572             mNestedOffsets[1] += mScrollOffset[1];
1573         } else if (ViewCompat.getOverScrollMode(this) != ViewCompat.OVER_SCROLL_NEVER) {
1574             if (ev != null) {
1575                 pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
1576             }
1577             considerReleasingGlowsOnScroll(x, y);
1578         }
1579         if (consumedX != 0 || consumedY != 0) {
1580             dispatchOnScrolled(consumedX, consumedY);
1581         }
1582         if (!awakenScrollBars()) {
1583             invalidate();
1584         }
1585         return consumedX != 0 || consumedY != 0;
1586     }
1587 
1588     /**
1589      * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
1590      * range. This value is used to compute the length of the thumb within the scrollbar's track.
1591      * </p>
1592      *
1593      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1594      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
1595      *
1596      * <p>Default implementation returns 0.</p>
1597      *
1598      * <p>If you want to support scroll bars, override
1599      * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
1600      * LayoutManager. </p>
1601      *
1602      * @return The horizontal offset of the scrollbar's thumb
1603      * @see android.support.v7.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset
1604      * (RecyclerView.State)
1605      */
1606     @Override
computeHorizontalScrollOffset()1607     public int computeHorizontalScrollOffset() {
1608         if (mLayout == null) {
1609             return 0;
1610         }
1611         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
1612     }
1613 
1614     /**
1615      * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
1616      * horizontal range. This value is used to compute the length of the thumb within the
1617      * scrollbar's track.</p>
1618      *
1619      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1620      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
1621      *
1622      * <p>Default implementation returns 0.</p>
1623      *
1624      * <p>If you want to support scroll bars, override
1625      * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
1626      * LayoutManager.</p>
1627      *
1628      * @return The horizontal extent of the scrollbar's thumb
1629      * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
1630      */
1631     @Override
computeHorizontalScrollExtent()1632     public int computeHorizontalScrollExtent() {
1633         if (mLayout == null) {
1634             return 0;
1635         }
1636         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
1637     }
1638 
1639     /**
1640      * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
1641      *
1642      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1643      * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
1644      *
1645      * <p>Default implementation returns 0.</p>
1646      *
1647      * <p>If you want to support scroll bars, override
1648      * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
1649      * LayoutManager.</p>
1650      *
1651      * @return The total horizontal range represented by the vertical scrollbar
1652      * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
1653      */
1654     @Override
computeHorizontalScrollRange()1655     public int computeHorizontalScrollRange() {
1656         if (mLayout == null) {
1657             return 0;
1658         }
1659         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
1660     }
1661 
1662     /**
1663      * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
1664      * This value is used to compute the length of the thumb within the scrollbar's track. </p>
1665      *
1666      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1667      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
1668      *
1669      * <p>Default implementation returns 0.</p>
1670      *
1671      * <p>If you want to support scroll bars, override
1672      * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
1673      * LayoutManager.</p>
1674      *
1675      * @return The vertical offset of the scrollbar's thumb
1676      * @see android.support.v7.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset
1677      * (RecyclerView.State)
1678      */
1679     @Override
computeVerticalScrollOffset()1680     public int computeVerticalScrollOffset() {
1681         if (mLayout == null) {
1682             return 0;
1683         }
1684         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
1685     }
1686 
1687     /**
1688      * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
1689      * This value is used to compute the length of the thumb within the scrollbar's track.</p>
1690      *
1691      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1692      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
1693      *
1694      * <p>Default implementation returns 0.</p>
1695      *
1696      * <p>If you want to support scroll bars, override
1697      * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
1698      * LayoutManager.</p>
1699      *
1700      * @return The vertical extent of the scrollbar's thumb
1701      * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
1702      */
1703     @Override
computeVerticalScrollExtent()1704     public int computeVerticalScrollExtent() {
1705         if (mLayout == null) {
1706             return 0;
1707         }
1708         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
1709     }
1710 
1711     /**
1712      * <p>Compute the vertical range that the vertical scrollbar represents.</p>
1713      *
1714      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1715      * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
1716      *
1717      * <p>Default implementation returns 0.</p>
1718      *
1719      * <p>If you want to support scroll bars, override
1720      * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
1721      * LayoutManager.</p>
1722      *
1723      * @return The total vertical range represented by the vertical scrollbar
1724      * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
1725      */
1726     @Override
computeVerticalScrollRange()1727     public int computeVerticalScrollRange() {
1728         if (mLayout == null) {
1729             return 0;
1730         }
1731         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
1732     }
1733 
1734 
eatRequestLayout()1735     void eatRequestLayout() {
1736         mEatRequestLayout++;
1737         if (mEatRequestLayout == 1 && !mLayoutFrozen) {
1738             mLayoutRequestEaten = false;
1739         }
1740     }
1741 
resumeRequestLayout(boolean performLayoutChildren)1742     void resumeRequestLayout(boolean performLayoutChildren) {
1743         if (mEatRequestLayout < 1) {
1744             //noinspection PointlessBooleanExpression
1745             if (DEBUG) {
1746                 throw new IllegalStateException("invalid eat request layout count");
1747             }
1748             mEatRequestLayout = 1;
1749         }
1750         if (!performLayoutChildren) {
1751             // Reset the layout request eaten counter.
1752             // This is necessary since eatRequest calls can be nested in which case the outher
1753             // call will override the inner one.
1754             // for instance:
1755             // eat layout for process adapter updates
1756             //   eat layout for dispatchLayout
1757             //     a bunch of req layout calls arrive
1758 
1759             mLayoutRequestEaten = false;
1760         }
1761         if (mEatRequestLayout == 1) {
1762             // when layout is frozen we should delay dispatchLayout()
1763             if (performLayoutChildren && mLayoutRequestEaten && !mLayoutFrozen &&
1764                     mLayout != null && mAdapter != null) {
1765                 dispatchLayout();
1766             }
1767             if (!mLayoutFrozen) {
1768                 mLayoutRequestEaten = false;
1769             }
1770         }
1771         mEatRequestLayout--;
1772     }
1773 
1774     /**
1775      * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
1776      * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
1777      * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
1778      * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
1779      * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
1780      * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
1781      * called.
1782      *
1783      * <p>
1784      * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
1785      * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
1786      * RecyclerView, State, int)}.
1787      * <p>
1788      * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
1789      * stop frozen.
1790      * <p>
1791      * Note: Running ItemAnimator is not stopped automatically,  it's caller's
1792      * responsibility to call ItemAnimator.end().
1793      *
1794      * @param frozen   true to freeze layout and scroll, false to re-enable.
1795      */
setLayoutFrozen(boolean frozen)1796     public void setLayoutFrozen(boolean frozen) {
1797         if (frozen != mLayoutFrozen) {
1798             assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
1799             if (!frozen) {
1800                 mLayoutFrozen = false;
1801                 if (mLayoutRequestEaten && mLayout != null && mAdapter != null) {
1802                     requestLayout();
1803                 }
1804                 mLayoutRequestEaten = false;
1805             } else {
1806                 final long now = SystemClock.uptimeMillis();
1807                 MotionEvent cancelEvent = MotionEvent.obtain(now, now,
1808                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
1809                 onTouchEvent(cancelEvent);
1810                 mLayoutFrozen = true;
1811                 mIgnoreMotionEventTillDown = true;
1812                 stopScroll();
1813             }
1814         }
1815     }
1816 
1817     /**
1818      * Returns true if layout and scroll are frozen.
1819      *
1820      * @return true if layout and scroll are frozen
1821      * @see #setLayoutFrozen(boolean)
1822      */
isLayoutFrozen()1823     public boolean isLayoutFrozen() {
1824         return mLayoutFrozen;
1825     }
1826 
1827     /**
1828      * Animate a scroll by the given amount of pixels along either axis.
1829      *
1830      * @param dx Pixels to scroll horizontally
1831      * @param dy Pixels to scroll vertically
1832      */
smoothScrollBy(int dx, int dy)1833     public void smoothScrollBy(int dx, int dy) {
1834         if (mLayout == null) {
1835             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
1836                     "Call setLayoutManager with a non-null argument.");
1837             return;
1838         }
1839         if (mLayoutFrozen) {
1840             return;
1841         }
1842         if (!mLayout.canScrollHorizontally()) {
1843             dx = 0;
1844         }
1845         if (!mLayout.canScrollVertically()) {
1846             dy = 0;
1847         }
1848         if (dx != 0 || dy != 0) {
1849             mViewFlinger.smoothScrollBy(dx, dy);
1850         }
1851     }
1852 
1853     /**
1854      * Begin a standard fling with an initial velocity along each axis in pixels per second.
1855      * If the velocity given is below the system-defined minimum this method will return false
1856      * and no fling will occur.
1857      *
1858      * @param velocityX Initial horizontal velocity in pixels per second
1859      * @param velocityY Initial vertical velocity in pixels per second
1860      * @return true if the fling was started, false if the velocity was too low to fling or
1861      * LayoutManager does not support scrolling in the axis fling is issued.
1862      *
1863      * @see LayoutManager#canScrollVertically()
1864      * @see LayoutManager#canScrollHorizontally()
1865      */
fling(int velocityX, int velocityY)1866     public boolean fling(int velocityX, int velocityY) {
1867         if (mLayout == null) {
1868             Log.e(TAG, "Cannot fling without a LayoutManager set. " +
1869                     "Call setLayoutManager with a non-null argument.");
1870             return false;
1871         }
1872         if (mLayoutFrozen) {
1873             return false;
1874         }
1875 
1876         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1877         final boolean canScrollVertical = mLayout.canScrollVertically();
1878 
1879         if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
1880             velocityX = 0;
1881         }
1882         if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
1883             velocityY = 0;
1884         }
1885         if (velocityX == 0 && velocityY == 0) {
1886             // If we don't have any velocity, return false
1887             return false;
1888         }
1889 
1890         if (!dispatchNestedPreFling(velocityX, velocityY)) {
1891             final boolean canScroll = canScrollHorizontal || canScrollVertical;
1892             dispatchNestedFling(velocityX, velocityY, canScroll);
1893 
1894             if (canScroll) {
1895                 velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
1896                 velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
1897                 mViewFlinger.fling(velocityX, velocityY);
1898                 return true;
1899             }
1900         }
1901         return false;
1902     }
1903 
1904     /**
1905      * Stop any current scroll in progress, such as one started by
1906      * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
1907      */
stopScroll()1908     public void stopScroll() {
1909         setScrollState(SCROLL_STATE_IDLE);
1910         stopScrollersInternal();
1911     }
1912 
1913     /**
1914      * Similar to {@link #stopScroll()} but does not set the state.
1915      */
stopScrollersInternal()1916     private void stopScrollersInternal() {
1917         mViewFlinger.stop();
1918         if (mLayout != null) {
1919             mLayout.stopSmoothScroller();
1920         }
1921     }
1922 
1923     /**
1924      * Returns the minimum velocity to start a fling.
1925      *
1926      * @return The minimum velocity to start a fling
1927      */
getMinFlingVelocity()1928     public int getMinFlingVelocity() {
1929         return mMinFlingVelocity;
1930     }
1931 
1932 
1933     /**
1934      * Returns the maximum fling velocity used by this RecyclerView.
1935      *
1936      * @return The maximum fling velocity used by this RecyclerView.
1937      */
getMaxFlingVelocity()1938     public int getMaxFlingVelocity() {
1939         return mMaxFlingVelocity;
1940     }
1941 
1942     /**
1943      * Apply a pull to relevant overscroll glow effects
1944      */
pullGlows(float x, float overscrollX, float y, float overscrollY)1945     private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
1946         boolean invalidate = false;
1947         if (overscrollX < 0) {
1948             ensureLeftGlow();
1949             if (mLeftGlow.onPull(-overscrollX / getWidth(), 1f - y  / getHeight())) {
1950                 invalidate = true;
1951             }
1952         } else if (overscrollX > 0) {
1953             ensureRightGlow();
1954             if (mRightGlow.onPull(overscrollX / getWidth(), y / getHeight())) {
1955                 invalidate = true;
1956             }
1957         }
1958 
1959         if (overscrollY < 0) {
1960             ensureTopGlow();
1961             if (mTopGlow.onPull(-overscrollY / getHeight(), x / getWidth())) {
1962                 invalidate = true;
1963             }
1964         } else if (overscrollY > 0) {
1965             ensureBottomGlow();
1966             if (mBottomGlow.onPull(overscrollY / getHeight(), 1f - x / getWidth())) {
1967                 invalidate = true;
1968             }
1969         }
1970 
1971         if (invalidate || overscrollX != 0 || overscrollY != 0) {
1972             ViewCompat.postInvalidateOnAnimation(this);
1973         }
1974     }
1975 
releaseGlows()1976     private void releaseGlows() {
1977         boolean needsInvalidate = false;
1978         if (mLeftGlow != null) needsInvalidate = mLeftGlow.onRelease();
1979         if (mTopGlow != null) needsInvalidate |= mTopGlow.onRelease();
1980         if (mRightGlow != null) needsInvalidate |= mRightGlow.onRelease();
1981         if (mBottomGlow != null) needsInvalidate |= mBottomGlow.onRelease();
1982         if (needsInvalidate) {
1983             ViewCompat.postInvalidateOnAnimation(this);
1984         }
1985     }
1986 
considerReleasingGlowsOnScroll(int dx, int dy)1987     private void considerReleasingGlowsOnScroll(int dx, int dy) {
1988         boolean needsInvalidate = false;
1989         if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
1990             needsInvalidate = mLeftGlow.onRelease();
1991         }
1992         if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
1993             needsInvalidate |= mRightGlow.onRelease();
1994         }
1995         if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
1996             needsInvalidate |= mTopGlow.onRelease();
1997         }
1998         if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
1999             needsInvalidate |= mBottomGlow.onRelease();
2000         }
2001         if (needsInvalidate) {
2002             ViewCompat.postInvalidateOnAnimation(this);
2003         }
2004     }
2005 
absorbGlows(int velocityX, int velocityY)2006     void absorbGlows(int velocityX, int velocityY) {
2007         if (velocityX < 0) {
2008             ensureLeftGlow();
2009             mLeftGlow.onAbsorb(-velocityX);
2010         } else if (velocityX > 0) {
2011             ensureRightGlow();
2012             mRightGlow.onAbsorb(velocityX);
2013         }
2014 
2015         if (velocityY < 0) {
2016             ensureTopGlow();
2017             mTopGlow.onAbsorb(-velocityY);
2018         } else if (velocityY > 0) {
2019             ensureBottomGlow();
2020             mBottomGlow.onAbsorb(velocityY);
2021         }
2022 
2023         if (velocityX != 0 || velocityY != 0) {
2024             ViewCompat.postInvalidateOnAnimation(this);
2025         }
2026     }
2027 
ensureLeftGlow()2028     void ensureLeftGlow() {
2029         if (mLeftGlow != null) {
2030             return;
2031         }
2032         mLeftGlow = new EdgeEffectCompat(getContext());
2033         if (mClipToPadding) {
2034             mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2035                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2036         } else {
2037             mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2038         }
2039     }
2040 
ensureRightGlow()2041     void ensureRightGlow() {
2042         if (mRightGlow != null) {
2043             return;
2044         }
2045         mRightGlow = new EdgeEffectCompat(getContext());
2046         if (mClipToPadding) {
2047             mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2048                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2049         } else {
2050             mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2051         }
2052     }
2053 
ensureTopGlow()2054     void ensureTopGlow() {
2055         if (mTopGlow != null) {
2056             return;
2057         }
2058         mTopGlow = new EdgeEffectCompat(getContext());
2059         if (mClipToPadding) {
2060             mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2061                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2062         } else {
2063             mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2064         }
2065 
2066     }
2067 
ensureBottomGlow()2068     void ensureBottomGlow() {
2069         if (mBottomGlow != null) {
2070             return;
2071         }
2072         mBottomGlow = new EdgeEffectCompat(getContext());
2073         if (mClipToPadding) {
2074             mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2075                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2076         } else {
2077             mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2078         }
2079     }
2080 
invalidateGlows()2081     void invalidateGlows() {
2082         mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
2083     }
2084 
2085     /**
2086      * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
2087      * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
2088      * that differs from other ViewGroups.
2089      * <p>
2090      * It first does a focus search within the RecyclerView. If this search finds a View that is in
2091      * the focus direction with respect to the currently focused View, RecyclerView returns that
2092      * child as the next focus target. When it cannot find such child, it calls
2093      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
2094      * in the focus search direction. If LayoutManager adds a View that matches the
2095      * focus search criteria, it will be returned as the focus search result. Otherwise,
2096      * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
2097      * <p>
2098      * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
2099      * is not in the focus direction is still valid focus target which may not be the desired
2100      * behavior if the Adapter has more children in the focus direction. To handle this case,
2101      * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
2102      * focus search in that direction. If there are no Views to gain focus, it will call
2103      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
2104      * focus search with the original (relative) direction. This allows RecyclerView to provide
2105      * better candidates to the focus search while still allowing the view system to take focus from
2106      * the RecyclerView and give it to a more suitable child if such child exists.
2107      *
2108      * @param focused The view that currently has focus
2109      * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
2110      * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
2111      * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
2112      *
2113      * @return A new View that can be the next focus after the focused View
2114      */
2115     @Override
focusSearch(View focused, int direction)2116     public View focusSearch(View focused, int direction) {
2117         View result = mLayout.onInterceptFocusSearch(focused, direction);
2118         if (result != null) {
2119             return result;
2120         }
2121         final boolean canRunFocusFailure = mAdapter != null && mLayout != null
2122                 && !isComputingLayout() && !mLayoutFrozen;
2123 
2124         final FocusFinder ff = FocusFinder.getInstance();
2125         if (canRunFocusFailure
2126                 && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
2127             // convert direction to absolute direction and see if we have a view there and if not
2128             // tell LayoutManager to add if it can.
2129             boolean needsFocusFailureLayout = false;
2130             if (mLayout.canScrollVertically()) {
2131                 final int absDir =
2132                         direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
2133                 final View found = ff.findNextFocus(this, focused, absDir);
2134                 needsFocusFailureLayout = found == null;
2135             }
2136             if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
2137                 boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
2138                 final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
2139                         ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2140                 final View found = ff.findNextFocus(this, focused, absDir);
2141                 needsFocusFailureLayout = found == null;
2142             }
2143             if (needsFocusFailureLayout) {
2144                 consumePendingUpdateOperations();
2145                 final View focusedItemView = findContainingItemView(focused);
2146                 if (focusedItemView == null) {
2147                     // panic, focused view is not a child anymore, cannot call super.
2148                     return null;
2149                 }
2150                 eatRequestLayout();
2151                 mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2152                 resumeRequestLayout(false);
2153             }
2154             result = ff.findNextFocus(this, focused, direction);
2155         } else {
2156             result = ff.findNextFocus(this, focused, direction);
2157             if (result == null && canRunFocusFailure) {
2158                 consumePendingUpdateOperations();
2159                 final View focusedItemView = findContainingItemView(focused);
2160                 if (focusedItemView == null) {
2161                     // panic, focused view is not a child anymore, cannot call super.
2162                     return null;
2163                 }
2164                 eatRequestLayout();
2165                 result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2166                 resumeRequestLayout(false);
2167             }
2168         }
2169         return isPreferredNextFocus(focused, result, direction)
2170                 ? result : super.focusSearch(focused, direction);
2171     }
2172 
2173     /**
2174      * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
2175      * assign it as the next focus View instead of letting view hierarchy decide.
2176      * A good candidate means a View that is aligned in the focus direction wrt the focused View
2177      * and is not the RecyclerView itself.
2178      * When this method returns false, RecyclerView will let the parent make the decision so the
2179      * same View may still get the focus as a result of that search.
2180      */
isPreferredNextFocus(View focused, View next, int direction)2181     private boolean isPreferredNextFocus(View focused, View next, int direction) {
2182         if (next == null || next == this) {
2183             return false;
2184         }
2185         if (focused == null) {
2186             return true;
2187         }
2188 
2189         if(direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
2190             final boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
2191             final int absHorizontal = (direction == View.FOCUS_FORWARD) ^ rtl
2192                     ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2193             if (isPreferredNextFocusAbsolute(focused, next, absHorizontal)) {
2194                 return true;
2195             }
2196             if (direction == View.FOCUS_FORWARD) {
2197                 return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_DOWN);
2198             } else {
2199                 return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_UP);
2200             }
2201         } else {
2202             return isPreferredNextFocusAbsolute(focused, next, direction);
2203         }
2204 
2205     }
2206 
2207     /**
2208      * Logic taken from FocusSarch#isCandidate
2209      */
isPreferredNextFocusAbsolute(View focused, View next, int direction)2210     private boolean isPreferredNextFocusAbsolute(View focused, View next, int direction) {
2211         mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2212         mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
2213         offsetDescendantRectToMyCoords(focused, mTempRect);
2214         offsetDescendantRectToMyCoords(next, mTempRect2);
2215         switch (direction) {
2216             case View.FOCUS_LEFT:
2217                 return (mTempRect.right > mTempRect2.right
2218                         || mTempRect.left >= mTempRect2.right)
2219                         && mTempRect.left > mTempRect2.left;
2220             case View.FOCUS_RIGHT:
2221                 return (mTempRect.left < mTempRect2.left
2222                         || mTempRect.right <= mTempRect2.left)
2223                         && mTempRect.right < mTempRect2.right;
2224             case View.FOCUS_UP:
2225                 return (mTempRect.bottom > mTempRect2.bottom
2226                         || mTempRect.top >= mTempRect2.bottom)
2227                         && mTempRect.top > mTempRect2.top;
2228             case View.FOCUS_DOWN:
2229                 return (mTempRect.top < mTempRect2.top
2230                         || mTempRect.bottom <= mTempRect2.top)
2231                         && mTempRect.bottom < mTempRect2.bottom;
2232         }
2233         throw new IllegalArgumentException("direction must be absolute. received:" + direction);
2234     }
2235 
2236     @Override
requestChildFocus(View child, View focused)2237     public void requestChildFocus(View child, View focused) {
2238         if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
2239             mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2240 
2241             // get item decor offsets w/o refreshing. If they are invalid, there will be another
2242             // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
2243             // View in viewport.
2244             final ViewGroup.LayoutParams focusedLayoutParams = focused.getLayoutParams();
2245             if (focusedLayoutParams instanceof LayoutParams) {
2246                 // if focused child has item decors, use them. Otherwise, ignore.
2247                 final LayoutParams lp = (LayoutParams) focusedLayoutParams;
2248                 if (!lp.mInsetsDirty) {
2249                     final Rect insets = lp.mDecorInsets;
2250                     mTempRect.left -= insets.left;
2251                     mTempRect.right += insets.right;
2252                     mTempRect.top -= insets.top;
2253                     mTempRect.bottom += insets.bottom;
2254                 }
2255             }
2256 
2257             offsetDescendantRectToMyCoords(focused, mTempRect);
2258             offsetRectIntoDescendantCoords(child, mTempRect);
2259             requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete);
2260         }
2261         super.requestChildFocus(child, focused);
2262     }
2263 
2264     @Override
requestChildRectangleOnScreen(View child, Rect rect, boolean immediate)2265     public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
2266         return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
2267     }
2268 
2269     @Override
addFocusables(ArrayList<View> views, int direction, int focusableMode)2270     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
2271         if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
2272             super.addFocusables(views, direction, focusableMode);
2273         }
2274     }
2275 
2276     @Override
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)2277     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
2278         if (isComputingLayout()) {
2279             // if we are in the middle of a layout calculation, don't let any child take focus.
2280             // RV will handle it after layout calculation is finished.
2281             return false;
2282         }
2283         return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
2284     }
2285 
2286     @Override
onAttachedToWindow()2287     protected void onAttachedToWindow() {
2288         super.onAttachedToWindow();
2289         mLayoutOrScrollCounter = 0;
2290         mIsAttached = true;
2291         mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
2292         if (mLayout != null) {
2293             mLayout.dispatchAttachedToWindow(this);
2294         }
2295         mPostedAnimatorRunner = false;
2296     }
2297 
2298     @Override
onDetachedFromWindow()2299     protected void onDetachedFromWindow() {
2300         super.onDetachedFromWindow();
2301         if (mItemAnimator != null) {
2302             mItemAnimator.endAnimations();
2303         }
2304         stopScroll();
2305         mIsAttached = false;
2306         if (mLayout != null) {
2307             mLayout.dispatchDetachedFromWindow(this, mRecycler);
2308         }
2309         removeCallbacks(mItemAnimatorRunner);
2310         mViewInfoStore.onDetach();
2311     }
2312 
2313     /**
2314      * Returns true if RecyclerView is attached to window.
2315      */
2316     // @override
isAttachedToWindow()2317     public boolean isAttachedToWindow() {
2318         return mIsAttached;
2319     }
2320 
2321     /**
2322      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2323      * {@link IllegalStateException} if it <b>is not</b>.
2324      *
2325      * @param message The message for the exception. Can be null.
2326      * @see #assertNotInLayoutOrScroll(String)
2327      */
assertInLayoutOrScroll(String message)2328     void assertInLayoutOrScroll(String message) {
2329         if (!isComputingLayout()) {
2330             if (message == null) {
2331                 throw new IllegalStateException("Cannot call this method unless RecyclerView is "
2332                         + "computing a layout or scrolling");
2333             }
2334             throw new IllegalStateException(message);
2335 
2336         }
2337     }
2338 
2339     /**
2340      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2341      * {@link IllegalStateException} if it <b>is</b>.
2342      *
2343      * @param message The message for the exception. Can be null.
2344      * @see #assertInLayoutOrScroll(String)
2345      */
assertNotInLayoutOrScroll(String message)2346     void assertNotInLayoutOrScroll(String message) {
2347         if (isComputingLayout()) {
2348             if (message == null) {
2349                 throw new IllegalStateException("Cannot call this method while RecyclerView is "
2350                         + "computing a layout or scrolling");
2351             }
2352             throw new IllegalStateException(message);
2353         }
2354     }
2355 
2356     /**
2357      * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
2358      * to child views or this view's standard scrolling behavior.
2359      *
2360      * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
2361      * returns true from
2362      * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
2363      * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
2364      * for each incoming MotionEvent until the end of the gesture.</p>
2365      *
2366      * @param listener Listener to add
2367      * @see SimpleOnItemTouchListener
2368      */
addOnItemTouchListener(OnItemTouchListener listener)2369     public void addOnItemTouchListener(OnItemTouchListener listener) {
2370         mOnItemTouchListeners.add(listener);
2371     }
2372 
2373     /**
2374      * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
2375      *
2376      * @param listener Listener to remove
2377      */
removeOnItemTouchListener(OnItemTouchListener listener)2378     public void removeOnItemTouchListener(OnItemTouchListener listener) {
2379         mOnItemTouchListeners.remove(listener);
2380         if (mActiveOnItemTouchListener == listener) {
2381             mActiveOnItemTouchListener = null;
2382         }
2383     }
2384 
dispatchOnItemTouchIntercept(MotionEvent e)2385     private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
2386         final int action = e.getAction();
2387         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
2388             mActiveOnItemTouchListener = null;
2389         }
2390 
2391         final int listenerCount = mOnItemTouchListeners.size();
2392         for (int i = 0; i < listenerCount; i++) {
2393             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2394             if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
2395                 mActiveOnItemTouchListener = listener;
2396                 return true;
2397             }
2398         }
2399         return false;
2400     }
2401 
dispatchOnItemTouch(MotionEvent e)2402     private boolean dispatchOnItemTouch(MotionEvent e) {
2403         final int action = e.getAction();
2404         if (mActiveOnItemTouchListener != null) {
2405             if (action == MotionEvent.ACTION_DOWN) {
2406                 // Stale state from a previous gesture, we're starting a new one. Clear it.
2407                 mActiveOnItemTouchListener = null;
2408             } else {
2409                 mActiveOnItemTouchListener.onTouchEvent(this, e);
2410                 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
2411                     // Clean up for the next gesture.
2412                     mActiveOnItemTouchListener = null;
2413                 }
2414                 return true;
2415             }
2416         }
2417 
2418         // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
2419         // as called from onInterceptTouchEvent; skip it.
2420         if (action != MotionEvent.ACTION_DOWN) {
2421             final int listenerCount = mOnItemTouchListeners.size();
2422             for (int i = 0; i < listenerCount; i++) {
2423                 final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2424                 if (listener.onInterceptTouchEvent(this, e)) {
2425                     mActiveOnItemTouchListener = listener;
2426                     return true;
2427                 }
2428             }
2429         }
2430         return false;
2431     }
2432 
2433     @Override
onInterceptTouchEvent(MotionEvent e)2434     public boolean onInterceptTouchEvent(MotionEvent e) {
2435         if (mLayoutFrozen) {
2436             // When layout is frozen,  RV does not intercept the motion event.
2437             // A child view e.g. a button may still get the click.
2438             return false;
2439         }
2440         if (dispatchOnItemTouchIntercept(e)) {
2441             cancelTouch();
2442             return true;
2443         }
2444 
2445         if (mLayout == null) {
2446             return false;
2447         }
2448 
2449         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2450         final boolean canScrollVertically = mLayout.canScrollVertically();
2451 
2452         if (mVelocityTracker == null) {
2453             mVelocityTracker = VelocityTracker.obtain();
2454         }
2455         mVelocityTracker.addMovement(e);
2456 
2457         final int action = MotionEventCompat.getActionMasked(e);
2458         final int actionIndex = MotionEventCompat.getActionIndex(e);
2459 
2460         switch (action) {
2461             case MotionEvent.ACTION_DOWN:
2462                 if (mIgnoreMotionEventTillDown) {
2463                     mIgnoreMotionEventTillDown = false;
2464                 }
2465                 mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
2466                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2467                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2468 
2469                 if (mScrollState == SCROLL_STATE_SETTLING) {
2470                     getParent().requestDisallowInterceptTouchEvent(true);
2471                     setScrollState(SCROLL_STATE_DRAGGING);
2472                 }
2473 
2474                 // Clear the nested offsets
2475                 mNestedOffsets[0] = mNestedOffsets[1] = 0;
2476 
2477                 int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2478                 if (canScrollHorizontally) {
2479                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2480                 }
2481                 if (canScrollVertically) {
2482                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2483                 }
2484                 startNestedScroll(nestedScrollAxis);
2485                 break;
2486 
2487             case MotionEventCompat.ACTION_POINTER_DOWN:
2488                 mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);
2489                 mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);
2490                 mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);
2491                 break;
2492 
2493             case MotionEvent.ACTION_MOVE: {
2494                 final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
2495                 if (index < 0) {
2496                     Log.e(TAG, "Error processing scroll; pointer index for id " +
2497                             mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2498                     return false;
2499                 }
2500 
2501                 final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
2502                 final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);
2503                 if (mScrollState != SCROLL_STATE_DRAGGING) {
2504                     final int dx = x - mInitialTouchX;
2505                     final int dy = y - mInitialTouchY;
2506                     boolean startScroll = false;
2507                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2508                         mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
2509                         startScroll = true;
2510                     }
2511                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2512                         mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
2513                         startScroll = true;
2514                     }
2515                     if (startScroll) {
2516                         setScrollState(SCROLL_STATE_DRAGGING);
2517                     }
2518                 }
2519             } break;
2520 
2521             case MotionEventCompat.ACTION_POINTER_UP: {
2522                 onPointerUp(e);
2523             } break;
2524 
2525             case MotionEvent.ACTION_UP: {
2526                 mVelocityTracker.clear();
2527                 stopNestedScroll();
2528             } break;
2529 
2530             case MotionEvent.ACTION_CANCEL: {
2531                 cancelTouch();
2532             }
2533         }
2534         return mScrollState == SCROLL_STATE_DRAGGING;
2535     }
2536 
2537     @Override
requestDisallowInterceptTouchEvent(boolean disallowIntercept)2538     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2539         final int listenerCount = mOnItemTouchListeners.size();
2540         for (int i = 0; i < listenerCount; i++) {
2541             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2542             listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
2543         }
2544         super.requestDisallowInterceptTouchEvent(disallowIntercept);
2545     }
2546 
2547     @Override
onTouchEvent(MotionEvent e)2548     public boolean onTouchEvent(MotionEvent e) {
2549         if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
2550             return false;
2551         }
2552         if (dispatchOnItemTouch(e)) {
2553             cancelTouch();
2554             return true;
2555         }
2556 
2557         if (mLayout == null) {
2558             return false;
2559         }
2560 
2561         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2562         final boolean canScrollVertically = mLayout.canScrollVertically();
2563 
2564         if (mVelocityTracker == null) {
2565             mVelocityTracker = VelocityTracker.obtain();
2566         }
2567         boolean eventAddedToVelocityTracker = false;
2568 
2569         final MotionEvent vtev = MotionEvent.obtain(e);
2570         final int action = MotionEventCompat.getActionMasked(e);
2571         final int actionIndex = MotionEventCompat.getActionIndex(e);
2572 
2573         if (action == MotionEvent.ACTION_DOWN) {
2574             mNestedOffsets[0] = mNestedOffsets[1] = 0;
2575         }
2576         vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
2577 
2578         switch (action) {
2579             case MotionEvent.ACTION_DOWN: {
2580                 mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
2581                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2582                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2583 
2584                 int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2585                 if (canScrollHorizontally) {
2586                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2587                 }
2588                 if (canScrollVertically) {
2589                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2590                 }
2591                 startNestedScroll(nestedScrollAxis);
2592             } break;
2593 
2594             case MotionEventCompat.ACTION_POINTER_DOWN: {
2595                 mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);
2596                 mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);
2597                 mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);
2598             } break;
2599 
2600             case MotionEvent.ACTION_MOVE: {
2601                 final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
2602                 if (index < 0) {
2603                     Log.e(TAG, "Error processing scroll; pointer index for id " +
2604                             mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2605                     return false;
2606                 }
2607 
2608                 final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
2609                 final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);
2610                 int dx = mLastTouchX - x;
2611                 int dy = mLastTouchY - y;
2612 
2613                 if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
2614                     dx -= mScrollConsumed[0];
2615                     dy -= mScrollConsumed[1];
2616                     vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
2617                     // Updated the nested offsets
2618                     mNestedOffsets[0] += mScrollOffset[0];
2619                     mNestedOffsets[1] += mScrollOffset[1];
2620                 }
2621 
2622                 if (mScrollState != SCROLL_STATE_DRAGGING) {
2623                     boolean startScroll = false;
2624                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2625                         if (dx > 0) {
2626                             dx -= mTouchSlop;
2627                         } else {
2628                             dx += mTouchSlop;
2629                         }
2630                         startScroll = true;
2631                     }
2632                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2633                         if (dy > 0) {
2634                             dy -= mTouchSlop;
2635                         } else {
2636                             dy += mTouchSlop;
2637                         }
2638                         startScroll = true;
2639                     }
2640                     if (startScroll) {
2641                         setScrollState(SCROLL_STATE_DRAGGING);
2642                     }
2643                 }
2644 
2645                 if (mScrollState == SCROLL_STATE_DRAGGING) {
2646                     mLastTouchX = x - mScrollOffset[0];
2647                     mLastTouchY = y - mScrollOffset[1];
2648 
2649                     if (scrollByInternal(
2650                             canScrollHorizontally ? dx : 0,
2651                             canScrollVertically ? dy : 0,
2652                             vtev)) {
2653                         getParent().requestDisallowInterceptTouchEvent(true);
2654                     }
2655                 }
2656             } break;
2657 
2658             case MotionEventCompat.ACTION_POINTER_UP: {
2659                 onPointerUp(e);
2660             } break;
2661 
2662             case MotionEvent.ACTION_UP: {
2663                 mVelocityTracker.addMovement(vtev);
2664                 eventAddedToVelocityTracker = true;
2665                 mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
2666                 final float xvel = canScrollHorizontally ?
2667                         -VelocityTrackerCompat.getXVelocity(mVelocityTracker, mScrollPointerId) : 0;
2668                 final float yvel = canScrollVertically ?
2669                         -VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId) : 0;
2670                 if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
2671                     setScrollState(SCROLL_STATE_IDLE);
2672                 }
2673                 resetTouch();
2674             } break;
2675 
2676             case MotionEvent.ACTION_CANCEL: {
2677                 cancelTouch();
2678             } break;
2679         }
2680 
2681         if (!eventAddedToVelocityTracker) {
2682             mVelocityTracker.addMovement(vtev);
2683         }
2684         vtev.recycle();
2685 
2686         return true;
2687     }
2688 
resetTouch()2689     private void resetTouch() {
2690         if (mVelocityTracker != null) {
2691             mVelocityTracker.clear();
2692         }
2693         stopNestedScroll();
2694         releaseGlows();
2695     }
2696 
cancelTouch()2697     private void cancelTouch() {
2698         resetTouch();
2699         setScrollState(SCROLL_STATE_IDLE);
2700     }
2701 
onPointerUp(MotionEvent e)2702     private void onPointerUp(MotionEvent e) {
2703         final int actionIndex = MotionEventCompat.getActionIndex(e);
2704         if (MotionEventCompat.getPointerId(e, actionIndex) == mScrollPointerId) {
2705             // Pick a new pointer to pick up the slack.
2706             final int newIndex = actionIndex == 0 ? 1 : 0;
2707             mScrollPointerId = MotionEventCompat.getPointerId(e, newIndex);
2708             mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, newIndex) + 0.5f);
2709             mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, newIndex) + 0.5f);
2710         }
2711     }
2712 
2713     // @Override
onGenericMotionEvent(MotionEvent event)2714     public boolean onGenericMotionEvent(MotionEvent event) {
2715         if (mLayout == null) {
2716             return false;
2717         }
2718         if (mLayoutFrozen) {
2719             return false;
2720         }
2721         if ((MotionEventCompat.getSource(event) & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
2722             if (event.getAction() == MotionEventCompat.ACTION_SCROLL) {
2723                 final float vScroll, hScroll;
2724                 if (mLayout.canScrollVertically()) {
2725                     // Inverse the sign of the vertical scroll to align the scroll orientation
2726                     // with AbsListView.
2727                     vScroll = -MotionEventCompat
2728                             .getAxisValue(event, MotionEventCompat.AXIS_VSCROLL);
2729                 } else {
2730                     vScroll = 0f;
2731                 }
2732                 if (mLayout.canScrollHorizontally()) {
2733                     hScroll = MotionEventCompat
2734                             .getAxisValue(event, MotionEventCompat.AXIS_HSCROLL);
2735                 } else {
2736                     hScroll = 0f;
2737                 }
2738 
2739                 if (vScroll != 0 || hScroll != 0) {
2740                     final float scrollFactor = getScrollFactor();
2741                     scrollByInternal((int) (hScroll * scrollFactor),
2742                             (int) (vScroll * scrollFactor), event);
2743                 }
2744             }
2745         }
2746         return false;
2747     }
2748 
2749     /**
2750      * Ported from View.getVerticalScrollFactor.
2751      */
getScrollFactor()2752     private float getScrollFactor() {
2753         if (mScrollFactor == Float.MIN_VALUE) {
2754             TypedValue outValue = new TypedValue();
2755             if (getContext().getTheme().resolveAttribute(
2756                     android.R.attr.listPreferredItemHeight, outValue, true)) {
2757                 mScrollFactor = outValue.getDimension(
2758                         getContext().getResources().getDisplayMetrics());
2759             } else {
2760                 return 0; //listPreferredItemHeight is not defined, no generic scrolling
2761             }
2762         }
2763         return mScrollFactor;
2764     }
2765 
2766     @Override
onMeasure(int widthSpec, int heightSpec)2767     protected void onMeasure(int widthSpec, int heightSpec) {
2768         if (mLayout == null) {
2769             defaultOnMeasure(widthSpec, heightSpec);
2770             return;
2771         }
2772         if (mLayout.mAutoMeasure) {
2773             final int widthMode = MeasureSpec.getMode(widthSpec);
2774             final int heightMode = MeasureSpec.getMode(heightSpec);
2775             final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
2776                     && heightMode == MeasureSpec.EXACTLY;
2777             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
2778             if (skipMeasure || mAdapter == null) {
2779                 return;
2780             }
2781             if (mState.mLayoutStep == State.STEP_START) {
2782                 dispatchLayoutStep1();
2783             }
2784             // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
2785             // consistency
2786             mLayout.setMeasureSpecs(widthSpec, heightSpec);
2787             mState.mIsMeasuring = true;
2788             dispatchLayoutStep2();
2789 
2790             // now we can get the width and height from the children.
2791             mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
2792 
2793             // if RecyclerView has non-exact width and height and if there is at least one child
2794             // which also has non-exact width & height, we have to re-measure.
2795             if (mLayout.shouldMeasureTwice()) {
2796                 mLayout.setMeasureSpecs(
2797                         MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
2798                         MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
2799                 mState.mIsMeasuring = true;
2800                 dispatchLayoutStep2();
2801                 // now we can get the width and height from the children.
2802                 mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
2803             }
2804         } else {
2805             if (mHasFixedSize) {
2806                 mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
2807                 return;
2808             }
2809             // custom onMeasure
2810             if (mAdapterUpdateDuringMeasure) {
2811                 eatRequestLayout();
2812                 processAdapterUpdatesAndSetAnimationFlags();
2813 
2814                 if (mState.mRunPredictiveAnimations) {
2815                     mState.mInPreLayout = true;
2816                 } else {
2817                     // consume remaining updates to provide a consistent state with the layout pass.
2818                     mAdapterHelper.consumeUpdatesInOnePass();
2819                     mState.mInPreLayout = false;
2820                 }
2821                 mAdapterUpdateDuringMeasure = false;
2822                 resumeRequestLayout(false);
2823             }
2824 
2825             if (mAdapter != null) {
2826                 mState.mItemCount = mAdapter.getItemCount();
2827             } else {
2828                 mState.mItemCount = 0;
2829             }
2830             eatRequestLayout();
2831             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
2832             resumeRequestLayout(false);
2833             mState.mInPreLayout = false; // clear
2834         }
2835     }
2836 
2837     /**
2838      * Used when onMeasure is called before layout manager is set
2839      */
defaultOnMeasure(int widthSpec, int heightSpec)2840     void defaultOnMeasure(int widthSpec, int heightSpec) {
2841         // calling LayoutManager here is not pretty but that API is already public and it is better
2842         // than creating another method since this is internal.
2843         final int width = LayoutManager.chooseSize(widthSpec,
2844                 getPaddingLeft() + getPaddingRight(),
2845                 ViewCompat.getMinimumWidth(this));
2846         final int height = LayoutManager.chooseSize(heightSpec,
2847                 getPaddingTop() + getPaddingBottom(),
2848                 ViewCompat.getMinimumHeight(this));
2849 
2850         setMeasuredDimension(width, height);
2851     }
2852 
2853     @Override
onSizeChanged(int w, int h, int oldw, int oldh)2854     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
2855         super.onSizeChanged(w, h, oldw, oldh);
2856         if (w != oldw || h != oldh) {
2857             invalidateGlows();
2858             // layout's w/h are updated during measure/layout steps.
2859         }
2860     }
2861 
2862     /**
2863      * Sets the {@link ItemAnimator} that will handle animations involving changes
2864      * to the items in this RecyclerView. By default, RecyclerView instantiates and
2865      * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
2866      * enabled for the RecyclerView depends on the ItemAnimator and whether
2867      * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
2868      * supports item animations}.
2869      *
2870      * @param animator The ItemAnimator being set. If null, no animations will occur
2871      * when changes occur to the items in this RecyclerView.
2872      */
setItemAnimator(ItemAnimator animator)2873     public void setItemAnimator(ItemAnimator animator) {
2874         if (mItemAnimator != null) {
2875             mItemAnimator.endAnimations();
2876             mItemAnimator.setListener(null);
2877         }
2878         mItemAnimator = animator;
2879         if (mItemAnimator != null) {
2880             mItemAnimator.setListener(mItemAnimatorListener);
2881         }
2882     }
2883 
onEnterLayoutOrScroll()2884     private void onEnterLayoutOrScroll() {
2885         mLayoutOrScrollCounter ++;
2886     }
2887 
onExitLayoutOrScroll()2888     private void onExitLayoutOrScroll() {
2889         mLayoutOrScrollCounter --;
2890         if (mLayoutOrScrollCounter < 1) {
2891             if (DEBUG && mLayoutOrScrollCounter < 0) {
2892                 throw new IllegalStateException("layout or scroll counter cannot go below zero."
2893                         + "Some calls are not matching");
2894             }
2895             mLayoutOrScrollCounter = 0;
2896             dispatchContentChangedIfNecessary();
2897         }
2898     }
2899 
isAccessibilityEnabled()2900     boolean isAccessibilityEnabled() {
2901         return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
2902     }
2903 
dispatchContentChangedIfNecessary()2904     private void dispatchContentChangedIfNecessary() {
2905         final int flags = mEatenAccessibilityChangeFlags;
2906         mEatenAccessibilityChangeFlags = 0;
2907         if (flags != 0 && isAccessibilityEnabled()) {
2908             final AccessibilityEvent event = AccessibilityEvent.obtain();
2909             event.setEventType(AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
2910             AccessibilityEventCompat.setContentChangeTypes(event, flags);
2911             sendAccessibilityEventUnchecked(event);
2912         }
2913     }
2914 
2915     /**
2916      * Returns whether RecyclerView is currently computing a layout.
2917      * <p>
2918      * If this method returns true, it means that RecyclerView is in a lockdown state and any
2919      * attempt to update adapter contents will result in an exception because adapter contents
2920      * cannot be changed while RecyclerView is trying to compute the layout.
2921      * <p>
2922      * It is very unlikely that your code will be running during this state as it is
2923      * called by the framework when a layout traversal happens or RecyclerView starts to scroll
2924      * in response to system events (touch, accessibility etc).
2925      * <p>
2926      * This case may happen if you have some custom logic to change adapter contents in
2927      * response to a View callback (e.g. focus change callback) which might be triggered during a
2928      * layout calculation. In these cases, you should just postpone the change using a Handler or a
2929      * similar mechanism.
2930      *
2931      * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
2932      *         otherwise
2933      */
isComputingLayout()2934     public boolean isComputingLayout() {
2935         return mLayoutOrScrollCounter > 0;
2936     }
2937 
2938     /**
2939      * Returns true if an accessibility event should not be dispatched now. This happens when an
2940      * accessibility request arrives while RecyclerView does not have a stable state which is very
2941      * hard to handle for a LayoutManager. Instead, this method records necessary information about
2942      * the event and dispatches a window change event after the critical section is finished.
2943      *
2944      * @return True if the accessibility event should be postponed.
2945      */
shouldDeferAccessibilityEvent(AccessibilityEvent event)2946     boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
2947         if (isComputingLayout()) {
2948             int type = 0;
2949             if (event != null) {
2950                 type = AccessibilityEventCompat.getContentChangeTypes(event);
2951             }
2952             if (type == 0) {
2953                 type = AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED;
2954             }
2955             mEatenAccessibilityChangeFlags |= type;
2956             return true;
2957         }
2958         return false;
2959     }
2960 
2961     @Override
sendAccessibilityEventUnchecked(AccessibilityEvent event)2962     public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
2963         if (shouldDeferAccessibilityEvent(event)) {
2964             return;
2965         }
2966         super.sendAccessibilityEventUnchecked(event);
2967     }
2968 
2969     /**
2970      * Gets the current ItemAnimator for this RecyclerView. A null return value
2971      * indicates that there is no animator and that item changes will happen without
2972      * any animations. By default, RecyclerView instantiates and
2973      * uses an instance of {@link DefaultItemAnimator}.
2974      *
2975      * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
2976      * when changes occur to the items in this RecyclerView.
2977      */
getItemAnimator()2978     public ItemAnimator getItemAnimator() {
2979         return mItemAnimator;
2980     }
2981 
2982     /**
2983      * Post a runnable to the next frame to run pending item animations. Only the first such
2984      * request will be posted, governed by the mPostedAnimatorRunner flag.
2985      */
postAnimationRunner()2986     private void postAnimationRunner() {
2987         if (!mPostedAnimatorRunner && mIsAttached) {
2988             ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
2989             mPostedAnimatorRunner = true;
2990         }
2991     }
2992 
predictiveItemAnimationsEnabled()2993     private boolean predictiveItemAnimationsEnabled() {
2994         return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
2995     }
2996 
2997     /**
2998      * Consumes adapter updates and calculates which type of animations we want to run.
2999      * Called in onMeasure and dispatchLayout.
3000      * <p>
3001      * This method may process only the pre-layout state of updates or all of them.
3002      */
processAdapterUpdatesAndSetAnimationFlags()3003     private void processAdapterUpdatesAndSetAnimationFlags() {
3004         if (mDataSetHasChangedAfterLayout) {
3005             // Processing these items have no value since data set changed unexpectedly.
3006             // Instead, we just reset it.
3007             mAdapterHelper.reset();
3008             markKnownViewsInvalid();
3009             mLayout.onItemsChanged(this);
3010         }
3011         // simple animations are a subset of advanced animations (which will cause a
3012         // pre-layout step)
3013         // If layout supports predictive animations, pre-process to decide if we want to run them
3014         if (predictiveItemAnimationsEnabled()) {
3015             mAdapterHelper.preProcess();
3016         } else {
3017             mAdapterHelper.consumeUpdatesInOnePass();
3018         }
3019         boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
3020         mState.mRunSimpleAnimations = mFirstLayoutComplete && mItemAnimator != null &&
3021                 (mDataSetHasChangedAfterLayout || animationTypeSupported ||
3022                         mLayout.mRequestedSimpleAnimations) &&
3023                 (!mDataSetHasChangedAfterLayout || mAdapter.hasStableIds());
3024         mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations &&
3025                 animationTypeSupported && !mDataSetHasChangedAfterLayout &&
3026                 predictiveItemAnimationsEnabled();
3027     }
3028 
3029     /**
3030      * Wrapper around layoutChildren() that handles animating changes caused by layout.
3031      * Animations work on the assumption that there are five different kinds of items
3032      * in play:
3033      * PERSISTENT: items are visible before and after layout
3034      * REMOVED: items were visible before layout and were removed by the app
3035      * ADDED: items did not exist before layout and were added by the app
3036      * DISAPPEARING: items exist in the data set before/after, but changed from
3037      * visible to non-visible in the process of layout (they were moved off
3038      * screen as a side-effect of other changes)
3039      * APPEARING: items exist in the data set before/after, but changed from
3040      * non-visible to visible in the process of layout (they were moved on
3041      * screen as a side-effect of other changes)
3042      * The overall approach figures out what items exist before/after layout and
3043      * infers one of the five above states for each of the items. Then the animations
3044      * are set up accordingly:
3045      * PERSISTENT views are animated via
3046      * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3047      * DISAPPEARING views are animated via
3048      * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3049      * APPEARING views are animated via
3050      * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3051      * and changed views are animated via
3052      * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
3053      */
dispatchLayout()3054     void dispatchLayout() {
3055         if (mAdapter == null) {
3056             Log.e(TAG, "No adapter attached; skipping layout");
3057             // leave the state in START
3058             return;
3059         }
3060         if (mLayout == null) {
3061             Log.e(TAG, "No layout manager attached; skipping layout");
3062             // leave the state in START
3063             return;
3064         }
3065         mState.mIsMeasuring = false;
3066         if (mState.mLayoutStep == State.STEP_START) {
3067             dispatchLayoutStep1();
3068             mLayout.setExactMeasureSpecsFrom(this);
3069             dispatchLayoutStep2();
3070         } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth() ||
3071                 mLayout.getHeight() != getHeight()) {
3072             // First 2 steps are done in onMeasure but looks like we have to run again due to
3073             // changed size.
3074             mLayout.setExactMeasureSpecsFrom(this);
3075             dispatchLayoutStep2();
3076         } else {
3077             // always make sure we sync them (to ensure mode is exact)
3078             mLayout.setExactMeasureSpecsFrom(this);
3079         }
3080         dispatchLayoutStep3();
3081     }
3082 
saveFocusInfo()3083     private void saveFocusInfo() {
3084         View child = null;
3085         if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
3086             child = getFocusedChild();
3087         }
3088 
3089         final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
3090         if (focusedVh == null) {
3091             resetFocusInfo();
3092         } else {
3093             mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
3094             mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION :
3095                     focusedVh.getAdapterPosition();
3096             mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
3097         }
3098     }
3099 
resetFocusInfo()3100     private void resetFocusInfo() {
3101         mState.mFocusedItemId = NO_ID;
3102         mState.mFocusedItemPosition = NO_POSITION;
3103         mState.mFocusedSubChildId = View.NO_ID;
3104     }
3105 
recoverFocusFromState()3106     private void recoverFocusFromState() {
3107         if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()) {
3108             return;
3109         }
3110         // only recover focus if RV itself has the focus or the focused view is hidden
3111         if (!isFocused()) {
3112             final View focusedChild = getFocusedChild();
3113             if (focusedChild == null || !mChildHelper.isHidden(focusedChild)) {
3114                 return;
3115             }
3116         }
3117         ViewHolder focusTarget = null;
3118         if (mState.mFocusedItemPosition != NO_POSITION) {
3119             focusTarget = findViewHolderForAdapterPosition(mState.mFocusedItemPosition);
3120         }
3121         if (focusTarget == null && mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
3122             focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
3123         }
3124         if (focusTarget == null || focusTarget.itemView.hasFocus() ||
3125                 !focusTarget.itemView.hasFocusable()) {
3126             return;
3127         }
3128         // looks like the focused item has been replaced with another view that represents the
3129         // same item in the adapter. Request focus on that.
3130         View viewToFocus = focusTarget.itemView;
3131         if (mState.mFocusedSubChildId != NO_ID) {
3132             View child = focusTarget.itemView.findViewById(mState.mFocusedSubChildId);
3133             if (child != null && child.isFocusable()) {
3134                 viewToFocus = child;
3135             }
3136         }
3137         viewToFocus.requestFocus();
3138     }
3139 
getDeepestFocusedViewWithId(View view)3140     private int getDeepestFocusedViewWithId(View view) {
3141         int lastKnownId = view.getId();
3142         while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
3143             view = ((ViewGroup) view).getFocusedChild();
3144             final int id = view.getId();
3145             if (id != View.NO_ID) {
3146                 lastKnownId = view.getId();
3147             }
3148         }
3149         return lastKnownId;
3150     }
3151 
3152     /**
3153      * The first step of a layout where we;
3154      * - process adapter updates
3155      * - decide which animation should run
3156      * - save information about current views
3157      * - If necessary, run predictive layout and save its information
3158      */
dispatchLayoutStep1()3159     private void dispatchLayoutStep1() {
3160         mState.assertLayoutStep(State.STEP_START);
3161         mState.mIsMeasuring = false;
3162         eatRequestLayout();
3163         mViewInfoStore.clear();
3164         onEnterLayoutOrScroll();
3165         saveFocusInfo();
3166         processAdapterUpdatesAndSetAnimationFlags();
3167         mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
3168         mItemsAddedOrRemoved = mItemsChanged = false;
3169         mState.mInPreLayout = mState.mRunPredictiveAnimations;
3170         mState.mItemCount = mAdapter.getItemCount();
3171         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3172 
3173         if (mState.mRunSimpleAnimations) {
3174             // Step 0: Find out where all non-removed items are, pre-layout
3175             int count = mChildHelper.getChildCount();
3176             for (int i = 0; i < count; ++i) {
3177                 final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3178                 if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
3179                     continue;
3180                 }
3181                 final ItemHolderInfo animationInfo = mItemAnimator
3182                         .recordPreLayoutInformation(mState, holder,
3183                                 ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
3184                                 holder.getUnmodifiedPayloads());
3185                 mViewInfoStore.addToPreLayout(holder, animationInfo);
3186                 if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
3187                         && !holder.shouldIgnore() && !holder.isInvalid()) {
3188                     long key = getChangedHolderKey(holder);
3189                     // This is NOT the only place where a ViewHolder is added to old change holders
3190                     // list. There is another case where:
3191                     //    * A VH is currently hidden but not deleted
3192                     //    * The hidden item is changed in the adapter
3193                     //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
3194                     // When this case is detected, RV will un-hide that view and add to the old
3195                     // change holders list.
3196                     mViewInfoStore.addToOldChangeHolders(key, holder);
3197                 }
3198             }
3199         }
3200         if (mState.mRunPredictiveAnimations) {
3201             // Step 1: run prelayout: This will use the old positions of items. The layout manager
3202             // is expected to layout everything, even removed items (though not to add removed
3203             // items back to the container). This gives the pre-layout position of APPEARING views
3204             // which come into existence as part of the real layout.
3205 
3206             // Save old positions so that LayoutManager can run its mapping logic.
3207             saveOldPositions();
3208             final boolean didStructureChange = mState.mStructureChanged;
3209             mState.mStructureChanged = false;
3210             // temporarily disable flag because we are asking for previous layout
3211             mLayout.onLayoutChildren(mRecycler, mState);
3212             mState.mStructureChanged = didStructureChange;
3213 
3214             for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
3215                 final View child = mChildHelper.getChildAt(i);
3216                 final ViewHolder viewHolder = getChildViewHolderInt(child);
3217                 if (viewHolder.shouldIgnore()) {
3218                     continue;
3219                 }
3220                 if (!mViewInfoStore.isInPreLayout(viewHolder)) {
3221                     int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
3222                     boolean wasHidden = viewHolder
3223                             .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3224                     if (!wasHidden) {
3225                         flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
3226                     }
3227                     final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
3228                             mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
3229                     if (wasHidden) {
3230                         recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
3231                     } else {
3232                         mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
3233                     }
3234                 }
3235             }
3236             // we don't process disappearing list because they may re-appear in post layout pass.
3237             clearOldPositions();
3238         } else {
3239             clearOldPositions();
3240         }
3241         onExitLayoutOrScroll();
3242         resumeRequestLayout(false);
3243         mState.mLayoutStep = State.STEP_LAYOUT;
3244     }
3245 
3246     /**
3247      * The second layout step where we do the actual layout of the views for the final state.
3248      * This step might be run multiple times if necessary (e.g. measure).
3249      */
dispatchLayoutStep2()3250     private void dispatchLayoutStep2() {
3251         eatRequestLayout();
3252         onEnterLayoutOrScroll();
3253         mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
3254         mAdapterHelper.consumeUpdatesInOnePass();
3255         mState.mItemCount = mAdapter.getItemCount();
3256         mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
3257 
3258         // Step 2: Run layout
3259         mState.mInPreLayout = false;
3260         mLayout.onLayoutChildren(mRecycler, mState);
3261 
3262         mState.mStructureChanged = false;
3263         mPendingSavedState = null;
3264 
3265         // onLayoutChildren may have caused client code to disable item animations; re-check
3266         mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
3267         mState.mLayoutStep = State.STEP_ANIMATIONS;
3268         onExitLayoutOrScroll();
3269         resumeRequestLayout(false);
3270     }
3271 
3272     /**
3273      * The final step of the layout where we save the information about views for animations,
3274      * trigger animations and do any necessary cleanup.
3275      */
dispatchLayoutStep3()3276     private void dispatchLayoutStep3() {
3277         mState.assertLayoutStep(State.STEP_ANIMATIONS);
3278         eatRequestLayout();
3279         onEnterLayoutOrScroll();
3280         mState.mLayoutStep = State.STEP_START;
3281         if (mState.mRunSimpleAnimations) {
3282             // Step 3: Find out where things are now, and process change animations.
3283             // traverse list in reverse because we may call animateChange in the loop which may
3284             // remove the target view holder.
3285             for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
3286                 ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3287                 if (holder.shouldIgnore()) {
3288                     continue;
3289                 }
3290                 long key = getChangedHolderKey(holder);
3291                 final ItemHolderInfo animationInfo = mItemAnimator
3292                         .recordPostLayoutInformation(mState, holder);
3293                 ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
3294                 if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
3295                     // run a change animation
3296 
3297                     // If an Item is CHANGED but the updated version is disappearing, it creates
3298                     // a conflicting case.
3299                     // Since a view that is marked as disappearing is likely to be going out of
3300                     // bounds, we run a change animation. Both views will be cleaned automatically
3301                     // once their animations finish.
3302                     // On the other hand, if it is the same view holder instance, we run a
3303                     // disappearing animation instead because we are not going to rebind the updated
3304                     // VH unless it is enforced by the layout manager.
3305                     final boolean oldDisappearing = mViewInfoStore.isDisappearing(
3306                             oldChangeViewHolder);
3307                     final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
3308                     if (oldDisappearing && oldChangeViewHolder == holder) {
3309                         // run disappear animation instead of change
3310                         mViewInfoStore.addToPostLayout(holder, animationInfo);
3311                     } else {
3312                         final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
3313                                 oldChangeViewHolder);
3314                         // we add and remove so that any post info is merged.
3315                         mViewInfoStore.addToPostLayout(holder, animationInfo);
3316                         ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
3317                         if (preInfo == null) {
3318                             handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
3319                         } else {
3320                             animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
3321                                     oldDisappearing, newDisappearing);
3322                         }
3323                     }
3324                 } else {
3325                     mViewInfoStore.addToPostLayout(holder, animationInfo);
3326                 }
3327             }
3328 
3329             // Step 4: Process view info lists and trigger animations
3330             mViewInfoStore.process(mViewInfoProcessCallback);
3331         }
3332 
3333         mLayout.removeAndRecycleScrapInt(mRecycler);
3334         mState.mPreviousLayoutItemCount = mState.mItemCount;
3335         mDataSetHasChangedAfterLayout = false;
3336         mState.mRunSimpleAnimations = false;
3337 
3338         mState.mRunPredictiveAnimations = false;
3339         mLayout.mRequestedSimpleAnimations = false;
3340         if (mRecycler.mChangedScrap != null) {
3341             mRecycler.mChangedScrap.clear();
3342         }
3343         mLayout.onLayoutCompleted(mState);
3344         onExitLayoutOrScroll();
3345         resumeRequestLayout(false);
3346         mViewInfoStore.clear();
3347         if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
3348             dispatchOnScrolled(0, 0);
3349         }
3350         recoverFocusFromState();
3351         resetFocusInfo();
3352     }
3353 
3354     /**
3355      * This handles the case where there is an unexpected VH missing in the pre-layout map.
3356      * <p>
3357      * We might be able to detect the error in the application which will help the developer to
3358      * resolve the issue.
3359      * <p>
3360      * If it is not an expected error, we at least print an error to notify the developer and ignore
3361      * the animation.
3362      *
3363      * https://code.google.com/p/android/issues/detail?id=193958
3364      *
3365      * @param key The change key
3366      * @param holder Current ViewHolder
3367      * @param oldChangeViewHolder Changed ViewHolder
3368      */
handleMissingPreInfoForChangeError(long key, ViewHolder holder, ViewHolder oldChangeViewHolder)3369     private void handleMissingPreInfoForChangeError(long key,
3370             ViewHolder holder, ViewHolder oldChangeViewHolder) {
3371         // check if two VH have the same key, if so, print that as an error
3372         final int childCount = mChildHelper.getChildCount();
3373         for (int i = 0; i < childCount; i++) {
3374             View view = mChildHelper.getChildAt(i);
3375             ViewHolder other = getChildViewHolderInt(view);
3376             if (other == holder) {
3377                 continue;
3378             }
3379             final long otherKey = getChangedHolderKey(other);
3380             if (otherKey == key) {
3381                 if (mAdapter != null && mAdapter.hasStableIds()) {
3382                     throw new IllegalStateException("Two different ViewHolders have the same stable"
3383                             + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
3384                             + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
3385                 } else {
3386                     throw new IllegalStateException("Two different ViewHolders have the same change"
3387                             + " ID. This might happen due to inconsistent Adapter update events or"
3388                             + " if the LayoutManager lays out the same View multiple times."
3389                             + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
3390                 }
3391             }
3392         }
3393         // Very unlikely to happen but if it does, notify the developer.
3394         Log.e(TAG, "Problem while matching changed view holders with the new"
3395                 + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
3396                 + " cannot be found but it is necessary for " + holder);
3397     }
3398 
3399     /**
3400      * Records the animation information for a view holder that was bounced from hidden list. It
3401      * also clears the bounce back flag.
3402      */
recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder, ItemHolderInfo animationInfo)3403     private void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
3404             ItemHolderInfo animationInfo) {
3405         // looks like this view bounced back from hidden list!
3406         viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3407         if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
3408                 && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
3409             long key = getChangedHolderKey(viewHolder);
3410             mViewInfoStore.addToOldChangeHolders(key, viewHolder);
3411         }
3412         mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
3413     }
3414 
findMinMaxChildLayoutPositions(int[] into)3415     private void findMinMaxChildLayoutPositions(int[] into) {
3416         final int count = mChildHelper.getChildCount();
3417         if (count == 0) {
3418             into[0] = 0;
3419             into[1] = 0;
3420             return;
3421         }
3422         int minPositionPreLayout = Integer.MAX_VALUE;
3423         int maxPositionPreLayout = Integer.MIN_VALUE;
3424         for (int i = 0; i < count; ++i) {
3425             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3426             if (holder.shouldIgnore()) {
3427                 continue;
3428             }
3429             final int pos = holder.getLayoutPosition();
3430             if (pos < minPositionPreLayout) {
3431                 minPositionPreLayout = pos;
3432             }
3433             if (pos > maxPositionPreLayout) {
3434                 maxPositionPreLayout = pos;
3435             }
3436         }
3437         into[0] = minPositionPreLayout;
3438         into[1] = maxPositionPreLayout;
3439     }
3440 
didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout)3441     private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
3442         int count = mChildHelper.getChildCount();
3443         if (count == 0) {
3444             return minPositionPreLayout != 0 || maxPositionPreLayout != 0;
3445         }
3446         // get the new min max
3447         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3448         return mMinMaxLayoutPositions[0] != minPositionPreLayout ||
3449                 mMinMaxLayoutPositions[1] != maxPositionPreLayout;
3450     }
3451 
3452     @Override
removeDetachedView(View child, boolean animate)3453     protected void removeDetachedView(View child, boolean animate) {
3454         ViewHolder vh = getChildViewHolderInt(child);
3455         if (vh != null) {
3456             if (vh.isTmpDetached()) {
3457                 vh.clearTmpDetachFlag();
3458             } else if (!vh.shouldIgnore()) {
3459                 throw new IllegalArgumentException("Called removeDetachedView with a view which"
3460                         + " is not flagged as tmp detached." + vh);
3461             }
3462         }
3463         dispatchChildDetached(child);
3464         super.removeDetachedView(child, animate);
3465     }
3466 
3467     /**
3468      * Returns a unique key to be used while handling change animations.
3469      * It might be child's position or stable id depending on the adapter type.
3470      */
getChangedHolderKey(ViewHolder holder)3471     long getChangedHolderKey(ViewHolder holder) {
3472         return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
3473     }
3474 
animateAppearance(@onNull ViewHolder itemHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)3475     private void animateAppearance(@NonNull ViewHolder itemHolder,
3476             @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
3477         itemHolder.setIsRecyclable(false);
3478         if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
3479             postAnimationRunner();
3480         }
3481     }
3482 
animateDisappearance(@onNull ViewHolder holder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo)3483     private void animateDisappearance(@NonNull ViewHolder holder,
3484             @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
3485         addAnimatingView(holder);
3486         holder.setIsRecyclable(false);
3487         if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
3488             postAnimationRunner();
3489         }
3490     }
3491 
animateChange(@onNull ViewHolder oldHolder, @NonNull ViewHolder newHolder, @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo, boolean oldHolderDisappearing, boolean newHolderDisappearing)3492     private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
3493             @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
3494             boolean oldHolderDisappearing, boolean newHolderDisappearing) {
3495         oldHolder.setIsRecyclable(false);
3496         if (oldHolderDisappearing) {
3497             addAnimatingView(oldHolder);
3498         }
3499         if (oldHolder != newHolder) {
3500             if (newHolderDisappearing) {
3501                 addAnimatingView(newHolder);
3502             }
3503             oldHolder.mShadowedHolder = newHolder;
3504             // old holder should disappear after animation ends
3505             addAnimatingView(oldHolder);
3506             mRecycler.unscrapView(oldHolder);
3507             newHolder.setIsRecyclable(false);
3508             newHolder.mShadowingHolder = oldHolder;
3509         }
3510         if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
3511             postAnimationRunner();
3512         }
3513     }
3514 
3515     @Override
onLayout(boolean changed, int l, int t, int r, int b)3516     protected void onLayout(boolean changed, int l, int t, int r, int b) {
3517         TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
3518         dispatchLayout();
3519         TraceCompat.endSection();
3520         mFirstLayoutComplete = true;
3521     }
3522 
3523     @Override
requestLayout()3524     public void requestLayout() {
3525         if (mEatRequestLayout == 0 && !mLayoutFrozen) {
3526             super.requestLayout();
3527         } else {
3528             mLayoutRequestEaten = true;
3529         }
3530     }
3531 
markItemDecorInsetsDirty()3532     void markItemDecorInsetsDirty() {
3533         final int childCount = mChildHelper.getUnfilteredChildCount();
3534         for (int i = 0; i < childCount; i++) {
3535             final View child = mChildHelper.getUnfilteredChildAt(i);
3536             ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
3537         }
3538         mRecycler.markItemDecorInsetsDirty();
3539     }
3540 
3541     @Override
draw(Canvas c)3542     public void draw(Canvas c) {
3543         super.draw(c);
3544 
3545         final int count = mItemDecorations.size();
3546         for (int i = 0; i < count; i++) {
3547             mItemDecorations.get(i).onDrawOver(c, this, mState);
3548         }
3549         // TODO If padding is not 0 and chilChildrenToPadding is false, to draw glows properly, we
3550         // need find children closest to edges. Not sure if it is worth the effort.
3551         boolean needsInvalidate = false;
3552         if (mLeftGlow != null && !mLeftGlow.isFinished()) {
3553             final int restore = c.save();
3554             final int padding = mClipToPadding ? getPaddingBottom() : 0;
3555             c.rotate(270);
3556             c.translate(-getHeight() + padding, 0);
3557             needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
3558             c.restoreToCount(restore);
3559         }
3560         if (mTopGlow != null && !mTopGlow.isFinished()) {
3561             final int restore = c.save();
3562             if (mClipToPadding) {
3563                 c.translate(getPaddingLeft(), getPaddingTop());
3564             }
3565             needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
3566             c.restoreToCount(restore);
3567         }
3568         if (mRightGlow != null && !mRightGlow.isFinished()) {
3569             final int restore = c.save();
3570             final int width = getWidth();
3571             final int padding = mClipToPadding ? getPaddingTop() : 0;
3572             c.rotate(90);
3573             c.translate(-padding, -width);
3574             needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
3575             c.restoreToCount(restore);
3576         }
3577         if (mBottomGlow != null && !mBottomGlow.isFinished()) {
3578             final int restore = c.save();
3579             c.rotate(180);
3580             if (mClipToPadding) {
3581                 c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
3582             } else {
3583                 c.translate(-getWidth(), -getHeight());
3584             }
3585             needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
3586             c.restoreToCount(restore);
3587         }
3588 
3589         // If some views are animating, ItemDecorators are likely to move/change with them.
3590         // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
3591         // display lists are not invalidated.
3592         if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0 &&
3593                 mItemAnimator.isRunning()) {
3594             needsInvalidate = true;
3595         }
3596 
3597         if (needsInvalidate) {
3598             ViewCompat.postInvalidateOnAnimation(this);
3599         }
3600     }
3601 
3602     @Override
onDraw(Canvas c)3603     public void onDraw(Canvas c) {
3604         super.onDraw(c);
3605 
3606         final int count = mItemDecorations.size();
3607         for (int i = 0; i < count; i++) {
3608             mItemDecorations.get(i).onDraw(c, this, mState);
3609         }
3610     }
3611 
3612     @Override
checkLayoutParams(ViewGroup.LayoutParams p)3613     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
3614         return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
3615     }
3616 
3617     @Override
generateDefaultLayoutParams()3618     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
3619         if (mLayout == null) {
3620             throw new IllegalStateException("RecyclerView has no LayoutManager");
3621         }
3622         return mLayout.generateDefaultLayoutParams();
3623     }
3624 
3625     @Override
generateLayoutParams(AttributeSet attrs)3626     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
3627         if (mLayout == null) {
3628             throw new IllegalStateException("RecyclerView has no LayoutManager");
3629         }
3630         return mLayout.generateLayoutParams(getContext(), attrs);
3631     }
3632 
3633     @Override
generateLayoutParams(ViewGroup.LayoutParams p)3634     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
3635         if (mLayout == null) {
3636             throw new IllegalStateException("RecyclerView has no LayoutManager");
3637         }
3638         return mLayout.generateLayoutParams(p);
3639     }
3640 
3641     /**
3642      * Returns true if RecyclerView is currently running some animations.
3643      * <p>
3644      * If you want to be notified when animations are finished, use
3645      * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
3646      *
3647      * @return True if there are some item animations currently running or waiting to be started.
3648      */
isAnimating()3649     public boolean isAnimating() {
3650         return mItemAnimator != null && mItemAnimator.isRunning();
3651     }
3652 
saveOldPositions()3653     void saveOldPositions() {
3654         final int childCount = mChildHelper.getUnfilteredChildCount();
3655         for (int i = 0; i < childCount; i++) {
3656             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3657             if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
3658                 throw new IllegalStateException("view holder cannot have position -1 unless it"
3659                         + " is removed");
3660             }
3661             if (!holder.shouldIgnore()) {
3662                 holder.saveOldPosition();
3663             }
3664         }
3665     }
3666 
clearOldPositions()3667     void clearOldPositions() {
3668         final int childCount = mChildHelper.getUnfilteredChildCount();
3669         for (int i = 0; i < childCount; i++) {
3670             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3671             if (!holder.shouldIgnore()) {
3672                 holder.clearOldPosition();
3673             }
3674         }
3675         mRecycler.clearOldPositions();
3676     }
3677 
offsetPositionRecordsForMove(int from, int to)3678     void offsetPositionRecordsForMove(int from, int to) {
3679         final int childCount = mChildHelper.getUnfilteredChildCount();
3680         final int start, end, inBetweenOffset;
3681         if (from < to) {
3682             start = from;
3683             end = to;
3684             inBetweenOffset = -1;
3685         } else {
3686             start = to;
3687             end = from;
3688             inBetweenOffset = 1;
3689         }
3690 
3691         for (int i = 0; i < childCount; i++) {
3692             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3693             if (holder == null || holder.mPosition < start || holder.mPosition > end) {
3694                 continue;
3695             }
3696             if (DEBUG) {
3697                 Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder " +
3698                         holder);
3699             }
3700             if (holder.mPosition == from) {
3701                 holder.offsetPosition(to - from, false);
3702             } else {
3703                 holder.offsetPosition(inBetweenOffset, false);
3704             }
3705 
3706             mState.mStructureChanged = true;
3707         }
3708         mRecycler.offsetPositionRecordsForMove(from, to);
3709         requestLayout();
3710     }
3711 
offsetPositionRecordsForInsert(int positionStart, int itemCount)3712     void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
3713         final int childCount = mChildHelper.getUnfilteredChildCount();
3714         for (int i = 0; i < childCount; i++) {
3715             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3716             if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
3717                 if (DEBUG) {
3718                     Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder " +
3719                             holder + " now at position " + (holder.mPosition + itemCount));
3720                 }
3721                 holder.offsetPosition(itemCount, false);
3722                 mState.mStructureChanged = true;
3723             }
3724         }
3725         mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
3726         requestLayout();
3727     }
3728 
offsetPositionRecordsForRemove(int positionStart, int itemCount, boolean applyToPreLayout)3729     void offsetPositionRecordsForRemove(int positionStart, int itemCount,
3730             boolean applyToPreLayout) {
3731         final int positionEnd = positionStart + itemCount;
3732         final int childCount = mChildHelper.getUnfilteredChildCount();
3733         for (int i = 0; i < childCount; i++) {
3734             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3735             if (holder != null && !holder.shouldIgnore()) {
3736                 if (holder.mPosition >= positionEnd) {
3737                     if (DEBUG) {
3738                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +
3739                                 " holder " + holder + " now at position " +
3740                                 (holder.mPosition - itemCount));
3741                     }
3742                     holder.offsetPosition(-itemCount, applyToPreLayout);
3743                     mState.mStructureChanged = true;
3744                 } else if (holder.mPosition >= positionStart) {
3745                     if (DEBUG) {
3746                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +
3747                                 " holder " + holder + " now REMOVED");
3748                     }
3749                     holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
3750                             applyToPreLayout);
3751                     mState.mStructureChanged = true;
3752                 }
3753             }
3754         }
3755         mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
3756         requestLayout();
3757     }
3758 
3759     /**
3760      * Rebind existing views for the given range, or create as needed.
3761      *
3762      * @param positionStart Adapter position to start at
3763      * @param itemCount Number of views that must explicitly be rebound
3764      */
viewRangeUpdate(int positionStart, int itemCount, Object payload)3765     void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
3766         final int childCount = mChildHelper.getUnfilteredChildCount();
3767         final int positionEnd = positionStart + itemCount;
3768 
3769         for (int i = 0; i < childCount; i++) {
3770             final View child = mChildHelper.getUnfilteredChildAt(i);
3771             final ViewHolder holder = getChildViewHolderInt(child);
3772             if (holder == null || holder.shouldIgnore()) {
3773                 continue;
3774             }
3775             if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
3776                 // We re-bind these view holders after pre-processing is complete so that
3777                 // ViewHolders have their final positions assigned.
3778                 holder.addFlags(ViewHolder.FLAG_UPDATE);
3779                 holder.addChangePayload(payload);
3780                 // lp cannot be null since we get ViewHolder from it.
3781                 ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
3782             }
3783         }
3784         mRecycler.viewRangeUpdate(positionStart, itemCount);
3785     }
3786 
canReuseUpdatedViewHolder(ViewHolder viewHolder)3787     private boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
3788         return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
3789                 viewHolder.getUnmodifiedPayloads());
3790     }
3791 
setDataSetChangedAfterLayout()3792     private void setDataSetChangedAfterLayout() {
3793         if (mDataSetHasChangedAfterLayout) {
3794             return;
3795         }
3796         mDataSetHasChangedAfterLayout = true;
3797         final int childCount = mChildHelper.getUnfilteredChildCount();
3798         for (int i = 0; i < childCount; i++) {
3799             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3800             if (holder != null && !holder.shouldIgnore()) {
3801                 holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
3802             }
3803         }
3804         mRecycler.setAdapterPositionsAsUnknown();
3805     }
3806 
3807     /**
3808      * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
3809      * data change event.
3810      */
markKnownViewsInvalid()3811     void markKnownViewsInvalid() {
3812         final int childCount = mChildHelper.getUnfilteredChildCount();
3813         for (int i = 0; i < childCount; i++) {
3814             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3815             if (holder != null && !holder.shouldIgnore()) {
3816                 holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
3817             }
3818         }
3819         markItemDecorInsetsDirty();
3820         mRecycler.markKnownViewsInvalid();
3821     }
3822 
3823     /**
3824      * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
3825      * will trigger a {@link #requestLayout()} call.
3826      */
invalidateItemDecorations()3827     public void invalidateItemDecorations() {
3828         if (mItemDecorations.size() == 0) {
3829             return;
3830         }
3831         if (mLayout != null) {
3832             mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
3833                     + " or layout");
3834         }
3835         markItemDecorInsetsDirty();
3836         requestLayout();
3837     }
3838 
3839     /**
3840      * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
3841      * focus even if the View representing the Item is replaced during a layout calculation.
3842      * <p>
3843      * By default, this value is {@code true}.
3844      *
3845      * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
3846      * focus.
3847      *
3848      * @see #setPreserveFocusAfterLayout(boolean)
3849      */
getPreserveFocusAfterLayout()3850     public boolean getPreserveFocusAfterLayout() {
3851         return mPreserveFocusAfterLayout;
3852     }
3853 
3854     /**
3855      * Set whether the RecyclerView should try to keep the same Item focused after a layout
3856      * calculation or not.
3857      * <p>
3858      * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
3859      * views may lose focus during a layout calculation as their state changes or they are replaced
3860      * with another view due to type change or animation. In these cases, RecyclerView can request
3861      * focus on the new view automatically.
3862      *
3863      * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
3864      *                                 layout calculations. Defaults to true.
3865      *
3866      * @see #getPreserveFocusAfterLayout()
3867      */
setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout)3868     public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
3869         mPreserveFocusAfterLayout = preserveFocusAfterLayout;
3870     }
3871 
3872     /**
3873      * Retrieve the {@link ViewHolder} for the given child view.
3874      *
3875      * @param child Child of this RecyclerView to query for its ViewHolder
3876      * @return The child view's ViewHolder
3877      */
getChildViewHolder(View child)3878     public ViewHolder getChildViewHolder(View child) {
3879         final ViewParent parent = child.getParent();
3880         if (parent != null && parent != this) {
3881             throw new IllegalArgumentException("View " + child + " is not a direct child of " +
3882                     this);
3883         }
3884         return getChildViewHolderInt(child);
3885     }
3886 
3887     /**
3888      * Traverses the ancestors of the given view and returns the item view that contains it and
3889      * also a direct child of the RecyclerView. This returned view can be used to get the
3890      * ViewHolder by calling {@link #getChildViewHolder(View)}.
3891      *
3892      * @param view The view that is a descendant of the RecyclerView.
3893      *
3894      * @return The direct child of the RecyclerView which contains the given view or null if the
3895      * provided view is not a descendant of this RecyclerView.
3896      *
3897      * @see #getChildViewHolder(View)
3898      * @see #findContainingViewHolder(View)
3899      */
3900     @Nullable
findContainingItemView(View view)3901     public View findContainingItemView(View view) {
3902         ViewParent parent = view.getParent();
3903         while (parent != null && parent != this && parent instanceof View) {
3904             view = (View) parent;
3905             parent = view.getParent();
3906         }
3907         return parent == this ? view : null;
3908     }
3909 
3910     /**
3911      * Returns the ViewHolder that contains the given view.
3912      *
3913      * @param view The view that is a descendant of the RecyclerView.
3914      *
3915      * @return The ViewHolder that contains the given view or null if the provided view is not a
3916      * descendant of this RecyclerView.
3917      */
3918     @Nullable
findContainingViewHolder(View view)3919     public ViewHolder findContainingViewHolder(View view) {
3920         View itemView = findContainingItemView(view);
3921         return itemView == null ? null : getChildViewHolder(itemView);
3922     }
3923 
3924 
getChildViewHolderInt(View child)3925     static ViewHolder getChildViewHolderInt(View child) {
3926         if (child == null) {
3927             return null;
3928         }
3929         return ((LayoutParams) child.getLayoutParams()).mViewHolder;
3930     }
3931 
3932     /**
3933      * @deprecated use {@link #getChildAdapterPosition(View)} or
3934      * {@link #getChildLayoutPosition(View)}.
3935      */
3936     @Deprecated
getChildPosition(View child)3937     public int getChildPosition(View child) {
3938         return getChildAdapterPosition(child);
3939     }
3940 
3941     /**
3942      * Return the adapter position that the given child view corresponds to.
3943      *
3944      * @param child Child View to query
3945      * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
3946      */
getChildAdapterPosition(View child)3947     public int getChildAdapterPosition(View child) {
3948         final ViewHolder holder = getChildViewHolderInt(child);
3949         return holder != null ? holder.getAdapterPosition() : NO_POSITION;
3950     }
3951 
3952     /**
3953      * Return the adapter position of the given child view as of the latest completed layout pass.
3954      * <p>
3955      * This position may not be equal to Item's adapter position if there are pending changes
3956      * in the adapter which have not been reflected to the layout yet.
3957      *
3958      * @param child Child View to query
3959      * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
3960      * the View is representing a removed item.
3961      */
getChildLayoutPosition(View child)3962     public int getChildLayoutPosition(View child) {
3963         final ViewHolder holder = getChildViewHolderInt(child);
3964         return holder != null ? holder.getLayoutPosition() : NO_POSITION;
3965     }
3966 
3967     /**
3968      * Return the stable item id that the given child view corresponds to.
3969      *
3970      * @param child Child View to query
3971      * @return Item id corresponding to the given view or {@link #NO_ID}
3972      */
getChildItemId(View child)3973     public long getChildItemId(View child) {
3974         if (mAdapter == null || !mAdapter.hasStableIds()) {
3975             return NO_ID;
3976         }
3977         final ViewHolder holder = getChildViewHolderInt(child);
3978         return holder != null ? holder.getItemId() : NO_ID;
3979     }
3980 
3981     /**
3982      * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
3983      * {@link #findViewHolderForAdapterPosition(int)}
3984      */
3985     @Deprecated
findViewHolderForPosition(int position)3986     public ViewHolder findViewHolderForPosition(int position) {
3987         return findViewHolderForPosition(position, false);
3988     }
3989 
3990     /**
3991      * Return the ViewHolder for the item in the given position of the data set as of the latest
3992      * layout pass.
3993      * <p>
3994      * This method checks only the children of RecyclerView. If the item at the given
3995      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
3996      * <p>
3997      * Note that when Adapter contents change, ViewHolder positions are not updated until the
3998      * next layout calculation. If there are pending adapter updates, the return value of this
3999      * method may not match your adapter contents. You can use
4000      * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
4001      * <p>
4002      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4003      * with the same layout position representing the same Item. In this case, the updated
4004      * ViewHolder will be returned.
4005      *
4006      * @param position The position of the item in the data set of the adapter
4007      * @return The ViewHolder at <code>position</code> or null if there is no such item
4008      */
findViewHolderForLayoutPosition(int position)4009     public ViewHolder findViewHolderForLayoutPosition(int position) {
4010         return findViewHolderForPosition(position, false);
4011     }
4012 
4013     /**
4014      * Return the ViewHolder for the item in the given position of the data set. Unlike
4015      * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
4016      * adapter changes that may not be reflected to the layout yet. On the other hand, if
4017      * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
4018      * calculated yet, this method will return <code>null</code> since the new positions of views
4019      * are unknown until the layout is calculated.
4020      * <p>
4021      * This method checks only the children of RecyclerView. If the item at the given
4022      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4023      * <p>
4024      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4025      * representing the same Item. In this case, the updated ViewHolder will be returned.
4026      *
4027      * @param position The position of the item in the data set of the adapter
4028      * @return The ViewHolder at <code>position</code> or null if there is no such item
4029      */
findViewHolderForAdapterPosition(int position)4030     public ViewHolder findViewHolderForAdapterPosition(int position) {
4031         if (mDataSetHasChangedAfterLayout) {
4032             return null;
4033         }
4034         final int childCount = mChildHelper.getUnfilteredChildCount();
4035         // hidden VHs are not preferred but if that is the only one we find, we rather return it
4036         ViewHolder hidden = null;
4037         for (int i = 0; i < childCount; i++) {
4038             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4039             if (holder != null && !holder.isRemoved() && getAdapterPositionFor(holder) == position) {
4040                 if (mChildHelper.isHidden(holder.itemView)) {
4041                     hidden = holder;
4042                 } else {
4043                     return holder;
4044                 }
4045             }
4046         }
4047         return hidden;
4048     }
4049 
findViewHolderForPosition(int position, boolean checkNewPosition)4050     ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
4051         final int childCount = mChildHelper.getUnfilteredChildCount();
4052         ViewHolder hidden = null;
4053         for (int i = 0; i < childCount; i++) {
4054             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4055             if (holder != null && !holder.isRemoved()) {
4056                 if (checkNewPosition) {
4057                     if (holder.mPosition != position) {
4058                         continue;
4059                     }
4060                 } else if (holder.getLayoutPosition() != position) {
4061                     continue;
4062                 }
4063                 if (mChildHelper.isHidden(holder.itemView)) {
4064                     hidden = holder;
4065                 } else {
4066                     return holder;
4067                 }
4068             }
4069         }
4070         // This method should not query cached views. It creates a problem during adapter updates
4071         // when we are dealing with already laid out views. Also, for the public method, it is more
4072         // reasonable to return null if position is not laid out.
4073         return hidden;
4074     }
4075 
4076     /**
4077      * Return the ViewHolder for the item with the given id. The RecyclerView must
4078      * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
4079      * return a non-null value.
4080      * <p>
4081      * This method checks only the children of RecyclerView. If the item with the given
4082      * <code>id</code> is not laid out, it <em>will not</em> create a new one.
4083      *
4084      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
4085      * same id. In this case, the updated ViewHolder will be returned.
4086      *
4087      * @param id The id for the requested item
4088      * @return The ViewHolder with the given <code>id</code> or null if there is no such item
4089      */
findViewHolderForItemId(long id)4090     public ViewHolder findViewHolderForItemId(long id) {
4091         if (mAdapter == null || !mAdapter.hasStableIds()) {
4092             return null;
4093         }
4094         final int childCount = mChildHelper.getUnfilteredChildCount();
4095         ViewHolder hidden = null;
4096         for (int i = 0; i < childCount; i++) {
4097             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4098             if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
4099                 if (mChildHelper.isHidden(holder.itemView)) {
4100                     hidden = holder;
4101                 } else {
4102                     return holder;
4103                 }
4104             }
4105         }
4106         return hidden;
4107     }
4108 
4109     /**
4110      * Find the topmost view under the given point.
4111      *
4112      * @param x Horizontal position in pixels to search
4113      * @param y Vertical position in pixels to search
4114      * @return The child view under (x, y) or null if no matching child is found
4115      */
findChildViewUnder(float x, float y)4116     public View findChildViewUnder(float x, float y) {
4117         final int count = mChildHelper.getChildCount();
4118         for (int i = count - 1; i >= 0; i--) {
4119             final View child = mChildHelper.getChildAt(i);
4120             final float translationX = ViewCompat.getTranslationX(child);
4121             final float translationY = ViewCompat.getTranslationY(child);
4122             if (x >= child.getLeft() + translationX &&
4123                     x <= child.getRight() + translationX &&
4124                     y >= child.getTop() + translationY &&
4125                     y <= child.getBottom() + translationY) {
4126                 return child;
4127             }
4128         }
4129         return null;
4130     }
4131 
4132     @Override
drawChild(Canvas canvas, View child, long drawingTime)4133     public boolean drawChild(Canvas canvas, View child, long drawingTime) {
4134         return super.drawChild(canvas, child, drawingTime);
4135     }
4136 
4137     /**
4138      * Offset the bounds of all child views by <code>dy</code> pixels.
4139      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4140      *
4141      * @param dy Vertical pixel offset to apply to the bounds of all child views
4142      */
offsetChildrenVertical(int dy)4143     public void offsetChildrenVertical(int dy) {
4144         final int childCount = mChildHelper.getChildCount();
4145         for (int i = 0; i < childCount; i++) {
4146             mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
4147         }
4148     }
4149 
4150     /**
4151      * Called when an item view is attached to this RecyclerView.
4152      *
4153      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4154      * of child views as they become attached. This will be called before a
4155      * {@link LayoutManager} measures or lays out the view and is a good time to perform these
4156      * changes.</p>
4157      *
4158      * @param child Child view that is now attached to this RecyclerView and its associated window
4159      */
onChildAttachedToWindow(View child)4160     public void onChildAttachedToWindow(View child) {
4161     }
4162 
4163     /**
4164      * Called when an item view is detached from this RecyclerView.
4165      *
4166      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4167      * of child views as they become detached. This will be called as a
4168      * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
4169      *
4170      * @param child Child view that is now detached from this RecyclerView and its associated window
4171      */
onChildDetachedFromWindow(View child)4172     public void onChildDetachedFromWindow(View child) {
4173     }
4174 
4175     /**
4176      * Offset the bounds of all child views by <code>dx</code> pixels.
4177      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4178      *
4179      * @param dx Horizontal pixel offset to apply to the bounds of all child views
4180      */
offsetChildrenHorizontal(int dx)4181     public void offsetChildrenHorizontal(int dx) {
4182         final int childCount = mChildHelper.getChildCount();
4183         for (int i = 0; i < childCount; i++) {
4184             mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
4185         }
4186     }
4187 
getItemDecorInsetsForChild(View child)4188     Rect getItemDecorInsetsForChild(View child) {
4189         final LayoutParams lp = (LayoutParams) child.getLayoutParams();
4190         if (!lp.mInsetsDirty) {
4191             return lp.mDecorInsets;
4192         }
4193 
4194         final Rect insets = lp.mDecorInsets;
4195         insets.set(0, 0, 0, 0);
4196         final int decorCount = mItemDecorations.size();
4197         for (int i = 0; i < decorCount; i++) {
4198             mTempRect.set(0, 0, 0, 0);
4199             mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
4200             insets.left += mTempRect.left;
4201             insets.top += mTempRect.top;
4202             insets.right += mTempRect.right;
4203             insets.bottom += mTempRect.bottom;
4204         }
4205         lp.mInsetsDirty = false;
4206         return insets;
4207     }
4208 
4209     /**
4210      * Called when the scroll position of this RecyclerView changes. Subclasses should use
4211      * this method to respond to scrolling within the adapter's data set instead of an explicit
4212      * listener.
4213      *
4214      * <p>This method will always be invoked before listeners. If a subclass needs to perform
4215      * any additional upkeep or bookkeeping after scrolling but before listeners run,
4216      * this is a good place to do so.</p>
4217      *
4218      * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
4219      * the distance scrolled in either direction within the adapter's data set instead of absolute
4220      * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
4221      * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
4222      * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
4223      * do not correspond to the data set scroll position. However, some subclasses may choose
4224      * to use these fields as special offsets.</p>
4225      *
4226      * @param dx horizontal distance scrolled in pixels
4227      * @param dy vertical distance scrolled in pixels
4228      */
onScrolled(int dx, int dy)4229     public void onScrolled(int dx, int dy) {
4230         // Do nothing
4231     }
4232 
dispatchOnScrolled(int hresult, int vresult)4233     void dispatchOnScrolled(int hresult, int vresult) {
4234         // Pass the current scrollX/scrollY values; no actual change in these properties occurred
4235         // but some general-purpose code may choose to respond to changes this way.
4236         final int scrollX = getScrollX();
4237         final int scrollY = getScrollY();
4238         onScrollChanged(scrollX, scrollY, scrollX, scrollY);
4239 
4240         // Pass the real deltas to onScrolled, the RecyclerView-specific method.
4241         onScrolled(hresult, vresult);
4242 
4243         // Invoke listeners last. Subclassed view methods always handle the event first.
4244         // All internal state is consistent by the time listeners are invoked.
4245         if (mScrollListener != null) {
4246             mScrollListener.onScrolled(this, hresult, vresult);
4247         }
4248         if (mScrollListeners != null) {
4249             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4250                 mScrollListeners.get(i).onScrolled(this, hresult, vresult);
4251             }
4252         }
4253     }
4254 
4255     /**
4256      * Called when the scroll state of this RecyclerView changes. Subclasses should use this
4257      * method to respond to state changes instead of an explicit listener.
4258      *
4259      * <p>This method will always be invoked before listeners, but after the LayoutManager
4260      * responds to the scroll state change.</p>
4261      *
4262      * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
4263      *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
4264      */
onScrollStateChanged(int state)4265     public void onScrollStateChanged(int state) {
4266         // Do nothing
4267     }
4268 
dispatchOnScrollStateChanged(int state)4269     void dispatchOnScrollStateChanged(int state) {
4270         // Let the LayoutManager go first; this allows it to bring any properties into
4271         // a consistent state before the RecyclerView subclass responds.
4272         if (mLayout != null) {
4273             mLayout.onScrollStateChanged(state);
4274         }
4275 
4276         // Let the RecyclerView subclass handle this event next; any LayoutManager property
4277         // changes will be reflected by this time.
4278         onScrollStateChanged(state);
4279 
4280         // Listeners go last. All other internal state is consistent by this point.
4281         if (mScrollListener != null) {
4282             mScrollListener.onScrollStateChanged(this, state);
4283         }
4284         if (mScrollListeners != null) {
4285             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4286                 mScrollListeners.get(i).onScrollStateChanged(this, state);
4287             }
4288         }
4289     }
4290 
4291     /**
4292      * Returns whether there are pending adapter updates which are not yet applied to the layout.
4293      * <p>
4294      * If this method returns <code>true</code>, it means that what user is currently seeing may not
4295      * reflect them adapter contents (depending on what has changed).
4296      * You may use this information to defer or cancel some operations.
4297      * <p>
4298      * This method returns true if RecyclerView has not yet calculated the first layout after it is
4299      * attached to the Window or the Adapter has been replaced.
4300      *
4301      * @return True if there are some adapter updates which are not yet reflected to layout or false
4302      * if layout is up to date.
4303      */
hasPendingAdapterUpdates()4304     public boolean hasPendingAdapterUpdates() {
4305         return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
4306                 || mAdapterHelper.hasPendingUpdates();
4307     }
4308 
4309     private class ViewFlinger implements Runnable {
4310         private int mLastFlingX;
4311         private int mLastFlingY;
4312         private ScrollerCompat mScroller;
4313         private Interpolator mInterpolator = sQuinticInterpolator;
4314 
4315 
4316         // When set to true, postOnAnimation callbacks are delayed until the run method completes
4317         private boolean mEatRunOnAnimationRequest = false;
4318 
4319         // Tracks if postAnimationCallback should be re-attached when it is done
4320         private boolean mReSchedulePostAnimationCallback = false;
4321 
ViewFlinger()4322         public ViewFlinger() {
4323             mScroller = ScrollerCompat.create(getContext(), sQuinticInterpolator);
4324         }
4325 
4326         @Override
run()4327         public void run() {
4328             if (mLayout == null) {
4329                 stop();
4330                 return; // no layout, cannot scroll.
4331             }
4332             disableRunOnAnimationRequests();
4333             consumePendingUpdateOperations();
4334             // keep a local reference so that if it is changed during onAnimation method, it won't
4335             // cause unexpected behaviors
4336             final ScrollerCompat scroller = mScroller;
4337             final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
4338             if (scroller.computeScrollOffset()) {
4339                 final int x = scroller.getCurrX();
4340                 final int y = scroller.getCurrY();
4341                 final int dx = x - mLastFlingX;
4342                 final int dy = y - mLastFlingY;
4343                 int hresult = 0;
4344                 int vresult = 0;
4345                 mLastFlingX = x;
4346                 mLastFlingY = y;
4347                 int overscrollX = 0, overscrollY = 0;
4348                 if (mAdapter != null) {
4349                     eatRequestLayout();
4350                     onEnterLayoutOrScroll();
4351                     TraceCompat.beginSection(TRACE_SCROLL_TAG);
4352                     if (dx != 0) {
4353                         hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
4354                         overscrollX = dx - hresult;
4355                     }
4356                     if (dy != 0) {
4357                         vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
4358                         overscrollY = dy - vresult;
4359                     }
4360                     TraceCompat.endSection();
4361                     repositionShadowingViews();
4362 
4363                     onExitLayoutOrScroll();
4364                     resumeRequestLayout(false);
4365 
4366                     if (smoothScroller != null && !smoothScroller.isPendingInitialRun() &&
4367                             smoothScroller.isRunning()) {
4368                         final int adapterSize = mState.getItemCount();
4369                         if (adapterSize == 0) {
4370                             smoothScroller.stop();
4371                         } else if (smoothScroller.getTargetPosition() >= adapterSize) {
4372                             smoothScroller.setTargetPosition(adapterSize - 1);
4373                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4374                         } else {
4375                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4376                         }
4377                     }
4378                 }
4379                 if (!mItemDecorations.isEmpty()) {
4380                     invalidate();
4381                 }
4382                 if (ViewCompat.getOverScrollMode(RecyclerView.this) !=
4383                         ViewCompat.OVER_SCROLL_NEVER) {
4384                     considerReleasingGlowsOnScroll(dx, dy);
4385                 }
4386                 if (overscrollX != 0 || overscrollY != 0) {
4387                     final int vel = (int) scroller.getCurrVelocity();
4388 
4389                     int velX = 0;
4390                     if (overscrollX != x) {
4391                         velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
4392                     }
4393 
4394                     int velY = 0;
4395                     if (overscrollY != y) {
4396                         velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
4397                     }
4398 
4399                     if (ViewCompat.getOverScrollMode(RecyclerView.this) !=
4400                             ViewCompat.OVER_SCROLL_NEVER) {
4401                         absorbGlows(velX, velY);
4402                     }
4403                     if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0) &&
4404                             (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
4405                         scroller.abortAnimation();
4406                     }
4407                 }
4408                 if (hresult != 0 || vresult != 0) {
4409                     dispatchOnScrolled(hresult, vresult);
4410                 }
4411 
4412                 if (!awakenScrollBars()) {
4413                     invalidate();
4414                 }
4415 
4416                 final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
4417                         && vresult == dy;
4418                 final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
4419                         && hresult == dx;
4420                 final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
4421                         || fullyConsumedVertical;
4422 
4423                 if (scroller.isFinished() || !fullyConsumedAny) {
4424                     setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this.
4425                 } else {
4426                     postOnAnimation();
4427                 }
4428             }
4429             // call this after the onAnimation is complete not to have inconsistent callbacks etc.
4430             if (smoothScroller != null) {
4431                 if (smoothScroller.isPendingInitialRun()) {
4432                     smoothScroller.onAnimation(0, 0);
4433                 }
4434                 if (!mReSchedulePostAnimationCallback) {
4435                     smoothScroller.stop(); //stop if it does not trigger any scroll
4436                 }
4437             }
4438             enableRunOnAnimationRequests();
4439         }
4440 
disableRunOnAnimationRequests()4441         private void disableRunOnAnimationRequests() {
4442             mReSchedulePostAnimationCallback = false;
4443             mEatRunOnAnimationRequest = true;
4444         }
4445 
enableRunOnAnimationRequests()4446         private void enableRunOnAnimationRequests() {
4447             mEatRunOnAnimationRequest = false;
4448             if (mReSchedulePostAnimationCallback) {
4449                 postOnAnimation();
4450             }
4451         }
4452 
postOnAnimation()4453         void postOnAnimation() {
4454             if (mEatRunOnAnimationRequest) {
4455                 mReSchedulePostAnimationCallback = true;
4456             } else {
4457                 removeCallbacks(this);
4458                 ViewCompat.postOnAnimation(RecyclerView.this, this);
4459             }
4460         }
4461 
fling(int velocityX, int velocityY)4462         public void fling(int velocityX, int velocityY) {
4463             setScrollState(SCROLL_STATE_SETTLING);
4464             mLastFlingX = mLastFlingY = 0;
4465             mScroller.fling(0, 0, velocityX, velocityY,
4466                     Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
4467             postOnAnimation();
4468         }
4469 
smoothScrollBy(int dx, int dy)4470         public void smoothScrollBy(int dx, int dy) {
4471             smoothScrollBy(dx, dy, 0, 0);
4472         }
4473 
smoothScrollBy(int dx, int dy, int vx, int vy)4474         public void smoothScrollBy(int dx, int dy, int vx, int vy) {
4475             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
4476         }
4477 
distanceInfluenceForSnapDuration(float f)4478         private float distanceInfluenceForSnapDuration(float f) {
4479             f -= 0.5f; // center the values about 0.
4480             f *= 0.3f * Math.PI / 2.0f;
4481             return (float) Math.sin(f);
4482         }
4483 
computeScrollDuration(int dx, int dy, int vx, int vy)4484         private int computeScrollDuration(int dx, int dy, int vx, int vy) {
4485             final int absDx = Math.abs(dx);
4486             final int absDy = Math.abs(dy);
4487             final boolean horizontal = absDx > absDy;
4488             final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
4489             final int delta = (int) Math.sqrt(dx * dx + dy * dy);
4490             final int containerSize = horizontal ? getWidth() : getHeight();
4491             final int halfContainerSize = containerSize / 2;
4492             final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
4493             final float distance = halfContainerSize + halfContainerSize *
4494                     distanceInfluenceForSnapDuration(distanceRatio);
4495 
4496             final int duration;
4497             if (velocity > 0) {
4498                 duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
4499             } else {
4500                 float absDelta = (float) (horizontal ? absDx : absDy);
4501                 duration = (int) (((absDelta / containerSize) + 1) * 300);
4502             }
4503             return Math.min(duration, MAX_SCROLL_DURATION);
4504         }
4505 
smoothScrollBy(int dx, int dy, int duration)4506         public void smoothScrollBy(int dx, int dy, int duration) {
4507             smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
4508         }
4509 
smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator)4510         public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
4511             if (mInterpolator != interpolator) {
4512                 mInterpolator = interpolator;
4513                 mScroller = ScrollerCompat.create(getContext(), interpolator);
4514             }
4515             setScrollState(SCROLL_STATE_SETTLING);
4516             mLastFlingX = mLastFlingY = 0;
4517             mScroller.startScroll(0, 0, dx, dy, duration);
4518             postOnAnimation();
4519         }
4520 
stop()4521         public void stop() {
4522             removeCallbacks(this);
4523             mScroller.abortAnimation();
4524         }
4525 
4526     }
4527 
repositionShadowingViews()4528     private void repositionShadowingViews() {
4529         // Fix up shadow views used by change animations
4530         int count = mChildHelper.getChildCount();
4531         for (int i = 0; i < count; i++) {
4532             View view = mChildHelper.getChildAt(i);
4533             ViewHolder holder = getChildViewHolder(view);
4534             if (holder != null && holder.mShadowingHolder != null) {
4535                 View shadowingView = holder.mShadowingHolder.itemView;
4536                 int left = view.getLeft();
4537                 int top = view.getTop();
4538                 if (left != shadowingView.getLeft() ||
4539                         top != shadowingView.getTop()) {
4540                     shadowingView.layout(left, top,
4541                             left + shadowingView.getWidth(),
4542                             top + shadowingView.getHeight());
4543                 }
4544             }
4545         }
4546     }
4547 
4548     private class RecyclerViewDataObserver extends AdapterDataObserver {
4549         @Override
onChanged()4550         public void onChanged() {
4551             assertNotInLayoutOrScroll(null);
4552             if (mAdapter.hasStableIds()) {
4553                 // TODO Determine what actually changed.
4554                 // This is more important to implement now since this callback will disable all
4555                 // animations because we cannot rely on positions.
4556                 mState.mStructureChanged = true;
4557                 setDataSetChangedAfterLayout();
4558             } else {
4559                 mState.mStructureChanged = true;
4560                 setDataSetChangedAfterLayout();
4561             }
4562             if (!mAdapterHelper.hasPendingUpdates()) {
4563                 requestLayout();
4564             }
4565         }
4566 
4567         @Override
onItemRangeChanged(int positionStart, int itemCount, Object payload)4568         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
4569             assertNotInLayoutOrScroll(null);
4570             if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
4571                 triggerUpdateProcessor();
4572             }
4573         }
4574 
4575         @Override
onItemRangeInserted(int positionStart, int itemCount)4576         public void onItemRangeInserted(int positionStart, int itemCount) {
4577             assertNotInLayoutOrScroll(null);
4578             if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
4579                 triggerUpdateProcessor();
4580             }
4581         }
4582 
4583         @Override
onItemRangeRemoved(int positionStart, int itemCount)4584         public void onItemRangeRemoved(int positionStart, int itemCount) {
4585             assertNotInLayoutOrScroll(null);
4586             if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
4587                 triggerUpdateProcessor();
4588             }
4589         }
4590 
4591         @Override
onItemRangeMoved(int fromPosition, int toPosition, int itemCount)4592         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
4593             assertNotInLayoutOrScroll(null);
4594             if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
4595                 triggerUpdateProcessor();
4596             }
4597         }
4598 
triggerUpdateProcessor()4599         void triggerUpdateProcessor() {
4600             if (mPostUpdatesOnAnimation && mHasFixedSize && mIsAttached) {
4601                 ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
4602             } else {
4603                 mAdapterUpdateDuringMeasure = true;
4604                 requestLayout();
4605             }
4606         }
4607     }
4608 
4609     /**
4610      * RecycledViewPool lets you share Views between multiple RecyclerViews.
4611      * <p>
4612      * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
4613      * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
4614      * <p>
4615      * RecyclerView automatically creates a pool for itself if you don't provide one.
4616      *
4617      */
4618     public static class RecycledViewPool {
4619         private SparseArray<ArrayList<ViewHolder>> mScrap =
4620                 new SparseArray<ArrayList<ViewHolder>>();
4621         private SparseIntArray mMaxScrap = new SparseIntArray();
4622         private int mAttachCount = 0;
4623 
4624         private static final int DEFAULT_MAX_SCRAP = 5;
4625 
clear()4626         public void clear() {
4627             mScrap.clear();
4628         }
4629 
setMaxRecycledViews(int viewType, int max)4630         public void setMaxRecycledViews(int viewType, int max) {
4631             mMaxScrap.put(viewType, max);
4632             final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
4633             if (scrapHeap != null) {
4634                 while (scrapHeap.size() > max) {
4635                     scrapHeap.remove(scrapHeap.size() - 1);
4636                 }
4637             }
4638         }
4639 
getRecycledView(int viewType)4640         public ViewHolder getRecycledView(int viewType) {
4641             final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
4642             if (scrapHeap != null && !scrapHeap.isEmpty()) {
4643                 final int index = scrapHeap.size() - 1;
4644                 final ViewHolder scrap = scrapHeap.get(index);
4645                 scrapHeap.remove(index);
4646                 return scrap;
4647             }
4648             return null;
4649         }
4650 
size()4651         int size() {
4652             int count = 0;
4653             for (int i = 0; i < mScrap.size(); i ++) {
4654                 ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i);
4655                 if (viewHolders != null) {
4656                     count += viewHolders.size();
4657                 }
4658             }
4659             return count;
4660         }
4661 
putRecycledView(ViewHolder scrap)4662         public void putRecycledView(ViewHolder scrap) {
4663             final int viewType = scrap.getItemViewType();
4664             final ArrayList scrapHeap = getScrapHeapForType(viewType);
4665             if (mMaxScrap.get(viewType) <= scrapHeap.size()) {
4666                 return;
4667             }
4668             if (DEBUG && scrapHeap.contains(scrap)) {
4669                 throw new IllegalArgumentException("this scrap item already exists");
4670             }
4671             scrap.resetInternal();
4672             scrapHeap.add(scrap);
4673         }
4674 
attach(Adapter adapter)4675         void attach(Adapter adapter) {
4676             mAttachCount++;
4677         }
4678 
detach()4679         void detach() {
4680             mAttachCount--;
4681         }
4682 
4683 
4684         /**
4685          * Detaches the old adapter and attaches the new one.
4686          * <p>
4687          * RecycledViewPool will clear its cache if it has only one adapter attached and the new
4688          * adapter uses a different ViewHolder than the oldAdapter.
4689          *
4690          * @param oldAdapter The previous adapter instance. Will be detached.
4691          * @param newAdapter The new adapter instance. Will be attached.
4692          * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
4693          *                               ViewHolder and view types.
4694          */
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, boolean compatibleWithPrevious)4695         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
4696                 boolean compatibleWithPrevious) {
4697             if (oldAdapter != null) {
4698                 detach();
4699             }
4700             if (!compatibleWithPrevious && mAttachCount == 0) {
4701                 clear();
4702             }
4703             if (newAdapter != null) {
4704                 attach(newAdapter);
4705             }
4706         }
4707 
getScrapHeapForType(int viewType)4708         private ArrayList<ViewHolder> getScrapHeapForType(int viewType) {
4709             ArrayList<ViewHolder> scrap = mScrap.get(viewType);
4710             if (scrap == null) {
4711                 scrap = new ArrayList<>();
4712                 mScrap.put(viewType, scrap);
4713                 if (mMaxScrap.indexOfKey(viewType) < 0) {
4714                     mMaxScrap.put(viewType, DEFAULT_MAX_SCRAP);
4715                 }
4716             }
4717             return scrap;
4718         }
4719     }
4720 
4721     /**
4722      * A Recycler is responsible for managing scrapped or detached item views for reuse.
4723      *
4724      * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
4725      * that has been marked for removal or reuse.</p>
4726      *
4727      * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
4728      * an adapter's data set representing the data at a given position or item ID.
4729      * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
4730      * If not, the view can be quickly reused by the LayoutManager with no further work.
4731      * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
4732      * may be repositioned by a LayoutManager without remeasurement.</p>
4733      */
4734     public final class Recycler {
4735         final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
4736         private ArrayList<ViewHolder> mChangedScrap = null;
4737 
4738         final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
4739 
4740         private final List<ViewHolder>
4741                 mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
4742 
4743         private int mViewCacheMax = DEFAULT_CACHE_SIZE;
4744 
4745         private RecycledViewPool mRecyclerPool;
4746 
4747         private ViewCacheExtension mViewCacheExtension;
4748 
4749         private static final int DEFAULT_CACHE_SIZE = 2;
4750 
4751         /**
4752          * Clear scrap views out of this recycler. Detached views contained within a
4753          * recycled view pool will remain.
4754          */
clear()4755         public void clear() {
4756             mAttachedScrap.clear();
4757             recycleAndClearCachedViews();
4758         }
4759 
4760         /**
4761          * Set the maximum number of detached, valid views we should retain for later use.
4762          *
4763          * @param viewCount Number of views to keep before sending views to the shared pool
4764          */
setViewCacheSize(int viewCount)4765         public void setViewCacheSize(int viewCount) {
4766             mViewCacheMax = viewCount;
4767             // first, try the views that can be recycled
4768             for (int i = mCachedViews.size() - 1; i >= 0 && mCachedViews.size() > viewCount; i--) {
4769                 recycleCachedViewAt(i);
4770             }
4771         }
4772 
4773         /**
4774          * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
4775          *
4776          * @return List of ViewHolders in the scrap list.
4777          */
getScrapList()4778         public List<ViewHolder> getScrapList() {
4779             return mUnmodifiableAttachedScrap;
4780         }
4781 
4782         /**
4783          * Helper method for getViewForPosition.
4784          * <p>
4785          * Checks whether a given view holder can be used for the provided position.
4786          *
4787          * @param holder ViewHolder
4788          * @return true if ViewHolder matches the provided position, false otherwise
4789          */
validateViewHolderForOffsetPosition(ViewHolder holder)4790         boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
4791             // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
4792             // if it is not removed, verify the type and id.
4793             if (holder.isRemoved()) {
4794                 if (DEBUG && !mState.isPreLayout()) {
4795                     throw new IllegalStateException("should not receive a removed view unelss it"
4796                             + " is pre layout");
4797                 }
4798                 return mState.isPreLayout();
4799             }
4800             if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
4801                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
4802                         + "adapter position" + holder);
4803             }
4804             if (!mState.isPreLayout()) {
4805                 // don't check type if it is pre-layout.
4806                 final int type = mAdapter.getItemViewType(holder.mPosition);
4807                 if (type != holder.getItemViewType()) {
4808                     return false;
4809                 }
4810             }
4811             if (mAdapter.hasStableIds()) {
4812                 return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
4813             }
4814             return true;
4815         }
4816 
4817         /**
4818          * Binds the given View to the position. The View can be a View previously retrieved via
4819          * {@link #getViewForPosition(int)} or created by
4820          * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
4821          * <p>
4822          * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
4823          * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
4824          * wants to handle its own recycling logic.
4825          * <p>
4826          * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
4827          * you don't need to call this method unless you want to bind this View to another position.
4828          *
4829          * @param view The view to update.
4830          * @param position The position of the item to bind to this View.
4831          */
bindViewToPosition(View view, int position)4832         public void bindViewToPosition(View view, int position) {
4833             ViewHolder holder = getChildViewHolderInt(view);
4834             if (holder == null) {
4835                 throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
4836                         + " pass arbitrary views to this method, they should be created by the "
4837                         + "Adapter");
4838             }
4839             final int offsetPosition = mAdapterHelper.findPositionOffset(position);
4840             if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
4841                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
4842                         + "position " + position + "(offset:" + offsetPosition + ")."
4843                         + "state:" + mState.getItemCount());
4844             }
4845             holder.mOwnerRecyclerView = RecyclerView.this;
4846             mAdapter.bindViewHolder(holder, offsetPosition);
4847             attachAccessibilityDelegate(view);
4848             if (mState.isPreLayout()) {
4849                 holder.mPreLayoutPosition = position;
4850             }
4851 
4852             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
4853             final LayoutParams rvLayoutParams;
4854             if (lp == null) {
4855                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
4856                 holder.itemView.setLayoutParams(rvLayoutParams);
4857             } else if (!checkLayoutParams(lp)) {
4858                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
4859                 holder.itemView.setLayoutParams(rvLayoutParams);
4860             } else {
4861                 rvLayoutParams = (LayoutParams) lp;
4862             }
4863 
4864             rvLayoutParams.mInsetsDirty = true;
4865             rvLayoutParams.mViewHolder = holder;
4866             rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
4867         }
4868 
4869         /**
4870          * RecyclerView provides artificial position range (item count) in pre-layout state and
4871          * automatically maps these positions to {@link Adapter} positions when
4872          * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
4873          * <p>
4874          * Usually, LayoutManager does not need to worry about this. However, in some cases, your
4875          * LayoutManager may need to call some custom component with item positions in which
4876          * case you need the actual adapter position instead of the pre layout position. You
4877          * can use this method to convert a pre-layout position to adapter (post layout) position.
4878          * <p>
4879          * Note that if the provided position belongs to a deleted ViewHolder, this method will
4880          * return -1.
4881          * <p>
4882          * Calling this method in post-layout state returns the same value back.
4883          *
4884          * @param position The pre-layout position to convert. Must be greater or equal to 0 and
4885          *                 less than {@link State#getItemCount()}.
4886          */
convertPreLayoutPositionToPostLayout(int position)4887         public int convertPreLayoutPositionToPostLayout(int position) {
4888             if (position < 0 || position >= mState.getItemCount()) {
4889                 throw new IndexOutOfBoundsException("invalid position " + position + ". State "
4890                         + "item count is " + mState.getItemCount());
4891             }
4892             if (!mState.isPreLayout()) {
4893                 return position;
4894             }
4895             return mAdapterHelper.findPositionOffset(position);
4896         }
4897 
4898         /**
4899          * Obtain a view initialized for the given position.
4900          *
4901          * This method should be used by {@link LayoutManager} implementations to obtain
4902          * views to represent data from an {@link Adapter}.
4903          * <p>
4904          * The Recycler may reuse a scrap or detached view from a shared pool if one is
4905          * available for the correct view type. If the adapter has not indicated that the
4906          * data at the given position has changed, the Recycler will attempt to hand back
4907          * a scrap view that was previously initialized for that data without rebinding.
4908          *
4909          * @param position Position to obtain a view for
4910          * @return A view representing the data at <code>position</code> from <code>adapter</code>
4911          */
getViewForPosition(int position)4912         public View getViewForPosition(int position) {
4913             return getViewForPosition(position, false);
4914         }
4915 
getViewForPosition(int position, boolean dryRun)4916         View getViewForPosition(int position, boolean dryRun) {
4917             if (position < 0 || position >= mState.getItemCount()) {
4918                 throw new IndexOutOfBoundsException("Invalid item position " + position
4919                         + "(" + position + "). Item count:" + mState.getItemCount());
4920             }
4921             boolean fromScrap = false;
4922             ViewHolder holder = null;
4923             // 0) If there is a changed scrap, try to find from there
4924             if (mState.isPreLayout()) {
4925                 holder = getChangedScrapViewForPosition(position);
4926                 fromScrap = holder != null;
4927             }
4928             // 1) Find from scrap by position
4929             if (holder == null) {
4930                 holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);
4931                 if (holder != null) {
4932                     if (!validateViewHolderForOffsetPosition(holder)) {
4933                         // recycle this scrap
4934                         if (!dryRun) {
4935                             // we would like to recycle this but need to make sure it is not used by
4936                             // animation logic etc.
4937                             holder.addFlags(ViewHolder.FLAG_INVALID);
4938                             if (holder.isScrap()) {
4939                                 removeDetachedView(holder.itemView, false);
4940                                 holder.unScrap();
4941                             } else if (holder.wasReturnedFromScrap()) {
4942                                 holder.clearReturnedFromScrapFlag();
4943                             }
4944                             recycleViewHolderInternal(holder);
4945                         }
4946                         holder = null;
4947                     } else {
4948                         fromScrap = true;
4949                     }
4950                 }
4951             }
4952             if (holder == null) {
4953                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
4954                 if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
4955                     throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
4956                             + "position " + position + "(offset:" + offsetPosition + ")."
4957                             + "state:" + mState.getItemCount());
4958                 }
4959 
4960                 final int type = mAdapter.getItemViewType(offsetPosition);
4961                 // 2) Find from scrap via stable ids, if exists
4962                 if (mAdapter.hasStableIds()) {
4963                     holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
4964                     if (holder != null) {
4965                         // update position
4966                         holder.mPosition = offsetPosition;
4967                         fromScrap = true;
4968                     }
4969                 }
4970                 if (holder == null && mViewCacheExtension != null) {
4971                     // We are NOT sending the offsetPosition because LayoutManager does not
4972                     // know it.
4973                     final View view = mViewCacheExtension
4974                             .getViewForPositionAndType(this, position, type);
4975                     if (view != null) {
4976                         holder = getChildViewHolder(view);
4977                         if (holder == null) {
4978                             throw new IllegalArgumentException("getViewForPositionAndType returned"
4979                                     + " a view which does not have a ViewHolder");
4980                         } else if (holder.shouldIgnore()) {
4981                             throw new IllegalArgumentException("getViewForPositionAndType returned"
4982                                     + " a view that is ignored. You must call stopIgnoring before"
4983                                     + " returning this view.");
4984                         }
4985                     }
4986                 }
4987                 if (holder == null) { // fallback to recycler
4988                     // try recycler.
4989                     // Head to the shared pool.
4990                     if (DEBUG) {
4991                         Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "
4992                                 + "pool");
4993                     }
4994                     holder = getRecycledViewPool().getRecycledView(type);
4995                     if (holder != null) {
4996                         holder.resetInternal();
4997                         if (FORCE_INVALIDATE_DISPLAY_LIST) {
4998                             invalidateDisplayListInt(holder);
4999                         }
5000                     }
5001                 }
5002                 if (holder == null) {
5003                     holder = mAdapter.createViewHolder(RecyclerView.this, type);
5004                     if (DEBUG) {
5005                         Log.d(TAG, "getViewForPosition created new ViewHolder");
5006                     }
5007                 }
5008             }
5009 
5010             // This is very ugly but the only place we can grab this information
5011             // before the View is rebound and returned to the LayoutManager for post layout ops.
5012             // We don't need this in pre-layout since the VH is not updated by the LM.
5013             if (fromScrap && !mState.isPreLayout() && holder
5014                     .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
5015                 holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5016                 if (mState.mRunSimpleAnimations) {
5017                     int changeFlags = ItemAnimator
5018                             .buildAdapterChangeFlagsForAnimations(holder);
5019                     changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
5020                     final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
5021                             holder, changeFlags, holder.getUnmodifiedPayloads());
5022                     recordAnimationInfoIfBouncedHiddenView(holder, info);
5023                 }
5024             }
5025 
5026             boolean bound = false;
5027             if (mState.isPreLayout() && holder.isBound()) {
5028                 // do not update unless we absolutely have to.
5029                 holder.mPreLayoutPosition = position;
5030             } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
5031                 if (DEBUG && holder.isRemoved()) {
5032                     throw new IllegalStateException("Removed holder should be bound and it should"
5033                             + " come here only in pre-layout. Holder: " + holder);
5034                 }
5035                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5036                 holder.mOwnerRecyclerView = RecyclerView.this;
5037                 mAdapter.bindViewHolder(holder, offsetPosition);
5038                 attachAccessibilityDelegate(holder.itemView);
5039                 bound = true;
5040                 if (mState.isPreLayout()) {
5041                     holder.mPreLayoutPosition = position;
5042                 }
5043             }
5044 
5045             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5046             final LayoutParams rvLayoutParams;
5047             if (lp == null) {
5048                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5049                 holder.itemView.setLayoutParams(rvLayoutParams);
5050             } else if (!checkLayoutParams(lp)) {
5051                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5052                 holder.itemView.setLayoutParams(rvLayoutParams);
5053             } else {
5054                 rvLayoutParams = (LayoutParams) lp;
5055             }
5056             rvLayoutParams.mViewHolder = holder;
5057             rvLayoutParams.mPendingInvalidate = fromScrap && bound;
5058             return holder.itemView;
5059         }
5060 
attachAccessibilityDelegate(View itemView)5061         private void attachAccessibilityDelegate(View itemView) {
5062             if (isAccessibilityEnabled()) {
5063                 if (ViewCompat.getImportantForAccessibility(itemView) ==
5064                         ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
5065                     ViewCompat.setImportantForAccessibility(itemView,
5066                             ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
5067                 }
5068                 if (!ViewCompat.hasAccessibilityDelegate(itemView)) {
5069                     ViewCompat.setAccessibilityDelegate(itemView,
5070                             mAccessibilityDelegate.getItemDelegate());
5071                 }
5072             }
5073         }
5074 
invalidateDisplayListInt(ViewHolder holder)5075         private void invalidateDisplayListInt(ViewHolder holder) {
5076             if (holder.itemView instanceof ViewGroup) {
5077                 invalidateDisplayListInt((ViewGroup) holder.itemView, false);
5078             }
5079         }
5080 
invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis)5081         private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
5082             for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
5083                 final View view = viewGroup.getChildAt(i);
5084                 if (view instanceof ViewGroup) {
5085                     invalidateDisplayListInt((ViewGroup) view, true);
5086                 }
5087             }
5088             if (!invalidateThis) {
5089                 return;
5090             }
5091             // we need to force it to become invisible
5092             if (viewGroup.getVisibility() == View.INVISIBLE) {
5093                 viewGroup.setVisibility(View.VISIBLE);
5094                 viewGroup.setVisibility(View.INVISIBLE);
5095             } else {
5096                 final int visibility = viewGroup.getVisibility();
5097                 viewGroup.setVisibility(View.INVISIBLE);
5098                 viewGroup.setVisibility(visibility);
5099             }
5100         }
5101 
5102         /**
5103          * Recycle a detached view. The specified view will be added to a pool of views
5104          * for later rebinding and reuse.
5105          *
5106          * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
5107          * View is scrapped, it will be removed from scrap list.</p>
5108          *
5109          * @param view Removed view for recycling
5110          * @see LayoutManager#removeAndRecycleView(View, Recycler)
5111          */
recycleView(View view)5112         public void recycleView(View view) {
5113             // This public recycle method tries to make view recycle-able since layout manager
5114             // intended to recycle this view (e.g. even if it is in scrap or change cache)
5115             ViewHolder holder = getChildViewHolderInt(view);
5116             if (holder.isTmpDetached()) {
5117                 removeDetachedView(view, false);
5118             }
5119             if (holder.isScrap()) {
5120                 holder.unScrap();
5121             } else if (holder.wasReturnedFromScrap()){
5122                 holder.clearReturnedFromScrapFlag();
5123             }
5124             recycleViewHolderInternal(holder);
5125         }
5126 
5127         /**
5128          * Internally, use this method instead of {@link #recycleView(android.view.View)} to
5129          * catch potential bugs.
5130          * @param view
5131          */
recycleViewInternal(View view)5132         void recycleViewInternal(View view) {
5133             recycleViewHolderInternal(getChildViewHolderInt(view));
5134         }
5135 
recycleAndClearCachedViews()5136         void recycleAndClearCachedViews() {
5137             final int count = mCachedViews.size();
5138             for (int i = count - 1; i >= 0; i--) {
5139                 recycleCachedViewAt(i);
5140             }
5141             mCachedViews.clear();
5142         }
5143 
5144         /**
5145          * Recycles a cached view and removes the view from the list. Views are added to cache
5146          * if and only if they are recyclable, so this method does not check it again.
5147          * <p>
5148          * A small exception to this rule is when the view does not have an animator reference
5149          * but transient state is true (due to animations created outside ItemAnimator). In that
5150          * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
5151          * still recyclable since Adapter wants to do so.
5152          *
5153          * @param cachedViewIndex The index of the view in cached views list
5154          */
recycleCachedViewAt(int cachedViewIndex)5155         void recycleCachedViewAt(int cachedViewIndex) {
5156             if (DEBUG) {
5157                 Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
5158             }
5159             ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
5160             if (DEBUG) {
5161                 Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
5162             }
5163             addViewHolderToRecycledViewPool(viewHolder);
5164             mCachedViews.remove(cachedViewIndex);
5165         }
5166 
5167         /**
5168          * internal implementation checks if view is scrapped or attached and throws an exception
5169          * if so.
5170          * Public version un-scraps before calling recycle.
5171          */
recycleViewHolderInternal(ViewHolder holder)5172         void recycleViewHolderInternal(ViewHolder holder) {
5173             if (holder.isScrap() || holder.itemView.getParent() != null) {
5174                 throw new IllegalArgumentException(
5175                         "Scrapped or attached views may not be recycled. isScrap:"
5176                                 + holder.isScrap() + " isAttached:"
5177                                 + (holder.itemView.getParent() != null));
5178             }
5179 
5180             if (holder.isTmpDetached()) {
5181                 throw new IllegalArgumentException("Tmp detached view should be removed "
5182                         + "from RecyclerView before it can be recycled: " + holder);
5183             }
5184 
5185             if (holder.shouldIgnore()) {
5186                 throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
5187                         + " should first call stopIgnoringView(view) before calling recycle.");
5188             }
5189             //noinspection unchecked
5190             final boolean transientStatePreventsRecycling = holder
5191                     .doesTransientStatePreventRecycling();
5192             final boolean forceRecycle = mAdapter != null
5193                     && transientStatePreventsRecycling
5194                     && mAdapter.onFailedToRecycleView(holder);
5195             boolean cached = false;
5196             boolean recycled = false;
5197             if (DEBUG && mCachedViews.contains(holder)) {
5198                 throw new IllegalArgumentException("cached view received recycle internal? " +
5199                         holder);
5200             }
5201             if (forceRecycle || holder.isRecyclable()) {
5202                 if (!holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED
5203                         | ViewHolder.FLAG_UPDATE)) {
5204                     // Retire oldest cached view
5205                     int cachedViewSize = mCachedViews.size();
5206                     if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
5207                         recycleCachedViewAt(0);
5208                         cachedViewSize --;
5209                     }
5210                     if (cachedViewSize < mViewCacheMax) {
5211                         mCachedViews.add(holder);
5212                         cached = true;
5213                     }
5214                 }
5215                 if (!cached) {
5216                     addViewHolderToRecycledViewPool(holder);
5217                     recycled = true;
5218                 }
5219             } else if (DEBUG) {
5220                 Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
5221                         + "re-visit here. We are still removing it from animation lists");
5222             }
5223             // even if the holder is not removed, we still call this method so that it is removed
5224             // from view holder lists.
5225             mViewInfoStore.removeViewHolder(holder);
5226             if (!cached && !recycled && transientStatePreventsRecycling) {
5227                 holder.mOwnerRecyclerView = null;
5228             }
5229         }
5230 
addViewHolderToRecycledViewPool(ViewHolder holder)5231         void addViewHolderToRecycledViewPool(ViewHolder holder) {
5232             ViewCompat.setAccessibilityDelegate(holder.itemView, null);
5233             dispatchViewRecycled(holder);
5234             holder.mOwnerRecyclerView = null;
5235             getRecycledViewPool().putRecycledView(holder);
5236         }
5237 
5238         /**
5239          * Used as a fast path for unscrapping and recycling a view during a bulk operation.
5240          * The caller must call {@link #clearScrap()} when it's done to update the recycler's
5241          * internal bookkeeping.
5242          */
quickRecycleScrapView(View view)5243         void quickRecycleScrapView(View view) {
5244             final ViewHolder holder = getChildViewHolderInt(view);
5245             holder.mScrapContainer = null;
5246             holder.mInChangeScrap = false;
5247             holder.clearReturnedFromScrapFlag();
5248             recycleViewHolderInternal(holder);
5249         }
5250 
5251         /**
5252          * Mark an attached view as scrap.
5253          *
5254          * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
5255          * for rebinding and reuse. Requests for a view for a given position may return a
5256          * reused or rebound scrap view instance.</p>
5257          *
5258          * @param view View to scrap
5259          */
scrapView(View view)5260         void scrapView(View view) {
5261             final ViewHolder holder = getChildViewHolderInt(view);
5262             if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
5263                     || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
5264                 if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
5265                     throw new IllegalArgumentException("Called scrap view with an invalid view."
5266                             + " Invalid views cannot be reused from scrap, they should rebound from"
5267                             + " recycler pool.");
5268                 }
5269                 holder.setScrapContainer(this, false);
5270                 mAttachedScrap.add(holder);
5271             } else {
5272                 if (mChangedScrap == null) {
5273                     mChangedScrap = new ArrayList<ViewHolder>();
5274                 }
5275                 holder.setScrapContainer(this, true);
5276                 mChangedScrap.add(holder);
5277             }
5278         }
5279 
5280         /**
5281          * Remove a previously scrapped view from the pool of eligible scrap.
5282          *
5283          * <p>This view will no longer be eligible for reuse until re-scrapped or
5284          * until it is explicitly removed and recycled.</p>
5285          */
unscrapView(ViewHolder holder)5286         void unscrapView(ViewHolder holder) {
5287             if (holder.mInChangeScrap) {
5288                 mChangedScrap.remove(holder);
5289             } else {
5290                 mAttachedScrap.remove(holder);
5291             }
5292             holder.mScrapContainer = null;
5293             holder.mInChangeScrap = false;
5294             holder.clearReturnedFromScrapFlag();
5295         }
5296 
getScrapCount()5297         int getScrapCount() {
5298             return mAttachedScrap.size();
5299         }
5300 
getScrapViewAt(int index)5301         View getScrapViewAt(int index) {
5302             return mAttachedScrap.get(index).itemView;
5303         }
5304 
clearScrap()5305         void clearScrap() {
5306             mAttachedScrap.clear();
5307             if (mChangedScrap != null) {
5308                 mChangedScrap.clear();
5309             }
5310         }
5311 
getChangedScrapViewForPosition(int position)5312         ViewHolder getChangedScrapViewForPosition(int position) {
5313             // If pre-layout, check the changed scrap for an exact match.
5314             final int changedScrapSize;
5315             if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
5316                 return null;
5317             }
5318             // find by position
5319             for (int i = 0; i < changedScrapSize; i++) {
5320                 final ViewHolder holder = mChangedScrap.get(i);
5321                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
5322                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5323                     return holder;
5324                 }
5325             }
5326             // find by id
5327             if (mAdapter.hasStableIds()) {
5328                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5329                 if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
5330                     final long id = mAdapter.getItemId(offsetPosition);
5331                     for (int i = 0; i < changedScrapSize; i++) {
5332                         final ViewHolder holder = mChangedScrap.get(i);
5333                         if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
5334                             holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5335                             return holder;
5336                         }
5337                     }
5338                 }
5339             }
5340             return null;
5341         }
5342 
5343         /**
5344          * Returns a scrap view for the position. If type is not INVALID_TYPE, it also checks if
5345          * ViewHolder's type matches the provided type.
5346          *
5347          * @param position Item position
5348          * @param type View type
5349          * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
5350          * @return a ViewHolder that can be re-used for this position.
5351          */
getScrapViewForPosition(int position, int type, boolean dryRun)5352         ViewHolder getScrapViewForPosition(int position, int type, boolean dryRun) {
5353             final int scrapCount = mAttachedScrap.size();
5354 
5355             // Try first for an exact, non-invalid match from scrap.
5356             for (int i = 0; i < scrapCount; i++) {
5357                 final ViewHolder holder = mAttachedScrap.get(i);
5358                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
5359                         && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
5360                     if (type != INVALID_TYPE && holder.getItemViewType() != type) {
5361                         Log.e(TAG, "Scrap view for position " + position + " isn't dirty but has" +
5362                                 " wrong view type! (found " + holder.getItemViewType() +
5363                                 " but expected " + type + ")");
5364                         break;
5365                     }
5366                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5367                     return holder;
5368                 }
5369             }
5370 
5371             if (!dryRun) {
5372                 View view = mChildHelper.findHiddenNonRemovedView(position, type);
5373                 if (view != null) {
5374                     // This View is good to be used. We just need to unhide, detach and move to the
5375                     // scrap list.
5376                     final ViewHolder vh = getChildViewHolderInt(view);
5377                     mChildHelper.unhide(view);
5378                     int layoutIndex = mChildHelper.indexOfChild(view);
5379                     if (layoutIndex == RecyclerView.NO_POSITION) {
5380                         throw new IllegalStateException("layout index should not be -1 after "
5381                                 + "unhiding a view:" + vh);
5382                     }
5383                     mChildHelper.detachViewFromParent(layoutIndex);
5384                     scrapView(view);
5385                     vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
5386                             | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5387                     return vh;
5388                 }
5389             }
5390 
5391             // Search in our first-level recycled view cache.
5392             final int cacheSize = mCachedViews.size();
5393             for (int i = 0; i < cacheSize; i++) {
5394                 final ViewHolder holder = mCachedViews.get(i);
5395                 // invalid view holders may be in cache if adapter has stable ids as they can be
5396                 // retrieved via getScrapViewForId
5397                 if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
5398                     if (!dryRun) {
5399                         mCachedViews.remove(i);
5400                     }
5401                     if (DEBUG) {
5402                         Log.d(TAG, "getScrapViewForPosition(" + position + ", " + type +
5403                                 ") found match in cache: " + holder);
5404                     }
5405                     return holder;
5406                 }
5407             }
5408             return null;
5409         }
5410 
getScrapViewForId(long id, int type, boolean dryRun)5411         ViewHolder getScrapViewForId(long id, int type, boolean dryRun) {
5412             // Look in our attached views first
5413             final int count = mAttachedScrap.size();
5414             for (int i = count - 1; i >= 0; i--) {
5415                 final ViewHolder holder = mAttachedScrap.get(i);
5416                 if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
5417                     if (type == holder.getItemViewType()) {
5418                         holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5419                         if (holder.isRemoved()) {
5420                             // this might be valid in two cases:
5421                             // > item is removed but we are in pre-layout pass
5422                             // >> do nothing. return as is. make sure we don't rebind
5423                             // > item is removed then added to another position and we are in
5424                             // post layout.
5425                             // >> remove removed and invalid flags, add update flag to rebind
5426                             // because item was invisible to us and we don't know what happened in
5427                             // between.
5428                             if (!mState.isPreLayout()) {
5429                                 holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE |
5430                                         ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
5431                             }
5432                         }
5433                         return holder;
5434                     } else if (!dryRun) {
5435                         // if we are running animations, it is actually better to keep it in scrap
5436                         // but this would force layout manager to lay it out which would be bad.
5437                         // Recycle this scrap. Type mismatch.
5438                         mAttachedScrap.remove(i);
5439                         removeDetachedView(holder.itemView, false);
5440                         quickRecycleScrapView(holder.itemView);
5441                     }
5442                 }
5443             }
5444 
5445             // Search the first-level cache
5446             final int cacheSize = mCachedViews.size();
5447             for (int i = cacheSize - 1; i >= 0; i--) {
5448                 final ViewHolder holder = mCachedViews.get(i);
5449                 if (holder.getItemId() == id) {
5450                     if (type == holder.getItemViewType()) {
5451                         if (!dryRun) {
5452                             mCachedViews.remove(i);
5453                         }
5454                         return holder;
5455                     } else if (!dryRun) {
5456                         recycleCachedViewAt(i);
5457                     }
5458                 }
5459             }
5460             return null;
5461         }
5462 
dispatchViewRecycled(ViewHolder holder)5463         void dispatchViewRecycled(ViewHolder holder) {
5464             if (mRecyclerListener != null) {
5465                 mRecyclerListener.onViewRecycled(holder);
5466             }
5467             if (mAdapter != null) {
5468                 mAdapter.onViewRecycled(holder);
5469             }
5470             if (mState != null) {
5471                 mViewInfoStore.removeViewHolder(holder);
5472             }
5473             if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
5474         }
5475 
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, boolean compatibleWithPrevious)5476         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
5477                 boolean compatibleWithPrevious) {
5478             clear();
5479             getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
5480         }
5481 
offsetPositionRecordsForMove(int from, int to)5482         void offsetPositionRecordsForMove(int from, int to) {
5483             final int start, end, inBetweenOffset;
5484             if (from < to) {
5485                 start = from;
5486                 end = to;
5487                 inBetweenOffset = -1;
5488             } else {
5489                 start = to;
5490                 end = from;
5491                 inBetweenOffset = 1;
5492             }
5493             final int cachedCount = mCachedViews.size();
5494             for (int i = 0; i < cachedCount; i++) {
5495                 final ViewHolder holder = mCachedViews.get(i);
5496                 if (holder == null || holder.mPosition < start || holder.mPosition > end) {
5497                     continue;
5498                 }
5499                 if (holder.mPosition == from) {
5500                     holder.offsetPosition(to - from, false);
5501                 } else {
5502                     holder.offsetPosition(inBetweenOffset, false);
5503                 }
5504                 if (DEBUG) {
5505                     Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder " +
5506                             holder);
5507                 }
5508             }
5509         }
5510 
offsetPositionRecordsForInsert(int insertedAt, int count)5511         void offsetPositionRecordsForInsert(int insertedAt, int count) {
5512             final int cachedCount = mCachedViews.size();
5513             for (int i = 0; i < cachedCount; i++) {
5514                 final ViewHolder holder = mCachedViews.get(i);
5515                 if (holder != null && holder.mPosition >= insertedAt) {
5516                     if (DEBUG) {
5517                         Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder " +
5518                                 holder + " now at position " + (holder.mPosition + count));
5519                     }
5520                     holder.offsetPosition(count, true);
5521                 }
5522             }
5523         }
5524 
5525         /**
5526          * @param removedFrom Remove start index
5527          * @param count Remove count
5528          * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
5529          *                         false, they'll be applied before the second layout pass
5530          */
offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout)5531         void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
5532             final int removedEnd = removedFrom + count;
5533             final int cachedCount = mCachedViews.size();
5534             for (int i = cachedCount - 1; i >= 0; i--) {
5535                 final ViewHolder holder = mCachedViews.get(i);
5536                 if (holder != null) {
5537                     if (holder.mPosition >= removedEnd) {
5538                         if (DEBUG) {
5539                             Log.d(TAG, "offsetPositionRecordsForRemove cached " + i +
5540                                     " holder " + holder + " now at position " +
5541                                     (holder.mPosition - count));
5542                         }
5543                         holder.offsetPosition(-count, applyToPreLayout);
5544                     } else if (holder.mPosition >= removedFrom) {
5545                         // Item for this view was removed. Dump it from the cache.
5546                         holder.addFlags(ViewHolder.FLAG_REMOVED);
5547                         recycleCachedViewAt(i);
5548                     }
5549                 }
5550             }
5551         }
5552 
setViewCacheExtension(ViewCacheExtension extension)5553         void setViewCacheExtension(ViewCacheExtension extension) {
5554             mViewCacheExtension = extension;
5555         }
5556 
setRecycledViewPool(RecycledViewPool pool)5557         void setRecycledViewPool(RecycledViewPool pool) {
5558             if (mRecyclerPool != null) {
5559                 mRecyclerPool.detach();
5560             }
5561             mRecyclerPool = pool;
5562             if (pool != null) {
5563                 mRecyclerPool.attach(getAdapter());
5564             }
5565         }
5566 
getRecycledViewPool()5567         RecycledViewPool getRecycledViewPool() {
5568             if (mRecyclerPool == null) {
5569                 mRecyclerPool = new RecycledViewPool();
5570             }
5571             return mRecyclerPool;
5572         }
5573 
viewRangeUpdate(int positionStart, int itemCount)5574         void viewRangeUpdate(int positionStart, int itemCount) {
5575             final int positionEnd = positionStart + itemCount;
5576             final int cachedCount = mCachedViews.size();
5577             for (int i = cachedCount - 1; i >= 0; i--) {
5578                 final ViewHolder holder = mCachedViews.get(i);
5579                 if (holder == null) {
5580                     continue;
5581                 }
5582 
5583                 final int pos = holder.getLayoutPosition();
5584                 if (pos >= positionStart && pos < positionEnd) {
5585                     holder.addFlags(ViewHolder.FLAG_UPDATE);
5586                     recycleCachedViewAt(i);
5587                     // cached views should not be flagged as changed because this will cause them
5588                     // to animate when they are returned from cache.
5589                 }
5590             }
5591         }
5592 
setAdapterPositionsAsUnknown()5593         void setAdapterPositionsAsUnknown() {
5594             final int cachedCount = mCachedViews.size();
5595             for (int i = 0; i < cachedCount; i++) {
5596                 final ViewHolder holder = mCachedViews.get(i);
5597                 if (holder != null) {
5598                     holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
5599                 }
5600             }
5601         }
5602 
markKnownViewsInvalid()5603         void markKnownViewsInvalid() {
5604             if (mAdapter != null && mAdapter.hasStableIds()) {
5605                 final int cachedCount = mCachedViews.size();
5606                 for (int i = 0; i < cachedCount; i++) {
5607                     final ViewHolder holder = mCachedViews.get(i);
5608                     if (holder != null) {
5609                         holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
5610                         holder.addChangePayload(null);
5611                     }
5612                 }
5613             } else {
5614                 // we cannot re-use cached views in this case. Recycle them all
5615                 recycleAndClearCachedViews();
5616             }
5617         }
5618 
clearOldPositions()5619         void clearOldPositions() {
5620             final int cachedCount = mCachedViews.size();
5621             for (int i = 0; i < cachedCount; i++) {
5622                 final ViewHolder holder = mCachedViews.get(i);
5623                 holder.clearOldPosition();
5624             }
5625             final int scrapCount = mAttachedScrap.size();
5626             for (int i = 0; i < scrapCount; i++) {
5627                 mAttachedScrap.get(i).clearOldPosition();
5628             }
5629             if (mChangedScrap != null) {
5630                 final int changedScrapCount = mChangedScrap.size();
5631                 for (int i = 0; i < changedScrapCount; i++) {
5632                     mChangedScrap.get(i).clearOldPosition();
5633                 }
5634             }
5635         }
5636 
markItemDecorInsetsDirty()5637         void markItemDecorInsetsDirty() {
5638             final int cachedCount = mCachedViews.size();
5639             for (int i = 0; i < cachedCount; i++) {
5640                 final ViewHolder holder = mCachedViews.get(i);
5641                 LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
5642                 if (layoutParams != null) {
5643                     layoutParams.mInsetsDirty = true;
5644                 }
5645             }
5646         }
5647     }
5648 
5649     /**
5650      * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
5651      * ben controlled by the developer.
5652      * <p>
5653      * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
5654      * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
5655      * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
5656      * {@link RecycledViewPool}.
5657      * <p>
5658      * Note that, Recycler never sends Views to this method to be cached. It is developers
5659      * responsibility to decide whether they want to keep their Views in this custom cache or let
5660      * the default recycling policy handle it.
5661      */
5662     public abstract static class ViewCacheExtension {
5663 
5664         /**
5665          * Returns a View that can be binded to the given Adapter position.
5666          * <p>
5667          * This method should <b>not</b> create a new View. Instead, it is expected to return
5668          * an already created View that can be re-used for the given type and position.
5669          * If the View is marked as ignored, it should first call
5670          * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
5671          * <p>
5672          * RecyclerView will re-bind the returned View to the position if necessary.
5673          *
5674          * @param recycler The Recycler that can be used to bind the View
5675          * @param position The adapter position
5676          * @param type     The type of the View, defined by adapter
5677          * @return A View that is bound to the given position or NULL if there is no View to re-use
5678          * @see LayoutManager#ignoreView(View)
5679          */
getViewForPositionAndType(Recycler recycler, int position, int type)5680         abstract public View getViewForPositionAndType(Recycler recycler, int position, int type);
5681     }
5682 
5683     /**
5684      * Base class for an Adapter
5685      *
5686      * <p>Adapters provide a binding from an app-specific data set to views that are displayed
5687      * within a {@link RecyclerView}.</p>
5688      */
5689     public static abstract class Adapter<VH extends ViewHolder> {
5690         private final AdapterDataObservable mObservable = new AdapterDataObservable();
5691         private boolean mHasStableIds = false;
5692 
5693         /**
5694          * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
5695          * an item.
5696          * <p>
5697          * This new ViewHolder should be constructed with a new View that can represent the items
5698          * of the given type. You can either create a new View manually or inflate it from an XML
5699          * layout file.
5700          * <p>
5701          * The new ViewHolder will be used to display items of the adapter using
5702          * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
5703          * different items in the data set, it is a good idea to cache references to sub views of
5704          * the View to avoid unnecessary {@link View#findViewById(int)} calls.
5705          *
5706          * @param parent The ViewGroup into which the new View will be added after it is bound to
5707          *               an adapter position.
5708          * @param viewType The view type of the new View.
5709          *
5710          * @return A new ViewHolder that holds a View of the given view type.
5711          * @see #getItemViewType(int)
5712          * @see #onBindViewHolder(ViewHolder, int)
5713          */
onCreateViewHolder(ViewGroup parent, int viewType)5714         public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
5715 
5716         /**
5717          * Called by RecyclerView to display the data at the specified position. This method should
5718          * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
5719          * position.
5720          * <p>
5721          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
5722          * again if the position of the item changes in the data set unless the item itself is
5723          * invalidated or the new position cannot be determined. For this reason, you should only
5724          * use the <code>position</code> parameter while acquiring the related data item inside
5725          * this method and should not keep a copy of it. If you need the position of an item later
5726          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
5727          * have the updated adapter position.
5728          *
5729          * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
5730          * handle effcient partial bind.
5731          *
5732          * @param holder The ViewHolder which should be updated to represent the contents of the
5733          *        item at the given position in the data set.
5734          * @param position The position of the item within the adapter's data set.
5735          */
onBindViewHolder(VH holder, int position)5736         public abstract void onBindViewHolder(VH holder, int position);
5737 
5738         /**
5739          * Called by RecyclerView to display the data at the specified position. This method
5740          * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
5741          * the given position.
5742          * <p>
5743          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
5744          * again if the position of the item changes in the data set unless the item itself is
5745          * invalidated or the new position cannot be determined. For this reason, you should only
5746          * use the <code>position</code> parameter while acquiring the related data item inside
5747          * this method and should not keep a copy of it. If you need the position of an item later
5748          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
5749          * have the updated adapter position.
5750          * <p>
5751          * Partial bind vs full bind:
5752          * <p>
5753          * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
5754          * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
5755          * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
5756          * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
5757          * Adapter should not assume that the payload passed in notify methods will be received by
5758          * onBindViewHolder().  For example when the view is not attached to the screen, the
5759          * payload in notifyItemChange() will be simply dropped.
5760          *
5761          * @param holder The ViewHolder which should be updated to represent the contents of the
5762          *               item at the given position in the data set.
5763          * @param position The position of the item within the adapter's data set.
5764          * @param payloads A non-null list of merged payloads. Can be empty list if requires full
5765          *                 update.
5766          */
onBindViewHolder(VH holder, int position, List<Object> payloads)5767         public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
5768             onBindViewHolder(holder, position);
5769         }
5770 
5771         /**
5772          * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
5773          * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
5774          *
5775          * @see #onCreateViewHolder(ViewGroup, int)
5776          */
createViewHolder(ViewGroup parent, int viewType)5777         public final VH createViewHolder(ViewGroup parent, int viewType) {
5778             TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
5779             final VH holder = onCreateViewHolder(parent, viewType);
5780             holder.mItemViewType = viewType;
5781             TraceCompat.endSection();
5782             return holder;
5783         }
5784 
5785         /**
5786          * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
5787          * {@link ViewHolder} contents with the item at the given position and also sets up some
5788          * private fields to be used by RecyclerView.
5789          *
5790          * @see #onBindViewHolder(ViewHolder, int)
5791          */
bindViewHolder(VH holder, int position)5792         public final void bindViewHolder(VH holder, int position) {
5793             holder.mPosition = position;
5794             if (hasStableIds()) {
5795                 holder.mItemId = getItemId(position);
5796             }
5797             holder.setFlags(ViewHolder.FLAG_BOUND,
5798                     ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
5799                             | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
5800             TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
5801             onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
5802             holder.clearPayload();
5803             TraceCompat.endSection();
5804         }
5805 
5806         /**
5807          * Return the view type of the item at <code>position</code> for the purposes
5808          * of view recycling.
5809          *
5810          * <p>The default implementation of this method returns 0, making the assumption of
5811          * a single view type for the adapter. Unlike ListView adapters, types need not
5812          * be contiguous. Consider using id resources to uniquely identify item view types.
5813          *
5814          * @param position position to query
5815          * @return integer value identifying the type of the view needed to represent the item at
5816          *                 <code>position</code>. Type codes need not be contiguous.
5817          */
getItemViewType(int position)5818         public int getItemViewType(int position) {
5819             return 0;
5820         }
5821 
5822         /**
5823          * Indicates whether each item in the data set can be represented with a unique identifier
5824          * of type {@link java.lang.Long}.
5825          *
5826          * @param hasStableIds Whether items in data set have unique identifiers or not.
5827          * @see #hasStableIds()
5828          * @see #getItemId(int)
5829          */
setHasStableIds(boolean hasStableIds)5830         public void setHasStableIds(boolean hasStableIds) {
5831             if (hasObservers()) {
5832                 throw new IllegalStateException("Cannot change whether this adapter has " +
5833                         "stable IDs while the adapter has registered observers.");
5834             }
5835             mHasStableIds = hasStableIds;
5836         }
5837 
5838         /**
5839          * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
5840          * would return false this method should return {@link #NO_ID}. The default implementation
5841          * of this method returns {@link #NO_ID}.
5842          *
5843          * @param position Adapter position to query
5844          * @return the stable ID of the item at position
5845          */
getItemId(int position)5846         public long getItemId(int position) {
5847             return NO_ID;
5848         }
5849 
5850         /**
5851          * Returns the total number of items in the data set hold by the adapter.
5852          *
5853          * @return The total number of items in this adapter.
5854          */
getItemCount()5855         public abstract int getItemCount();
5856 
5857         /**
5858          * Returns true if this adapter publishes a unique <code>long</code> value that can
5859          * act as a key for the item at a given position in the data set. If that item is relocated
5860          * in the data set, the ID returned for that item should be the same.
5861          *
5862          * @return true if this adapter's items have stable IDs
5863          */
hasStableIds()5864         public final boolean hasStableIds() {
5865             return mHasStableIds;
5866         }
5867 
5868         /**
5869          * Called when a view created by this adapter has been recycled.
5870          *
5871          * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
5872          * needs to be attached to its parent {@link RecyclerView}. This can be because it has
5873          * fallen out of visibility or a set of cached views represented by views still
5874          * attached to the parent RecyclerView. If an item view has large or expensive data
5875          * bound to it such as large bitmaps, this may be a good place to release those
5876          * resources.</p>
5877          * <p>
5878          * RecyclerView calls this method right before clearing ViewHolder's internal data and
5879          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
5880          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
5881          * its adapter position.
5882          *
5883          * @param holder The ViewHolder for the view being recycled
5884          */
onViewRecycled(VH holder)5885         public void onViewRecycled(VH holder) {
5886         }
5887 
5888         /**
5889          * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
5890          * due to its transient state. Upon receiving this callback, Adapter can clear the
5891          * animation(s) that effect the View's transient state and return <code>true</code> so that
5892          * the View can be recycled. Keep in mind that the View in question is already removed from
5893          * the RecyclerView.
5894          * <p>
5895          * In some cases, it is acceptable to recycle a View although it has transient state. Most
5896          * of the time, this is a case where the transient state will be cleared in
5897          * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
5898          * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
5899          * value of this method to decide whether the View should be recycled or not.
5900          * <p>
5901          * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
5902          * should never receive this callback because RecyclerView keeps those Views as children
5903          * until their animations are complete. This callback is useful when children of the item
5904          * views create animations which may not be easy to implement using an {@link ItemAnimator}.
5905          * <p>
5906          * You should <em>never</em> fix this issue by calling
5907          * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
5908          * <code>holder.itemView.setHasTransientState(true);</code>. Each
5909          * <code>View.setHasTransientState(true)</code> call must be matched by a
5910          * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
5911          * may become inconsistent. You should always prefer to end or cancel animations that are
5912          * triggering the transient state instead of handling it manually.
5913          *
5914          * @param holder The ViewHolder containing the View that could not be recycled due to its
5915          *               transient state.
5916          * @return True if the View should be recycled, false otherwise. Note that if this method
5917          * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
5918          * the View and recycle it regardless. If this method returns <code>false</code>,
5919          * RecyclerView will check the View's transient state again before giving a final decision.
5920          * Default implementation returns false.
5921          */
onFailedToRecycleView(VH holder)5922         public boolean onFailedToRecycleView(VH holder) {
5923             return false;
5924         }
5925 
5926         /**
5927          * Called when a view created by this adapter has been attached to a window.
5928          *
5929          * <p>This can be used as a reasonable signal that the view is about to be seen
5930          * by the user. If the adapter previously freed any resources in
5931          * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
5932          * those resources should be restored here.</p>
5933          *
5934          * @param holder Holder of the view being attached
5935          */
onViewAttachedToWindow(VH holder)5936         public void onViewAttachedToWindow(VH holder) {
5937         }
5938 
5939         /**
5940          * Called when a view created by this adapter has been detached from its window.
5941          *
5942          * <p>Becoming detached from the window is not necessarily a permanent condition;
5943          * the consumer of an Adapter's views may choose to cache views offscreen while they
5944          * are not visible, attaching an detaching them as appropriate.</p>
5945          *
5946          * @param holder Holder of the view being detached
5947          */
onViewDetachedFromWindow(VH holder)5948         public void onViewDetachedFromWindow(VH holder) {
5949         }
5950 
5951         /**
5952          * Returns true if one or more observers are attached to this adapter.
5953          *
5954          * @return true if this adapter has observers
5955          */
hasObservers()5956         public final boolean hasObservers() {
5957             return mObservable.hasObservers();
5958         }
5959 
5960         /**
5961          * Register a new observer to listen for data changes.
5962          *
5963          * <p>The adapter may publish a variety of events describing specific changes.
5964          * Not all adapters may support all change types and some may fall back to a generic
5965          * {@link android.support.v7.widget.RecyclerView.AdapterDataObserver#onChanged()
5966          * "something changed"} event if more specific data is not available.</p>
5967          *
5968          * <p>Components registering observers with an adapter are responsible for
5969          * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
5970          * unregistering} those observers when finished.</p>
5971          *
5972          * @param observer Observer to register
5973          *
5974          * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
5975          */
registerAdapterDataObserver(AdapterDataObserver observer)5976         public void registerAdapterDataObserver(AdapterDataObserver observer) {
5977             mObservable.registerObserver(observer);
5978         }
5979 
5980         /**
5981          * Unregister an observer currently listening for data changes.
5982          *
5983          * <p>The unregistered observer will no longer receive events about changes
5984          * to the adapter.</p>
5985          *
5986          * @param observer Observer to unregister
5987          *
5988          * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
5989          */
unregisterAdapterDataObserver(AdapterDataObserver observer)5990         public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
5991             mObservable.unregisterObserver(observer);
5992         }
5993 
5994         /**
5995          * Called by RecyclerView when it starts observing this Adapter.
5996          * <p>
5997          * Keep in mind that same adapter may be observed by multiple RecyclerViews.
5998          *
5999          * @param recyclerView The RecyclerView instance which started observing this adapter.
6000          * @see #onDetachedFromRecyclerView(RecyclerView)
6001          */
onAttachedToRecyclerView(RecyclerView recyclerView)6002         public void onAttachedToRecyclerView(RecyclerView recyclerView) {
6003         }
6004 
6005         /**
6006          * Called by RecyclerView when it stops observing this Adapter.
6007          *
6008          * @param recyclerView The RecyclerView instance which stopped observing this adapter.
6009          * @see #onAttachedToRecyclerView(RecyclerView)
6010          */
onDetachedFromRecyclerView(RecyclerView recyclerView)6011         public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
6012         }
6013 
6014         /**
6015          * Notify any registered observers that the data set has changed.
6016          *
6017          * <p>There are two different classes of data change events, item changes and structural
6018          * changes. Item changes are when a single item has its data updated but no positional
6019          * changes have occurred. Structural changes are when items are inserted, removed or moved
6020          * within the data set.</p>
6021          *
6022          * <p>This event does not specify what about the data set has changed, forcing
6023          * any observers to assume that all existing items and structure may no longer be valid.
6024          * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
6025          *
6026          * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
6027          * for adapters that report that they have {@link #hasStableIds() stable IDs} when
6028          * this method is used. This can help for the purposes of animation and visual
6029          * object persistence but individual item views will still need to be rebound
6030          * and relaid out.</p>
6031          *
6032          * <p>If you are writing an adapter it will always be more efficient to use the more
6033          * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
6034          * as a last resort.</p>
6035          *
6036          * @see #notifyItemChanged(int)
6037          * @see #notifyItemInserted(int)
6038          * @see #notifyItemRemoved(int)
6039          * @see #notifyItemRangeChanged(int, int)
6040          * @see #notifyItemRangeInserted(int, int)
6041          * @see #notifyItemRangeRemoved(int, int)
6042          */
notifyDataSetChanged()6043         public final void notifyDataSetChanged() {
6044             mObservable.notifyChanged();
6045         }
6046 
6047         /**
6048          * Notify any registered observers that the item at <code>position</code> has changed.
6049          * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
6050          *
6051          * <p>This is an item change event, not a structural change event. It indicates that any
6052          * reflection of the data at <code>position</code> is out of date and should be updated.
6053          * The item at <code>position</code> retains the same identity.</p>
6054          *
6055          * @param position Position of the item that has changed
6056          *
6057          * @see #notifyItemRangeChanged(int, int)
6058          */
notifyItemChanged(int position)6059         public final void notifyItemChanged(int position) {
6060             mObservable.notifyItemRangeChanged(position, 1);
6061         }
6062 
6063         /**
6064          * Notify any registered observers that the item at <code>position</code> has changed with an
6065          * optional payload object.
6066          *
6067          * <p>This is an item change event, not a structural change event. It indicates that any
6068          * reflection of the data at <code>position</code> is out of date and should be updated.
6069          * The item at <code>position</code> retains the same identity.
6070          * </p>
6071          *
6072          * <p>
6073          * Client can optionally pass a payload for partial change. These payloads will be merged
6074          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6075          * item is already represented by a ViewHolder and it will be rebound to the same
6076          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6077          * payloads on that item and prevent future payload until
6078          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6079          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6080          * attached, the payload will be simply dropped.
6081          *
6082          * @param position Position of the item that has changed
6083          * @param payload Optional parameter, use null to identify a "full" update
6084          *
6085          * @see #notifyItemRangeChanged(int, int)
6086          */
notifyItemChanged(int position, Object payload)6087         public final void notifyItemChanged(int position, Object payload) {
6088             mObservable.notifyItemRangeChanged(position, 1, payload);
6089         }
6090 
6091         /**
6092          * Notify any registered observers that the <code>itemCount</code> items starting at
6093          * position <code>positionStart</code> have changed.
6094          * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
6095          *
6096          * <p>This is an item change event, not a structural change event. It indicates that
6097          * any reflection of the data in the given position range is out of date and should
6098          * be updated. The items in the given range retain the same identity.</p>
6099          *
6100          * @param positionStart Position of the first item that has changed
6101          * @param itemCount Number of items that have changed
6102          *
6103          * @see #notifyItemChanged(int)
6104          */
notifyItemRangeChanged(int positionStart, int itemCount)6105         public final void notifyItemRangeChanged(int positionStart, int itemCount) {
6106             mObservable.notifyItemRangeChanged(positionStart, itemCount);
6107         }
6108 
6109         /**
6110          * Notify any registered observers that the <code>itemCount</code> items starting at
6111          * position<code>positionStart</code> have changed. An optional payload can be
6112          * passed to each changed item.
6113          *
6114          * <p>This is an item change event, not a structural change event. It indicates that any
6115          * reflection of the data in the given position range is out of date and should be updated.
6116          * The items in the given range retain the same identity.
6117          * </p>
6118          *
6119          * <p>
6120          * Client can optionally pass a payload for partial change. These payloads will be merged
6121          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6122          * item is already represented by a ViewHolder and it will be rebound to the same
6123          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6124          * payloads on that item and prevent future payload until
6125          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6126          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6127          * attached, the payload will be simply dropped.
6128          *
6129          * @param positionStart Position of the first item that has changed
6130          * @param itemCount Number of items that have changed
6131          * @param payload  Optional parameter, use null to identify a "full" update
6132          *
6133          * @see #notifyItemChanged(int)
6134          */
notifyItemRangeChanged(int positionStart, int itemCount, Object payload)6135         public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
6136             mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
6137         }
6138 
6139         /**
6140          * Notify any registered observers that the item reflected at <code>position</code>
6141          * has been newly inserted. The item previously at <code>position</code> is now at
6142          * position <code>position + 1</code>.
6143          *
6144          * <p>This is a structural change event. Representations of other existing items in the
6145          * data set are still considered up to date and will not be rebound, though their
6146          * positions may be altered.</p>
6147          *
6148          * @param position Position of the newly inserted item in the data set
6149          *
6150          * @see #notifyItemRangeInserted(int, int)
6151          */
notifyItemInserted(int position)6152         public final void notifyItemInserted(int position) {
6153             mObservable.notifyItemRangeInserted(position, 1);
6154         }
6155 
6156         /**
6157          * Notify any registered observers that the item reflected at <code>fromPosition</code>
6158          * has been moved to <code>toPosition</code>.
6159          *
6160          * <p>This is a structural change event. Representations of other existing items in the
6161          * data set are still considered up to date and will not be rebound, though their
6162          * positions may be altered.</p>
6163          *
6164          * @param fromPosition Previous position of the item.
6165          * @param toPosition New position of the item.
6166          */
notifyItemMoved(int fromPosition, int toPosition)6167         public final void notifyItemMoved(int fromPosition, int toPosition) {
6168             mObservable.notifyItemMoved(fromPosition, toPosition);
6169         }
6170 
6171         /**
6172          * Notify any registered observers that the currently reflected <code>itemCount</code>
6173          * items starting at <code>positionStart</code> have been newly inserted. The items
6174          * previously located at <code>positionStart</code> and beyond can now be found starting
6175          * at position <code>positionStart + itemCount</code>.
6176          *
6177          * <p>This is a structural change event. Representations of other existing items in the
6178          * data set are still considered up to date and will not be rebound, though their positions
6179          * may be altered.</p>
6180          *
6181          * @param positionStart Position of the first item that was inserted
6182          * @param itemCount Number of items inserted
6183          *
6184          * @see #notifyItemInserted(int)
6185          */
notifyItemRangeInserted(int positionStart, int itemCount)6186         public final void notifyItemRangeInserted(int positionStart, int itemCount) {
6187             mObservable.notifyItemRangeInserted(positionStart, itemCount);
6188         }
6189 
6190         /**
6191          * Notify any registered observers that the item previously located at <code>position</code>
6192          * has been removed from the data set. The items previously located at and after
6193          * <code>position</code> may now be found at <code>oldPosition - 1</code>.
6194          *
6195          * <p>This is a structural change event. Representations of other existing items in the
6196          * data set are still considered up to date and will not be rebound, though their positions
6197          * may be altered.</p>
6198          *
6199          * @param position Position of the item that has now been removed
6200          *
6201          * @see #notifyItemRangeRemoved(int, int)
6202          */
notifyItemRemoved(int position)6203         public final void notifyItemRemoved(int position) {
6204             mObservable.notifyItemRangeRemoved(position, 1);
6205         }
6206 
6207         /**
6208          * Notify any registered observers that the <code>itemCount</code> items previously
6209          * located at <code>positionStart</code> have been removed from the data set. The items
6210          * previously located at and after <code>positionStart + itemCount</code> may now be found
6211          * at <code>oldPosition - itemCount</code>.
6212          *
6213          * <p>This is a structural change event. Representations of other existing items in the data
6214          * set are still considered up to date and will not be rebound, though their positions
6215          * may be altered.</p>
6216          *
6217          * @param positionStart Previous position of the first item that was removed
6218          * @param itemCount Number of items removed from the data set
6219          */
notifyItemRangeRemoved(int positionStart, int itemCount)6220         public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
6221             mObservable.notifyItemRangeRemoved(positionStart, itemCount);
6222         }
6223     }
6224 
dispatchChildDetached(View child)6225     private void dispatchChildDetached(View child) {
6226         final ViewHolder viewHolder = getChildViewHolderInt(child);
6227         onChildDetachedFromWindow(child);
6228         if (mAdapter != null && viewHolder != null) {
6229             mAdapter.onViewDetachedFromWindow(viewHolder);
6230         }
6231         if (mOnChildAttachStateListeners != null) {
6232             final int cnt = mOnChildAttachStateListeners.size();
6233             for (int i = cnt - 1; i >= 0; i--) {
6234                 mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
6235             }
6236         }
6237     }
6238 
dispatchChildAttached(View child)6239     private void dispatchChildAttached(View child) {
6240         final ViewHolder viewHolder = getChildViewHolderInt(child);
6241         onChildAttachedToWindow(child);
6242         if (mAdapter != null && viewHolder != null) {
6243             mAdapter.onViewAttachedToWindow(viewHolder);
6244         }
6245         if (mOnChildAttachStateListeners != null) {
6246             final int cnt = mOnChildAttachStateListeners.size();
6247             for (int i = cnt - 1; i >= 0; i--) {
6248                 mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
6249             }
6250         }
6251     }
6252 
6253     /**
6254      * A <code>LayoutManager</code> is responsible for measuring and positioning item views
6255      * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
6256      * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
6257      * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
6258      * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
6259      * layout managers are provided for general use.
6260      * <p/>
6261      * If the LayoutManager specifies a default constructor or one with the signature
6262      * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
6263      * instantiate and set the LayoutManager when being inflated. Most used properties can
6264      * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
6265      * a LayoutManager specifies both constructors, the non-default constructor will take
6266      * precedence.
6267      *
6268      */
6269     public static abstract class LayoutManager {
6270         ChildHelper mChildHelper;
6271         RecyclerView mRecyclerView;
6272 
6273         @Nullable
6274         SmoothScroller mSmoothScroller;
6275 
6276         private boolean mRequestedSimpleAnimations = false;
6277 
6278         boolean mIsAttachedToWindow = false;
6279 
6280         private boolean mAutoMeasure = false;
6281 
6282         /**
6283          * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
6284          * if the space that will be given to it is already larger than what it has measured before.
6285          */
6286         private boolean mMeasurementCacheEnabled = true;
6287 
6288 
6289         /**
6290          * These measure specs might be the measure specs that were passed into RecyclerView's
6291          * onMeasure method OR fake measure specs created by the RecyclerView.
6292          * For example, when a layout is run, RecyclerView always sets these specs to be
6293          * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
6294          * <p>
6295          * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
6296          * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
6297          * corrupt values. Older platforms have no responsibility to provide a size if they set
6298          * mode to unspecified.
6299          */
6300         private int mWidthMode, mHeightMode;
6301         private int mWidth, mHeight;
6302 
setRecyclerView(RecyclerView recyclerView)6303         void setRecyclerView(RecyclerView recyclerView) {
6304             if (recyclerView == null) {
6305                 mRecyclerView = null;
6306                 mChildHelper = null;
6307                 mWidth = 0;
6308                 mHeight = 0;
6309             } else {
6310                 mRecyclerView = recyclerView;
6311                 mChildHelper = recyclerView.mChildHelper;
6312                 mWidth = recyclerView.getWidth();
6313                 mHeight = recyclerView.getHeight();
6314             }
6315             mWidthMode = MeasureSpec.EXACTLY;
6316             mHeightMode = MeasureSpec.EXACTLY;
6317         }
6318 
setMeasureSpecs(int wSpec, int hSpec)6319         void setMeasureSpecs(int wSpec, int hSpec) {
6320             mWidth = MeasureSpec.getSize(wSpec);
6321             mWidthMode = MeasureSpec.getMode(wSpec);
6322             if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
6323                 mWidth = 0;
6324             }
6325 
6326             mHeight = MeasureSpec.getSize(hSpec);
6327             mHeightMode = MeasureSpec.getMode(hSpec);
6328             if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
6329                 mHeight = 0;
6330             }
6331         }
6332 
6333         /**
6334          * Called after a layout is calculated during a measure pass when using auto-measure.
6335          * <p>
6336          * It simply traverses all children to calculate a bounding box then calls
6337          * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
6338          * if they need to handle the bounding box differently.
6339          * <p>
6340          * For example, GridLayoutManager override that method to ensure that even if a column is
6341          * empty, the GridLayoutManager still measures wide enough to include it.
6342          *
6343          * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
6344          * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
6345          */
setMeasuredDimensionFromChildren(int widthSpec, int heightSpec)6346         void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
6347             final int count = getChildCount();
6348             if (count == 0) {
6349                 mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
6350                 return;
6351             }
6352             int minX = Integer.MAX_VALUE;
6353             int minY = Integer.MAX_VALUE;
6354             int maxX = Integer.MIN_VALUE;
6355             int maxY = Integer.MIN_VALUE;
6356 
6357             for (int i = 0; i < count; i++) {
6358                 View child = getChildAt(i);
6359                 LayoutParams lp = (LayoutParams) child.getLayoutParams();
6360                 final Rect bounds = mRecyclerView.mTempRect;
6361                 getDecoratedBoundsWithMargins(child, bounds);
6362                 if (bounds.left < minX) {
6363                     minX = bounds.left;
6364                 }
6365                 if (bounds.right > maxX) {
6366                     maxX = bounds.right;
6367                 }
6368                 if (bounds.top < minY) {
6369                     minY = bounds.top;
6370                 }
6371                 if (bounds.bottom > maxY) {
6372                     maxY = bounds.bottom;
6373                 }
6374             }
6375             mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
6376             setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
6377         }
6378 
6379         /**
6380          * Sets the measured dimensions from the given bounding box of the children and the
6381          * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
6382          * called after the RecyclerView calls
6383          * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a measurement pass.
6384          * <p>
6385          * This method should call {@link #setMeasuredDimension(int, int)}.
6386          * <p>
6387          * The default implementation adds the RecyclerView's padding to the given bounding box
6388          * then caps the value to be within the given measurement specs.
6389          * <p>
6390          * This method is only called if the LayoutManager opted into the auto measurement API.
6391          *
6392          * @param childrenBounds The bounding box of all children
6393          * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
6394          * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
6395          *
6396          * @see #setAutoMeasureEnabled(boolean)
6397          */
setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec)6398         public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
6399             int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
6400             int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
6401             int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
6402             int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
6403             setMeasuredDimension(width, height);
6404         }
6405 
6406         /**
6407          * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
6408          */
requestLayout()6409         public void requestLayout() {
6410             if(mRecyclerView != null) {
6411                 mRecyclerView.requestLayout();
6412             }
6413         }
6414 
6415         /**
6416          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
6417          * {@link IllegalStateException} if it <b>is not</b>.
6418          *
6419          * @param message The message for the exception. Can be null.
6420          * @see #assertNotInLayoutOrScroll(String)
6421          */
assertInLayoutOrScroll(String message)6422         public void assertInLayoutOrScroll(String message) {
6423             if (mRecyclerView != null) {
6424                 mRecyclerView.assertInLayoutOrScroll(message);
6425             }
6426         }
6427 
6428         /**
6429          * Chooses a size from the given specs and parameters that is closest to the desired size
6430          * and also complies with the spec.
6431          *
6432          * @param spec The measureSpec
6433          * @param desired The preferred measurement
6434          * @param min The minimum value
6435          *
6436          * @return A size that fits to the given specs
6437          */
chooseSize(int spec, int desired, int min)6438         public static int chooseSize(int spec, int desired, int min) {
6439             final int mode = View.MeasureSpec.getMode(spec);
6440             final int size = View.MeasureSpec.getSize(spec);
6441             switch (mode) {
6442                 case View.MeasureSpec.EXACTLY:
6443                     return size;
6444                 case View.MeasureSpec.AT_MOST:
6445                     return Math.min(size, Math.max(desired, min));
6446                 case View.MeasureSpec.UNSPECIFIED:
6447                 default:
6448                     return Math.max(desired, min);
6449             }
6450         }
6451 
6452         /**
6453          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
6454          * {@link IllegalStateException} if it <b>is</b>.
6455          *
6456          * @param message The message for the exception. Can be null.
6457          * @see #assertInLayoutOrScroll(String)
6458          */
assertNotInLayoutOrScroll(String message)6459         public void assertNotInLayoutOrScroll(String message) {
6460             if (mRecyclerView != null) {
6461                 mRecyclerView.assertNotInLayoutOrScroll(message);
6462             }
6463         }
6464 
6465         /**
6466          * Defines whether the layout should be measured by the RecyclerView or the LayoutManager
6467          * wants to handle the layout measurements itself.
6468          * <p>
6469          * This method is usually called by the LayoutManager with value {@code true} if it wants
6470          * to support WRAP_CONTENT. If you are using a public LayoutManager but want to customize
6471          * the measurement logic, you can call this method with {@code false} and override
6472          * {@link LayoutManager#onMeasure(int, int)} to implement your custom measurement logic.
6473          * <p>
6474          * AutoMeasure is a convenience mechanism for LayoutManagers to easily wrap their content or
6475          * handle various specs provided by the RecyclerView's parent.
6476          * It works by calling {@link LayoutManager#onLayoutChildren(Recycler, State)} during an
6477          * {@link RecyclerView#onMeasure(int, int)} call, then calculating desired dimensions based
6478          * on children's positions. It does this while supporting all existing animation
6479          * capabilities of the RecyclerView.
6480          * <p>
6481          * AutoMeasure works as follows:
6482          * <ol>
6483          * <li>LayoutManager should call {@code setAutoMeasureEnabled(true)} to enable it. All of
6484          * the framework LayoutManagers use {@code auto-measure}.</li>
6485          * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided specs are
6486          * exact, RecyclerView will only call LayoutManager's {@code onMeasure} and return without
6487          * doing any layout calculation.</li>
6488          * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
6489          * layout process in {@code onMeasure} call. It will process all pending Adapter updates and
6490          * decide whether to run a predictive layout or not. If it decides to do so, it will first
6491          * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
6492          * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
6493          * return the width and height of the RecyclerView as of the last layout calculation.
6494          * <p>
6495          * After handling the predictive case, RecyclerView will call
6496          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
6497          * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
6498          * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
6499          * {@link #getWidth()} and {@link #getWidthMode()}.</li>
6500          * <li>After the layout calculation, RecyclerView sets the measured width & height by
6501          * calculating the bounding box for the children (+ RecyclerView's padding). The
6502          * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
6503          * different values. For instance, GridLayoutManager overrides this value to handle the case
6504          * where if it is vertical and has 3 columns but only 2 items, it should still measure its
6505          * width to fit 3 items, not 2.</li>
6506          * <li>Any following on measure call to the RecyclerView will run
6507          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
6508          * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
6509          * take care of which views are actually added / removed / moved / changed for animations so
6510          * that the LayoutManager should not worry about them and handle each
6511          * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.
6512          * </li>
6513          * <li>When measure is complete and RecyclerView's
6514          * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
6515          * whether it already did layout calculations during the measure pass and if so, it re-uses
6516          * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
6517          * if the last measure spec was different from the final dimensions or adapter contents
6518          * have changed between the measure call and the layout call.</li>
6519          * <li>Finally, animations are calculated and run as usual.</li>
6520          * </ol>
6521          *
6522          * @param enabled <code>True</code> if the Layout should be measured by the
6523          *                             RecyclerView, <code>false</code> if the LayoutManager wants
6524          *                             to measure itself.
6525          *
6526          * @see #setMeasuredDimension(Rect, int, int)
6527          * @see #isAutoMeasureEnabled()
6528          */
setAutoMeasureEnabled(boolean enabled)6529         public void setAutoMeasureEnabled(boolean enabled) {
6530             mAutoMeasure = enabled;
6531         }
6532 
6533         /**
6534          * Returns whether the LayoutManager uses the automatic measurement API or not.
6535          *
6536          * @return <code>True</code> if the LayoutManager is measured by the RecyclerView or
6537          * <code>false</code> if it measures itself.
6538          *
6539          * @see #setAutoMeasureEnabled(boolean)
6540          */
isAutoMeasureEnabled()6541         public boolean isAutoMeasureEnabled() {
6542             return mAutoMeasure;
6543         }
6544 
6545         /**
6546          * Returns whether this LayoutManager supports automatic item animations.
6547          * A LayoutManager wishing to support item animations should obey certain
6548          * rules as outlined in {@link #onLayoutChildren(Recycler, State)}.
6549          * The default return value is <code>false</code>, so subclasses of LayoutManager
6550          * will not get predictive item animations by default.
6551          *
6552          * <p>Whether item animations are enabled in a RecyclerView is determined both
6553          * by the return value from this method and the
6554          * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
6555          * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
6556          * method returns false, then simple item animations will be enabled, in which
6557          * views that are moving onto or off of the screen are simply faded in/out. If
6558          * the RecyclerView has a non-null ItemAnimator and this method returns true,
6559          * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to
6560          * setup up the information needed to more intelligently predict where appearing
6561          * and disappearing views should be animated from/to.</p>
6562          *
6563          * @return true if predictive item animations should be enabled, false otherwise
6564          */
supportsPredictiveItemAnimations()6565         public boolean supportsPredictiveItemAnimations() {
6566             return false;
6567         }
6568 
dispatchAttachedToWindow(RecyclerView view)6569         void dispatchAttachedToWindow(RecyclerView view) {
6570             mIsAttachedToWindow = true;
6571             onAttachedToWindow(view);
6572         }
6573 
dispatchDetachedFromWindow(RecyclerView view, Recycler recycler)6574         void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
6575             mIsAttachedToWindow = false;
6576             onDetachedFromWindow(view, recycler);
6577         }
6578 
6579         /**
6580          * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
6581          * to a window.
6582          *
6583          * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
6584          * is attached to window.
6585          */
isAttachedToWindow()6586         public boolean isAttachedToWindow() {
6587             return mIsAttachedToWindow;
6588         }
6589 
6590         /**
6591          * Causes the Runnable to execute on the next animation time step.
6592          * The runnable will be run on the user interface thread.
6593          * <p>
6594          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
6595          *
6596          * @param action The Runnable that will be executed.
6597          *
6598          * @see #removeCallbacks
6599          */
postOnAnimation(Runnable action)6600         public void postOnAnimation(Runnable action) {
6601             if (mRecyclerView != null) {
6602                 ViewCompat.postOnAnimation(mRecyclerView, action);
6603             }
6604         }
6605 
6606         /**
6607          * Removes the specified Runnable from the message queue.
6608          * <p>
6609          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
6610          *
6611          * @param action The Runnable to remove from the message handling queue
6612          *
6613          * @return true if RecyclerView could ask the Handler to remove the Runnable,
6614          *         false otherwise. When the returned value is true, the Runnable
6615          *         may or may not have been actually removed from the message queue
6616          *         (for instance, if the Runnable was not in the queue already.)
6617          *
6618          * @see #postOnAnimation
6619          */
removeCallbacks(Runnable action)6620         public boolean removeCallbacks(Runnable action) {
6621             if (mRecyclerView != null) {
6622                 return mRecyclerView.removeCallbacks(action);
6623             }
6624             return false;
6625         }
6626         /**
6627          * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
6628          * is attached to a window.
6629          * <p>
6630          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
6631          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
6632          * not requested on the RecyclerView while it was detached.
6633          * <p>
6634          * Subclass implementations should always call through to the superclass implementation.
6635          *
6636          * @param view The RecyclerView this LayoutManager is bound to
6637          *
6638          * @see #onDetachedFromWindow(RecyclerView, Recycler)
6639          */
6640         @CallSuper
onAttachedToWindow(RecyclerView view)6641         public void onAttachedToWindow(RecyclerView view) {
6642         }
6643 
6644         /**
6645          * @deprecated
6646          * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
6647          */
6648         @Deprecated
onDetachedFromWindow(RecyclerView view)6649         public void onDetachedFromWindow(RecyclerView view) {
6650 
6651         }
6652 
6653         /**
6654          * Called when this LayoutManager is detached from its parent RecyclerView or when
6655          * its parent RecyclerView is detached from its window.
6656          * <p>
6657          * LayoutManager should clear all of its View references as another LayoutManager might be
6658          * assigned to the RecyclerView.
6659          * <p>
6660          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
6661          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
6662          * not requested on the RecyclerView while it was detached.
6663          * <p>
6664          * If your LayoutManager has View references that it cleans in on-detach, it should also
6665          * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
6666          * RecyclerView is re-attached.
6667          * <p>
6668          * Subclass implementations should always call through to the superclass implementation.
6669          *
6670          * @param view The RecyclerView this LayoutManager is bound to
6671          * @param recycler The recycler to use if you prefer to recycle your children instead of
6672          *                 keeping them around.
6673          *
6674          * @see #onAttachedToWindow(RecyclerView)
6675          */
6676         @CallSuper
onDetachedFromWindow(RecyclerView view, Recycler recycler)6677         public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
6678             onDetachedFromWindow(view);
6679         }
6680 
6681         /**
6682          * Check if the RecyclerView is configured to clip child views to its padding.
6683          *
6684          * @return true if this RecyclerView clips children to its padding, false otherwise
6685          */
getClipToPadding()6686         public boolean getClipToPadding() {
6687             return mRecyclerView != null && mRecyclerView.mClipToPadding;
6688         }
6689 
6690         /**
6691          * Lay out all relevant child views from the given adapter.
6692          *
6693          * The LayoutManager is in charge of the behavior of item animations. By default,
6694          * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
6695          * item animations are enabled. This means that add/remove operations on the
6696          * adapter will result in animations to add new or appearing items, removed or
6697          * disappearing items, and moved items. If a LayoutManager returns false from
6698          * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
6699          * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
6700          * RecyclerView will have enough information to run those animations in a simple
6701          * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
6702          * simply fade views in and out, whether they are actually added/removed or whether
6703          * they are moved on or off the screen due to other add/remove operations.
6704          *
6705          * <p>A LayoutManager wanting a better item animation experience, where items can be
6706          * animated onto and off of the screen according to where the items exist when they
6707          * are not on screen, then the LayoutManager should return true from
6708          * {@link #supportsPredictiveItemAnimations()} and add additional logic to
6709          * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
6710          * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
6711          * once as a "pre" layout step to determine where items would have been prior to
6712          * a real layout, and again to do the "real" layout. In the pre-layout phase,
6713          * items will remember their pre-layout positions to allow them to be laid out
6714          * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
6715          * be returned from the scrap to help determine correct placement of other items.
6716          * These removed items should not be added to the child list, but should be used
6717          * to help calculate correct positioning of other views, including views that
6718          * were not previously onscreen (referred to as APPEARING views), but whose
6719          * pre-layout offscreen position can be determined given the extra
6720          * information about the pre-layout removed views.</p>
6721          *
6722          * <p>The second layout pass is the real layout in which only non-removed views
6723          * will be used. The only additional requirement during this pass is, if
6724          * {@link #supportsPredictiveItemAnimations()} returns true, to note which
6725          * views exist in the child list prior to layout and which are not there after
6726          * layout (referred to as DISAPPEARING views), and to position/layout those views
6727          * appropriately, without regard to the actual bounds of the RecyclerView. This allows
6728          * the animation system to know the location to which to animate these disappearing
6729          * views.</p>
6730          *
6731          * <p>The default LayoutManager implementations for RecyclerView handle all of these
6732          * requirements for animations already. Clients of RecyclerView can either use one
6733          * of these layout managers directly or look at their implementations of
6734          * onLayoutChildren() to see how they account for the APPEARING and
6735          * DISAPPEARING views.</p>
6736          *
6737          * @param recycler         Recycler to use for fetching potentially cached views for a
6738          *                         position
6739          * @param state            Transient state of RecyclerView
6740          */
onLayoutChildren(Recycler recycler, State state)6741         public void onLayoutChildren(Recycler recycler, State state) {
6742             Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
6743         }
6744 
6745         /**
6746          * Called after a full layout calculation is finished. The layout calculation may include
6747          * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
6748          * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
6749          * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
6750          * <p>
6751          * This is a good place for the LayoutManager to do some cleanup like pending scroll
6752          * position, saved state etc.
6753          *
6754          * @param state Transient state of RecyclerView
6755          */
onLayoutCompleted(State state)6756         public void onLayoutCompleted(State state) {
6757         }
6758 
6759         /**
6760          * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
6761          *
6762          * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
6763          * to store extra information specific to the layout. Client code should subclass
6764          * {@link RecyclerView.LayoutParams} for this purpose.</p>
6765          *
6766          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
6767          * you must also override
6768          * {@link #checkLayoutParams(LayoutParams)},
6769          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
6770          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
6771          *
6772          * @return A new LayoutParams for a child view
6773          */
generateDefaultLayoutParams()6774         public abstract LayoutParams generateDefaultLayoutParams();
6775 
6776         /**
6777          * Determines the validity of the supplied LayoutParams object.
6778          *
6779          * <p>This should check to make sure that the object is of the correct type
6780          * and all values are within acceptable ranges. The default implementation
6781          * returns <code>true</code> for non-null params.</p>
6782          *
6783          * @param lp LayoutParams object to check
6784          * @return true if this LayoutParams object is valid, false otherwise
6785          */
checkLayoutParams(LayoutParams lp)6786         public boolean checkLayoutParams(LayoutParams lp) {
6787             return lp != null;
6788         }
6789 
6790         /**
6791          * Create a LayoutParams object suitable for this LayoutManager, copying relevant
6792          * values from the supplied LayoutParams object if possible.
6793          *
6794          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
6795          * you must also override
6796          * {@link #checkLayoutParams(LayoutParams)},
6797          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
6798          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
6799          *
6800          * @param lp Source LayoutParams object to copy values from
6801          * @return a new LayoutParams object
6802          */
generateLayoutParams(ViewGroup.LayoutParams lp)6803         public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
6804             if (lp instanceof LayoutParams) {
6805                 return new LayoutParams((LayoutParams) lp);
6806             } else if (lp instanceof MarginLayoutParams) {
6807                 return new LayoutParams((MarginLayoutParams) lp);
6808             } else {
6809                 return new LayoutParams(lp);
6810             }
6811         }
6812 
6813         /**
6814          * Create a LayoutParams object suitable for this LayoutManager from
6815          * an inflated layout resource.
6816          *
6817          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
6818          * you must also override
6819          * {@link #checkLayoutParams(LayoutParams)},
6820          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
6821          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
6822          *
6823          * @param c Context for obtaining styled attributes
6824          * @param attrs AttributeSet describing the supplied arguments
6825          * @return a new LayoutParams object
6826          */
generateLayoutParams(Context c, AttributeSet attrs)6827         public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
6828             return new LayoutParams(c, attrs);
6829         }
6830 
6831         /**
6832          * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
6833          * The default implementation does nothing and returns 0.
6834          *
6835          * @param dx            distance to scroll by in pixels. X increases as scroll position
6836          *                      approaches the right.
6837          * @param recycler      Recycler to use for fetching potentially cached views for a
6838          *                      position
6839          * @param state         Transient state of RecyclerView
6840          * @return The actual distance scrolled. The return value will be negative if dx was
6841          * negative and scrolling proceeeded in that direction.
6842          * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
6843          */
scrollHorizontallyBy(int dx, Recycler recycler, State state)6844         public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
6845             return 0;
6846         }
6847 
6848         /**
6849          * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
6850          * The default implementation does nothing and returns 0.
6851          *
6852          * @param dy            distance to scroll in pixels. Y increases as scroll position
6853          *                      approaches the bottom.
6854          * @param recycler      Recycler to use for fetching potentially cached views for a
6855          *                      position
6856          * @param state         Transient state of RecyclerView
6857          * @return The actual distance scrolled. The return value will be negative if dy was
6858          * negative and scrolling proceeeded in that direction.
6859          * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
6860          */
scrollVerticallyBy(int dy, Recycler recycler, State state)6861         public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
6862             return 0;
6863         }
6864 
6865         /**
6866          * Query if horizontal scrolling is currently supported. The default implementation
6867          * returns false.
6868          *
6869          * @return True if this LayoutManager can scroll the current contents horizontally
6870          */
canScrollHorizontally()6871         public boolean canScrollHorizontally() {
6872             return false;
6873         }
6874 
6875         /**
6876          * Query if vertical scrolling is currently supported. The default implementation
6877          * returns false.
6878          *
6879          * @return True if this LayoutManager can scroll the current contents vertically
6880          */
canScrollVertically()6881         public boolean canScrollVertically() {
6882             return false;
6883         }
6884 
6885         /**
6886          * Scroll to the specified adapter position.
6887          *
6888          * Actual position of the item on the screen depends on the LayoutManager implementation.
6889          * @param position Scroll to this adapter position.
6890          */
scrollToPosition(int position)6891         public void scrollToPosition(int position) {
6892             if (DEBUG) {
6893                 Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
6894             }
6895         }
6896 
6897         /**
6898          * <p>Smooth scroll to the specified adapter position.</p>
6899          * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
6900          * instance and call {@link #startSmoothScroll(SmoothScroller)}.
6901          * </p>
6902          * @param recyclerView The RecyclerView to which this layout manager is attached
6903          * @param state    Current State of RecyclerView
6904          * @param position Scroll to this adapter position.
6905          */
smoothScrollToPosition(RecyclerView recyclerView, State state, int position)6906         public void smoothScrollToPosition(RecyclerView recyclerView, State state,
6907                 int position) {
6908             Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
6909         }
6910 
6911         /**
6912          * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
6913          * <p>Calling this method will cancel any previous smooth scroll request.</p>
6914          * @param smoothScroller Unstance which defines how smooth scroll should be animated
6915          */
startSmoothScroll(SmoothScroller smoothScroller)6916         public void startSmoothScroll(SmoothScroller smoothScroller) {
6917             if (mSmoothScroller != null && smoothScroller != mSmoothScroller
6918                     && mSmoothScroller.isRunning()) {
6919                 mSmoothScroller.stop();
6920             }
6921             mSmoothScroller = smoothScroller;
6922             mSmoothScroller.start(mRecyclerView, this);
6923         }
6924 
6925         /**
6926          * @return true if RecycylerView is currently in the state of smooth scrolling.
6927          */
isSmoothScrolling()6928         public boolean isSmoothScrolling() {
6929             return mSmoothScroller != null && mSmoothScroller.isRunning();
6930         }
6931 
6932 
6933         /**
6934          * Returns the resolved layout direction for this RecyclerView.
6935          *
6936          * @return {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout
6937          * direction is RTL or returns
6938          * {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction
6939          * is not RTL.
6940          */
getLayoutDirection()6941         public int getLayoutDirection() {
6942             return ViewCompat.getLayoutDirection(mRecyclerView);
6943         }
6944 
6945         /**
6946          * Ends all animations on the view created by the {@link ItemAnimator}.
6947          *
6948          * @param view The View for which the animations should be ended.
6949          * @see RecyclerView.ItemAnimator#endAnimations()
6950          */
endAnimation(View view)6951         public void endAnimation(View view) {
6952             if (mRecyclerView.mItemAnimator != null) {
6953                 mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
6954             }
6955         }
6956 
6957         /**
6958          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
6959          * to the layout that is known to be going away, either because it has been
6960          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
6961          * visible portion of the container but is being laid out in order to inform RecyclerView
6962          * in how to animate the item out of view.
6963          * <p>
6964          * Views added via this method are going to be invisible to LayoutManager after the
6965          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
6966          * or won't be included in {@link #getChildCount()} method.
6967          *
6968          * @param child View to add and then remove with animation.
6969          */
addDisappearingView(View child)6970         public void addDisappearingView(View child) {
6971             addDisappearingView(child, -1);
6972         }
6973 
6974         /**
6975          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
6976          * to the layout that is known to be going away, either because it has been
6977          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
6978          * visible portion of the container but is being laid out in order to inform RecyclerView
6979          * in how to animate the item out of view.
6980          * <p>
6981          * Views added via this method are going to be invisible to LayoutManager after the
6982          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
6983          * or won't be included in {@link #getChildCount()} method.
6984          *
6985          * @param child View to add and then remove with animation.
6986          * @param index Index of the view.
6987          */
addDisappearingView(View child, int index)6988         public void addDisappearingView(View child, int index) {
6989             addViewInt(child, index, true);
6990         }
6991 
6992         /**
6993          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
6994          * use this method to add views obtained from a {@link Recycler} using
6995          * {@link Recycler#getViewForPosition(int)}.
6996          *
6997          * @param child View to add
6998          */
addView(View child)6999         public void addView(View child) {
7000             addView(child, -1);
7001         }
7002 
7003         /**
7004          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
7005          * use this method to add views obtained from a {@link Recycler} using
7006          * {@link Recycler#getViewForPosition(int)}.
7007          *
7008          * @param child View to add
7009          * @param index Index to add child at
7010          */
addView(View child, int index)7011         public void addView(View child, int index) {
7012             addViewInt(child, index, false);
7013         }
7014 
addViewInt(View child, int index, boolean disappearing)7015         private void addViewInt(View child, int index, boolean disappearing) {
7016             final ViewHolder holder = getChildViewHolderInt(child);
7017             if (disappearing || holder.isRemoved()) {
7018                 // these views will be hidden at the end of the layout pass.
7019                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
7020             } else {
7021                 // This may look like unnecessary but may happen if layout manager supports
7022                 // predictive layouts and adapter removed then re-added the same item.
7023                 // In this case, added version will be visible in the post layout (because add is
7024                 // deferred) but RV will still bind it to the same View.
7025                 // So if a View re-appears in post layout pass, remove it from disappearing list.
7026                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
7027             }
7028             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
7029             if (holder.wasReturnedFromScrap() || holder.isScrap()) {
7030                 if (holder.isScrap()) {
7031                     holder.unScrap();
7032                 } else {
7033                     holder.clearReturnedFromScrapFlag();
7034                 }
7035                 mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
7036                 if (DISPATCH_TEMP_DETACH) {
7037                     ViewCompat.dispatchFinishTemporaryDetach(child);
7038                 }
7039             } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
7040                 // ensure in correct position
7041                 int currentIndex = mChildHelper.indexOfChild(child);
7042                 if (index == -1) {
7043                     index = mChildHelper.getChildCount();
7044                 }
7045                 if (currentIndex == -1) {
7046                     throw new IllegalStateException("Added View has RecyclerView as parent but"
7047                             + " view is not a real child. Unfiltered index:"
7048                             + mRecyclerView.indexOfChild(child));
7049                 }
7050                 if (currentIndex != index) {
7051                     mRecyclerView.mLayout.moveView(currentIndex, index);
7052                 }
7053             } else {
7054                 mChildHelper.addView(child, index, false);
7055                 lp.mInsetsDirty = true;
7056                 if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
7057                     mSmoothScroller.onChildAttachedToWindow(child);
7058                 }
7059             }
7060             if (lp.mPendingInvalidate) {
7061                 if (DEBUG) {
7062                     Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
7063                 }
7064                 holder.itemView.invalidate();
7065                 lp.mPendingInvalidate = false;
7066             }
7067         }
7068 
7069         /**
7070          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
7071          * use this method to completely remove a child view that is no longer needed.
7072          * LayoutManagers should strongly consider recycling removed views using
7073          * {@link Recycler#recycleView(android.view.View)}.
7074          *
7075          * @param child View to remove
7076          */
removeView(View child)7077         public void removeView(View child) {
7078             mChildHelper.removeView(child);
7079         }
7080 
7081         /**
7082          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
7083          * use this method to completely remove a child view that is no longer needed.
7084          * LayoutManagers should strongly consider recycling removed views using
7085          * {@link Recycler#recycleView(android.view.View)}.
7086          *
7087          * @param index Index of the child view to remove
7088          */
removeViewAt(int index)7089         public void removeViewAt(int index) {
7090             final View child = getChildAt(index);
7091             if (child != null) {
7092                 mChildHelper.removeViewAt(index);
7093             }
7094         }
7095 
7096         /**
7097          * Remove all views from the currently attached RecyclerView. This will not recycle
7098          * any of the affected views; the LayoutManager is responsible for doing so if desired.
7099          */
removeAllViews()7100         public void removeAllViews() {
7101             // Only remove non-animating views
7102             final int childCount = getChildCount();
7103             for (int i = childCount - 1; i >= 0; i--) {
7104                 mChildHelper.removeViewAt(i);
7105             }
7106         }
7107 
7108         /**
7109          * Returns offset of the RecyclerView's text baseline from the its top boundary.
7110          *
7111          * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
7112          * there is no baseline.
7113          */
getBaseline()7114         public int getBaseline() {
7115             return -1;
7116         }
7117 
7118         /**
7119          * Returns the adapter position of the item represented by the given View. This does not
7120          * contain any adapter changes that might have happened after the last layout.
7121          *
7122          * @param view The view to query
7123          * @return The adapter position of the item which is rendered by this View.
7124          */
getPosition(View view)7125         public int getPosition(View view) {
7126             return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
7127         }
7128 
7129         /**
7130          * Returns the View type defined by the adapter.
7131          *
7132          * @param view The view to query
7133          * @return The type of the view assigned by the adapter.
7134          */
getItemViewType(View view)7135         public int getItemViewType(View view) {
7136             return getChildViewHolderInt(view).getItemViewType();
7137         }
7138 
7139         /**
7140          * Traverses the ancestors of the given view and returns the item view that contains it
7141          * and also a direct child of the LayoutManager.
7142          * <p>
7143          * Note that this method may return null if the view is a child of the RecyclerView but
7144          * not a child of the LayoutManager (e.g. running a disappear animation).
7145          *
7146          * @param view The view that is a descendant of the LayoutManager.
7147          *
7148          * @return The direct child of the LayoutManager which contains the given view or null if
7149          * the provided view is not a descendant of this LayoutManager.
7150          *
7151          * @see RecyclerView#getChildViewHolder(View)
7152          * @see RecyclerView#findContainingViewHolder(View)
7153          */
7154         @Nullable
findContainingItemView(View view)7155         public View findContainingItemView(View view) {
7156             if (mRecyclerView == null) {
7157                 return null;
7158             }
7159             View found = mRecyclerView.findContainingItemView(view);
7160             if (found == null) {
7161                 return null;
7162             }
7163             if (mChildHelper.isHidden(found)) {
7164                 return null;
7165             }
7166             return found;
7167         }
7168 
7169         /**
7170          * Finds the view which represents the given adapter position.
7171          * <p>
7172          * This method traverses each child since it has no information about child order.
7173          * Override this method to improve performance if your LayoutManager keeps data about
7174          * child views.
7175          * <p>
7176          * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
7177          *
7178          * @param position Position of the item in adapter
7179          * @return The child view that represents the given position or null if the position is not
7180          * laid out
7181          */
findViewByPosition(int position)7182         public View findViewByPosition(int position) {
7183             final int childCount = getChildCount();
7184             for (int i = 0; i < childCount; i++) {
7185                 View child = getChildAt(i);
7186                 ViewHolder vh = getChildViewHolderInt(child);
7187                 if (vh == null) {
7188                     continue;
7189                 }
7190                 if (vh.getLayoutPosition() == position && !vh.shouldIgnore() &&
7191                         (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
7192                     return child;
7193                 }
7194             }
7195             return null;
7196         }
7197 
7198         /**
7199          * Temporarily detach a child view.
7200          *
7201          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
7202          * views currently attached to the RecyclerView. Generally LayoutManager implementations
7203          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
7204          * so that the detached view may be rebound and reused.</p>
7205          *
7206          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
7207          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
7208          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
7209          * before the LayoutManager entry point method called by RecyclerView returns.</p>
7210          *
7211          * @param child Child to detach
7212          */
detachView(View child)7213         public void detachView(View child) {
7214             final int ind = mChildHelper.indexOfChild(child);
7215             if (ind >= 0) {
7216                 detachViewInternal(ind, child);
7217             }
7218         }
7219 
7220         /**
7221          * Temporarily detach a child view.
7222          *
7223          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
7224          * views currently attached to the RecyclerView. Generally LayoutManager implementations
7225          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
7226          * so that the detached view may be rebound and reused.</p>
7227          *
7228          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
7229          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
7230          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
7231          * before the LayoutManager entry point method called by RecyclerView returns.</p>
7232          *
7233          * @param index Index of the child to detach
7234          */
detachViewAt(int index)7235         public void detachViewAt(int index) {
7236             detachViewInternal(index, getChildAt(index));
7237         }
7238 
detachViewInternal(int index, View view)7239         private void detachViewInternal(int index, View view) {
7240             if (DISPATCH_TEMP_DETACH) {
7241                 ViewCompat.dispatchStartTemporaryDetach(view);
7242             }
7243             mChildHelper.detachViewFromParent(index);
7244         }
7245 
7246         /**
7247          * Reattach a previously {@link #detachView(android.view.View) detached} view.
7248          * This method should not be used to reattach views that were previously
7249          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
7250          *
7251          * @param child Child to reattach
7252          * @param index Intended child index for child
7253          * @param lp LayoutParams for child
7254          */
attachView(View child, int index, LayoutParams lp)7255         public void attachView(View child, int index, LayoutParams lp) {
7256             ViewHolder vh = getChildViewHolderInt(child);
7257             if (vh.isRemoved()) {
7258                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
7259             } else {
7260                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
7261             }
7262             mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
7263             if (DISPATCH_TEMP_DETACH)  {
7264                 ViewCompat.dispatchFinishTemporaryDetach(child);
7265             }
7266         }
7267 
7268         /**
7269          * Reattach a previously {@link #detachView(android.view.View) detached} view.
7270          * This method should not be used to reattach views that were previously
7271          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
7272          *
7273          * @param child Child to reattach
7274          * @param index Intended child index for child
7275          */
attachView(View child, int index)7276         public void attachView(View child, int index) {
7277             attachView(child, index, (LayoutParams) child.getLayoutParams());
7278         }
7279 
7280         /**
7281          * Reattach a previously {@link #detachView(android.view.View) detached} view.
7282          * This method should not be used to reattach views that were previously
7283          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
7284          *
7285          * @param child Child to reattach
7286          */
attachView(View child)7287         public void attachView(View child) {
7288             attachView(child, -1);
7289         }
7290 
7291         /**
7292          * Finish removing a view that was previously temporarily
7293          * {@link #detachView(android.view.View) detached}.
7294          *
7295          * @param child Detached child to remove
7296          */
removeDetachedView(View child)7297         public void removeDetachedView(View child) {
7298             mRecyclerView.removeDetachedView(child, false);
7299         }
7300 
7301         /**
7302          * Moves a View from one position to another.
7303          *
7304          * @param fromIndex The View's initial index
7305          * @param toIndex The View's target index
7306          */
moveView(int fromIndex, int toIndex)7307         public void moveView(int fromIndex, int toIndex) {
7308             View view = getChildAt(fromIndex);
7309             if (view == null) {
7310                 throw new IllegalArgumentException("Cannot move a child from non-existing index:"
7311                         + fromIndex);
7312             }
7313             detachViewAt(fromIndex);
7314             attachView(view, toIndex);
7315         }
7316 
7317         /**
7318          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
7319          *
7320          * <p>Scrapping a view allows it to be rebound and reused to show updated or
7321          * different data.</p>
7322          *
7323          * @param child Child to detach and scrap
7324          * @param recycler Recycler to deposit the new scrap view into
7325          */
detachAndScrapView(View child, Recycler recycler)7326         public void detachAndScrapView(View child, Recycler recycler) {
7327             int index = mChildHelper.indexOfChild(child);
7328             scrapOrRecycleView(recycler, index, child);
7329         }
7330 
7331         /**
7332          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
7333          *
7334          * <p>Scrapping a view allows it to be rebound and reused to show updated or
7335          * different data.</p>
7336          *
7337          * @param index Index of child to detach and scrap
7338          * @param recycler Recycler to deposit the new scrap view into
7339          */
detachAndScrapViewAt(int index, Recycler recycler)7340         public void detachAndScrapViewAt(int index, Recycler recycler) {
7341             final View child = getChildAt(index);
7342             scrapOrRecycleView(recycler, index, child);
7343         }
7344 
7345         /**
7346          * Remove a child view and recycle it using the given Recycler.
7347          *
7348          * @param child Child to remove and recycle
7349          * @param recycler Recycler to use to recycle child
7350          */
removeAndRecycleView(View child, Recycler recycler)7351         public void removeAndRecycleView(View child, Recycler recycler) {
7352             removeView(child);
7353             recycler.recycleView(child);
7354         }
7355 
7356         /**
7357          * Remove a child view and recycle it using the given Recycler.
7358          *
7359          * @param index Index of child to remove and recycle
7360          * @param recycler Recycler to use to recycle child
7361          */
removeAndRecycleViewAt(int index, Recycler recycler)7362         public void removeAndRecycleViewAt(int index, Recycler recycler) {
7363             final View view = getChildAt(index);
7364             removeViewAt(index);
7365             recycler.recycleView(view);
7366         }
7367 
7368         /**
7369          * Return the current number of child views attached to the parent RecyclerView.
7370          * This does not include child views that were temporarily detached and/or scrapped.
7371          *
7372          * @return Number of attached children
7373          */
getChildCount()7374         public int getChildCount() {
7375             return mChildHelper != null ? mChildHelper.getChildCount() : 0;
7376         }
7377 
7378         /**
7379          * Return the child view at the given index
7380          * @param index Index of child to return
7381          * @return Child view at index
7382          */
getChildAt(int index)7383         public View getChildAt(int index) {
7384             return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
7385         }
7386 
7387         /**
7388          * Return the width measurement spec mode of the RecyclerView.
7389          * <p>
7390          * This value is set only if the LayoutManager opts into the auto measure api via
7391          * {@link #setAutoMeasureEnabled(boolean)}.
7392          * <p>
7393          * When RecyclerView is running a layout, this value is always set to
7394          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
7395          *
7396          * @return Width measure spec mode.
7397          *
7398          * @see View.MeasureSpec#getMode(int)
7399          * @see View#onMeasure(int, int)
7400          */
getWidthMode()7401         public int getWidthMode() {
7402             return mWidthMode;
7403         }
7404 
7405         /**
7406          * Return the height measurement spec mode of the RecyclerView.
7407          * <p>
7408          * This value is set only if the LayoutManager opts into the auto measure api via
7409          * {@link #setAutoMeasureEnabled(boolean)}.
7410          * <p>
7411          * When RecyclerView is running a layout, this value is always set to
7412          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
7413          *
7414          * @return Height measure spec mode.
7415          *
7416          * @see View.MeasureSpec#getMode(int)
7417          * @see View#onMeasure(int, int)
7418          */
getHeightMode()7419         public int getHeightMode() {
7420             return mHeightMode;
7421         }
7422 
7423         /**
7424          * Return the width of the parent RecyclerView
7425          *
7426          * @return Width in pixels
7427          */
getWidth()7428         public int getWidth() {
7429             return mWidth;
7430         }
7431 
7432         /**
7433          * Return the height of the parent RecyclerView
7434          *
7435          * @return Height in pixels
7436          */
getHeight()7437         public int getHeight() {
7438             return mHeight;
7439         }
7440 
7441         /**
7442          * Return the left padding of the parent RecyclerView
7443          *
7444          * @return Padding in pixels
7445          */
getPaddingLeft()7446         public int getPaddingLeft() {
7447             return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
7448         }
7449 
7450         /**
7451          * Return the top padding of the parent RecyclerView
7452          *
7453          * @return Padding in pixels
7454          */
getPaddingTop()7455         public int getPaddingTop() {
7456             return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
7457         }
7458 
7459         /**
7460          * Return the right padding of the parent RecyclerView
7461          *
7462          * @return Padding in pixels
7463          */
getPaddingRight()7464         public int getPaddingRight() {
7465             return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
7466         }
7467 
7468         /**
7469          * Return the bottom padding of the parent RecyclerView
7470          *
7471          * @return Padding in pixels
7472          */
getPaddingBottom()7473         public int getPaddingBottom() {
7474             return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
7475         }
7476 
7477         /**
7478          * Return the start padding of the parent RecyclerView
7479          *
7480          * @return Padding in pixels
7481          */
getPaddingStart()7482         public int getPaddingStart() {
7483             return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;
7484         }
7485 
7486         /**
7487          * Return the end padding of the parent RecyclerView
7488          *
7489          * @return Padding in pixels
7490          */
getPaddingEnd()7491         public int getPaddingEnd() {
7492             return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;
7493         }
7494 
7495         /**
7496          * Returns true if the RecyclerView this LayoutManager is bound to has focus.
7497          *
7498          * @return True if the RecyclerView has focus, false otherwise.
7499          * @see View#isFocused()
7500          */
isFocused()7501         public boolean isFocused() {
7502             return mRecyclerView != null && mRecyclerView.isFocused();
7503         }
7504 
7505         /**
7506          * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
7507          *
7508          * @return true if the RecyclerView has or contains focus
7509          * @see View#hasFocus()
7510          */
hasFocus()7511         public boolean hasFocus() {
7512             return mRecyclerView != null && mRecyclerView.hasFocus();
7513         }
7514 
7515         /**
7516          * Returns the item View which has or contains focus.
7517          *
7518          * @return A direct child of RecyclerView which has focus or contains the focused child.
7519          */
getFocusedChild()7520         public View getFocusedChild() {
7521             if (mRecyclerView == null) {
7522                 return null;
7523             }
7524             final View focused = mRecyclerView.getFocusedChild();
7525             if (focused == null || mChildHelper.isHidden(focused)) {
7526                 return null;
7527             }
7528             return focused;
7529         }
7530 
7531         /**
7532          * Returns the number of items in the adapter bound to the parent RecyclerView.
7533          * <p>
7534          * Note that this number is not necessarily equal to {@link State#getItemCount()}. In
7535          * methods where State is available, you should use {@link State#getItemCount()} instead.
7536          * For more details, check the documentation for {@link State#getItemCount()}.
7537          *
7538          * @return The number of items in the bound adapter
7539          * @see State#getItemCount()
7540          */
getItemCount()7541         public int getItemCount() {
7542             final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
7543             return a != null ? a.getItemCount() : 0;
7544         }
7545 
7546         /**
7547          * Offset all child views attached to the parent RecyclerView by dx pixels along
7548          * the horizontal axis.
7549          *
7550          * @param dx Pixels to offset by
7551          */
offsetChildrenHorizontal(int dx)7552         public void offsetChildrenHorizontal(int dx) {
7553             if (mRecyclerView != null) {
7554                 mRecyclerView.offsetChildrenHorizontal(dx);
7555             }
7556         }
7557 
7558         /**
7559          * Offset all child views attached to the parent RecyclerView by dy pixels along
7560          * the vertical axis.
7561          *
7562          * @param dy Pixels to offset by
7563          */
offsetChildrenVertical(int dy)7564         public void offsetChildrenVertical(int dy) {
7565             if (mRecyclerView != null) {
7566                 mRecyclerView.offsetChildrenVertical(dy);
7567             }
7568         }
7569 
7570         /**
7571          * Flags a view so that it will not be scrapped or recycled.
7572          * <p>
7573          * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
7574          * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
7575          * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
7576          * ignore the child.
7577          * <p>
7578          * Before this child can be recycled again, you have to call
7579          * {@link #stopIgnoringView(View)}.
7580          * <p>
7581          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
7582          *
7583          * @param view View to ignore.
7584          * @see #stopIgnoringView(View)
7585          */
ignoreView(View view)7586         public void ignoreView(View view) {
7587             if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
7588                 // checking this because calling this method on a recycled or detached view may
7589                 // cause loss of state.
7590                 throw new IllegalArgumentException("View should be fully attached to be ignored");
7591             }
7592             final ViewHolder vh = getChildViewHolderInt(view);
7593             vh.addFlags(ViewHolder.FLAG_IGNORE);
7594             mRecyclerView.mViewInfoStore.removeViewHolder(vh);
7595         }
7596 
7597         /**
7598          * View can be scrapped and recycled again.
7599          * <p>
7600          * Note that calling this method removes all information in the view holder.
7601          * <p>
7602          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
7603          *
7604          * @param view View to ignore.
7605          */
stopIgnoringView(View view)7606         public void stopIgnoringView(View view) {
7607             final ViewHolder vh = getChildViewHolderInt(view);
7608             vh.stopIgnoring();
7609             vh.resetInternal();
7610             vh.addFlags(ViewHolder.FLAG_INVALID);
7611         }
7612 
7613         /**
7614          * Temporarily detach and scrap all currently attached child views. Views will be scrapped
7615          * into the given Recycler. The Recycler may prefer to reuse scrap views before
7616          * other views that were previously recycled.
7617          *
7618          * @param recycler Recycler to scrap views into
7619          */
detachAndScrapAttachedViews(Recycler recycler)7620         public void detachAndScrapAttachedViews(Recycler recycler) {
7621             final int childCount = getChildCount();
7622             for (int i = childCount - 1; i >= 0; i--) {
7623                 final View v = getChildAt(i);
7624                 scrapOrRecycleView(recycler, i, v);
7625             }
7626         }
7627 
scrapOrRecycleView(Recycler recycler, int index, View view)7628         private void scrapOrRecycleView(Recycler recycler, int index, View view) {
7629             final ViewHolder viewHolder = getChildViewHolderInt(view);
7630             if (viewHolder.shouldIgnore()) {
7631                 if (DEBUG) {
7632                     Log.d(TAG, "ignoring view " + viewHolder);
7633                 }
7634                 return;
7635             }
7636             if (viewHolder.isInvalid() && !viewHolder.isRemoved() &&
7637                     !mRecyclerView.mAdapter.hasStableIds()) {
7638                 removeViewAt(index);
7639                 recycler.recycleViewHolderInternal(viewHolder);
7640             } else {
7641                 detachViewAt(index);
7642                 recycler.scrapView(view);
7643                 mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
7644             }
7645         }
7646 
7647         /**
7648          * Recycles the scrapped views.
7649          * <p>
7650          * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
7651          * the expected behavior if scrapped views are used for animations. Otherwise, we need to
7652          * call remove and invalidate RecyclerView to ensure UI update.
7653          *
7654          * @param recycler Recycler
7655          */
removeAndRecycleScrapInt(Recycler recycler)7656         void removeAndRecycleScrapInt(Recycler recycler) {
7657             final int scrapCount = recycler.getScrapCount();
7658             // Loop backward, recycler might be changed by removeDetachedView()
7659             for (int i = scrapCount - 1; i >= 0; i--) {
7660                 final View scrap = recycler.getScrapViewAt(i);
7661                 final ViewHolder vh = getChildViewHolderInt(scrap);
7662                 if (vh.shouldIgnore()) {
7663                     continue;
7664                 }
7665                 // If the scrap view is animating, we need to cancel them first. If we cancel it
7666                 // here, ItemAnimator callback may recycle it which will cause double recycling.
7667                 // To avoid this, we mark it as not recycleable before calling the item animator.
7668                 // Since removeDetachedView calls a user API, a common mistake (ending animations on
7669                 // the view) may recycle it too, so we guard it before we call user APIs.
7670                 vh.setIsRecyclable(false);
7671                 if (vh.isTmpDetached()) {
7672                     mRecyclerView.removeDetachedView(scrap, false);
7673                 }
7674                 if (mRecyclerView.mItemAnimator != null) {
7675                     mRecyclerView.mItemAnimator.endAnimation(vh);
7676                 }
7677                 vh.setIsRecyclable(true);
7678                 recycler.quickRecycleScrapView(scrap);
7679             }
7680             recycler.clearScrap();
7681             if (scrapCount > 0) {
7682                 mRecyclerView.invalidate();
7683             }
7684         }
7685 
7686 
7687         /**
7688          * Measure a child view using standard measurement policy, taking the padding
7689          * of the parent RecyclerView and any added item decorations into account.
7690          *
7691          * <p>If the RecyclerView can be scrolled in either dimension the caller may
7692          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
7693          *
7694          * @param child Child view to measure
7695          * @param widthUsed Width in pixels currently consumed by other views, if relevant
7696          * @param heightUsed Height in pixels currently consumed by other views, if relevant
7697          */
measureChild(View child, int widthUsed, int heightUsed)7698         public void measureChild(View child, int widthUsed, int heightUsed) {
7699             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
7700 
7701             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
7702             widthUsed += insets.left + insets.right;
7703             heightUsed += insets.top + insets.bottom;
7704             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
7705                     getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
7706                     canScrollHorizontally());
7707             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
7708                     getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
7709                     canScrollVertically());
7710             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
7711                 child.measure(widthSpec, heightSpec);
7712             }
7713         }
7714 
7715         /**
7716          * RecyclerView internally does its own View measurement caching which should help with
7717          * WRAP_CONTENT.
7718          * <p>
7719          * Use this method if the View is already measured once in this layout pass.
7720          */
shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp)7721         boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
7722             return !mMeasurementCacheEnabled
7723                     || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
7724                     || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
7725         }
7726 
7727         // we may consider making this public
7728         /**
7729          * RecyclerView internally does its own View measurement caching which should help with
7730          * WRAP_CONTENT.
7731          * <p>
7732          * Use this method if the View is not yet measured and you need to decide whether to
7733          * measure this View or not.
7734          */
shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp)7735         boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
7736             return child.isLayoutRequested()
7737                     || !mMeasurementCacheEnabled
7738                     || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
7739                     || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
7740         }
7741 
7742         /**
7743          * In addition to the View Framework's measurement cache, RecyclerView uses its own
7744          * additional measurement cache for its children to avoid re-measuring them when not
7745          * necessary. It is on by default but it can be turned off via
7746          * {@link #setMeasurementCacheEnabled(boolean)}.
7747          *
7748          * @return True if measurement cache is enabled, false otherwise.
7749          *
7750          * @see #setMeasurementCacheEnabled(boolean)
7751          */
isMeasurementCacheEnabled()7752         public boolean isMeasurementCacheEnabled() {
7753             return mMeasurementCacheEnabled;
7754         }
7755 
7756         /**
7757          * Sets whether RecyclerView should use its own measurement cache for the children. This is
7758          * a more aggressive cache than the framework uses.
7759          *
7760          * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
7761          *
7762          * @see #isMeasurementCacheEnabled()
7763          */
setMeasurementCacheEnabled(boolean measurementCacheEnabled)7764         public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
7765             mMeasurementCacheEnabled = measurementCacheEnabled;
7766         }
7767 
isMeasurementUpToDate(int childSize, int spec, int dimension)7768         private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
7769             final int specMode = MeasureSpec.getMode(spec);
7770             final int specSize = MeasureSpec.getSize(spec);
7771             if (dimension > 0 && childSize != dimension) {
7772                 return false;
7773             }
7774             switch (specMode) {
7775                 case MeasureSpec.UNSPECIFIED:
7776                     return true;
7777                 case MeasureSpec.AT_MOST:
7778                     return specSize >= childSize;
7779                 case MeasureSpec.EXACTLY:
7780                     return  specSize == childSize;
7781             }
7782             return false;
7783         }
7784 
7785         /**
7786          * Measure a child view using standard measurement policy, taking the padding
7787          * of the parent RecyclerView, any added item decorations and the child margins
7788          * into account.
7789          *
7790          * <p>If the RecyclerView can be scrolled in either dimension the caller may
7791          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
7792          *
7793          * @param child Child view to measure
7794          * @param widthUsed Width in pixels currently consumed by other views, if relevant
7795          * @param heightUsed Height in pixels currently consumed by other views, if relevant
7796          */
measureChildWithMargins(View child, int widthUsed, int heightUsed)7797         public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
7798             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
7799 
7800             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
7801             widthUsed += insets.left + insets.right;
7802             heightUsed += insets.top + insets.bottom;
7803 
7804             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
7805                     getPaddingLeft() + getPaddingRight() +
7806                             lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
7807                     canScrollHorizontally());
7808             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
7809                     getPaddingTop() + getPaddingBottom() +
7810                             lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
7811                     canScrollVertically());
7812             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
7813                 child.measure(widthSpec, heightSpec);
7814             }
7815         }
7816 
7817         /**
7818          * Calculate a MeasureSpec value for measuring a child view in one dimension.
7819          *
7820          * @param parentSize Size of the parent view where the child will be placed
7821          * @param padding Total space currently consumed by other elements of the parent
7822          * @param childDimension Desired size of the child view, or FILL_PARENT/WRAP_CONTENT.
7823          *                       Generally obtained from the child view's LayoutParams
7824          * @param canScroll true if the parent RecyclerView can scroll in this dimension
7825          *
7826          * @return a MeasureSpec value for the child view
7827          * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
7828          */
7829         @Deprecated
getChildMeasureSpec(int parentSize, int padding, int childDimension, boolean canScroll)7830         public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
7831                 boolean canScroll) {
7832             int size = Math.max(0, parentSize - padding);
7833             int resultSize = 0;
7834             int resultMode = 0;
7835             if (canScroll) {
7836                 if (childDimension >= 0) {
7837                     resultSize = childDimension;
7838                     resultMode = MeasureSpec.EXACTLY;
7839                 } else {
7840                     // FILL_PARENT can't be applied since we can scroll in this dimension, wrap
7841                     // instead using UNSPECIFIED.
7842                     resultSize = 0;
7843                     resultMode = MeasureSpec.UNSPECIFIED;
7844                 }
7845             } else {
7846                 if (childDimension >= 0) {
7847                     resultSize = childDimension;
7848                     resultMode = MeasureSpec.EXACTLY;
7849                 } else if (childDimension == LayoutParams.FILL_PARENT) {
7850                     resultSize = size;
7851                     // TODO this should be my spec.
7852                     resultMode = MeasureSpec.EXACTLY;
7853                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
7854                     resultSize = size;
7855                     resultMode = MeasureSpec.AT_MOST;
7856                 }
7857             }
7858             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
7859         }
7860 
7861         /**
7862          * Calculate a MeasureSpec value for measuring a child view in one dimension.
7863          *
7864          * @param parentSize Size of the parent view where the child will be placed
7865          * @param parentMode The measurement spec mode of the parent
7866          * @param padding Total space currently consumed by other elements of parent
7867          * @param childDimension Desired size of the child view, or FILL_PARENT/WRAP_CONTENT.
7868          *                       Generally obtained from the child view's LayoutParams
7869          * @param canScroll true if the parent RecyclerView can scroll in this dimension
7870          *
7871          * @return a MeasureSpec value for the child view
7872          */
getChildMeasureSpec(int parentSize, int parentMode, int padding, int childDimension, boolean canScroll)7873         public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
7874                 int childDimension, boolean canScroll) {
7875             int size = Math.max(0, parentSize - padding);
7876             int resultSize = 0;
7877             int resultMode = 0;
7878             if (canScroll) {
7879                 if (childDimension >= 0) {
7880                     resultSize = childDimension;
7881                     resultMode = MeasureSpec.EXACTLY;
7882                 } else if (childDimension == LayoutParams.FILL_PARENT){
7883                     switch (parentMode) {
7884                         case MeasureSpec.AT_MOST:
7885                         case MeasureSpec.EXACTLY:
7886                             resultSize = size;
7887                             resultMode = parentMode;
7888                             break;
7889                         case MeasureSpec.UNSPECIFIED:
7890                             resultSize = 0;
7891                             resultMode = MeasureSpec.UNSPECIFIED;
7892                             break;
7893                     }
7894                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
7895                     resultSize = 0;
7896                     resultMode = MeasureSpec.UNSPECIFIED;
7897                 }
7898             } else {
7899                 if (childDimension >= 0) {
7900                     resultSize = childDimension;
7901                     resultMode = MeasureSpec.EXACTLY;
7902                 } else if (childDimension == LayoutParams.FILL_PARENT) {
7903                     resultSize = size;
7904                     resultMode = parentMode;
7905                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
7906                     resultSize = size;
7907                     if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
7908                         resultMode = MeasureSpec.AT_MOST;
7909                     } else {
7910                         resultMode = MeasureSpec.UNSPECIFIED;
7911                     }
7912 
7913                 }
7914             }
7915             //noinspection WrongConstant
7916             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
7917         }
7918 
7919         /**
7920          * Returns the measured width of the given child, plus the additional size of
7921          * any insets applied by {@link ItemDecoration ItemDecorations}.
7922          *
7923          * @param child Child view to query
7924          * @return child's measured width plus <code>ItemDecoration</code> insets
7925          *
7926          * @see View#getMeasuredWidth()
7927          */
getDecoratedMeasuredWidth(View child)7928         public int getDecoratedMeasuredWidth(View child) {
7929             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
7930             return child.getMeasuredWidth() + insets.left + insets.right;
7931         }
7932 
7933         /**
7934          * Returns the measured height of the given child, plus the additional size of
7935          * any insets applied by {@link ItemDecoration ItemDecorations}.
7936          *
7937          * @param child Child view to query
7938          * @return child's measured height plus <code>ItemDecoration</code> insets
7939          *
7940          * @see View#getMeasuredHeight()
7941          */
getDecoratedMeasuredHeight(View child)7942         public int getDecoratedMeasuredHeight(View child) {
7943             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
7944             return child.getMeasuredHeight() + insets.top + insets.bottom;
7945         }
7946 
7947         /**
7948          * Lay out the given child view within the RecyclerView using coordinates that
7949          * include any current {@link ItemDecoration ItemDecorations}.
7950          *
7951          * <p>LayoutManagers should prefer working in sizes and coordinates that include
7952          * item decoration insets whenever possible. This allows the LayoutManager to effectively
7953          * ignore decoration insets within measurement and layout code. See the following
7954          * methods:</p>
7955          * <ul>
7956          *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
7957          *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
7958          *     <li>{@link #measureChild(View, int, int)}</li>
7959          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
7960          *     <li>{@link #getDecoratedLeft(View)}</li>
7961          *     <li>{@link #getDecoratedTop(View)}</li>
7962          *     <li>{@link #getDecoratedRight(View)}</li>
7963          *     <li>{@link #getDecoratedBottom(View)}</li>
7964          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
7965          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
7966          * </ul>
7967          *
7968          * @param child Child to lay out
7969          * @param left Left edge, with item decoration insets included
7970          * @param top Top edge, with item decoration insets included
7971          * @param right Right edge, with item decoration insets included
7972          * @param bottom Bottom edge, with item decoration insets included
7973          *
7974          * @see View#layout(int, int, int, int)
7975          * @see #layoutDecoratedWithMargins(View, int, int, int, int)
7976          */
layoutDecorated(View child, int left, int top, int right, int bottom)7977         public void layoutDecorated(View child, int left, int top, int right, int bottom) {
7978             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
7979             child.layout(left + insets.left, top + insets.top, right - insets.right,
7980                     bottom - insets.bottom);
7981         }
7982 
7983         /**
7984          * Lay out the given child view within the RecyclerView using coordinates that
7985          * include any current {@link ItemDecoration ItemDecorations} and margins.
7986          *
7987          * <p>LayoutManagers should prefer working in sizes and coordinates that include
7988          * item decoration insets whenever possible. This allows the LayoutManager to effectively
7989          * ignore decoration insets within measurement and layout code. See the following
7990          * methods:</p>
7991          * <ul>
7992          *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
7993          *     <li>{@link #measureChild(View, int, int)}</li>
7994          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
7995          *     <li>{@link #getDecoratedLeft(View)}</li>
7996          *     <li>{@link #getDecoratedTop(View)}</li>
7997          *     <li>{@link #getDecoratedRight(View)}</li>
7998          *     <li>{@link #getDecoratedBottom(View)}</li>
7999          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
8000          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
8001          * </ul>
8002          *
8003          * @param child Child to lay out
8004          * @param left Left edge, with item decoration insets and left margin included
8005          * @param top Top edge, with item decoration insets and top margin included
8006          * @param right Right edge, with item decoration insets and right margin included
8007          * @param bottom Bottom edge, with item decoration insets and bottom margin included
8008          *
8009          * @see View#layout(int, int, int, int)
8010          * @see #layoutDecorated(View, int, int, int, int)
8011          */
layoutDecoratedWithMargins(View child, int left, int top, int right, int bottom)8012         public void layoutDecoratedWithMargins(View child, int left, int top, int right,
8013                 int bottom) {
8014             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8015             final Rect insets = lp.mDecorInsets;
8016             child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
8017                     right - insets.right - lp.rightMargin,
8018                     bottom - insets.bottom - lp.bottomMargin);
8019         }
8020 
8021         /**
8022          * Calculates the bounding box of the View while taking into account its matrix changes
8023          * (translation, scale etc) with respect to the RecyclerView.
8024          * <p>
8025          * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
8026          * the View's matrix so that the decor offsets also go through the same transformation.
8027          *
8028          * @param child The ItemView whose bounding box should be calculated.
8029          * @param includeDecorInsets True if the decor insets should be included in the bounding box
8030          * @param out The rectangle into which the output will be written.
8031          */
getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out)8032         public void getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out) {
8033             if (includeDecorInsets) {
8034                 Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8035                 out.set(-insets.left, -insets.top,
8036                         child.getWidth() + insets.right, child.getHeight() + insets.bottom);
8037             } else {
8038                 out.set(0, 0, child.getWidth(), child.getHeight());
8039             }
8040 
8041             if (mRecyclerView != null) {
8042                 final Matrix childMatrix = ViewCompat.getMatrix(child);
8043                 if (childMatrix != null && !childMatrix.isIdentity()) {
8044                     final RectF tempRectF = mRecyclerView.mTempRectF;
8045                     tempRectF.set(out);
8046                     childMatrix.mapRect(tempRectF);
8047                     out.set(
8048                             (int) Math.floor(tempRectF.left),
8049                             (int) Math.floor(tempRectF.top),
8050                             (int) Math.ceil(tempRectF.right),
8051                             (int) Math.ceil(tempRectF.bottom)
8052                     );
8053                 }
8054             }
8055             out.offset(child.getLeft(), child.getTop());
8056         }
8057 
8058         /**
8059          * Returns the bounds of the view including its decoration and margins.
8060          *
8061          * @param view The view element to check
8062          * @param outBounds A rect that will receive the bounds of the element including its
8063          *                  decoration and margins.
8064          */
getDecoratedBoundsWithMargins(View view, Rect outBounds)8065         public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
8066             final LayoutParams lp = (LayoutParams) view.getLayoutParams();
8067             final Rect insets = lp.mDecorInsets;
8068             outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
8069                     view.getTop() - insets.top - lp.topMargin,
8070                     view.getRight() + insets.right + lp.rightMargin,
8071                     view.getBottom() + insets.bottom + lp.bottomMargin);
8072         }
8073 
8074         /**
8075          * Returns the left edge of the given child view within its parent, offset by any applied
8076          * {@link ItemDecoration ItemDecorations}.
8077          *
8078          * @param child Child to query
8079          * @return Child left edge with offsets applied
8080          * @see #getLeftDecorationWidth(View)
8081          */
getDecoratedLeft(View child)8082         public int getDecoratedLeft(View child) {
8083             return child.getLeft() - getLeftDecorationWidth(child);
8084         }
8085 
8086         /**
8087          * Returns the top edge of the given child view within its parent, offset by any applied
8088          * {@link ItemDecoration ItemDecorations}.
8089          *
8090          * @param child Child to query
8091          * @return Child top edge with offsets applied
8092          * @see #getTopDecorationHeight(View)
8093          */
getDecoratedTop(View child)8094         public int getDecoratedTop(View child) {
8095             return child.getTop() - getTopDecorationHeight(child);
8096         }
8097 
8098         /**
8099          * Returns the right edge of the given child view within its parent, offset by any applied
8100          * {@link ItemDecoration ItemDecorations}.
8101          *
8102          * @param child Child to query
8103          * @return Child right edge with offsets applied
8104          * @see #getRightDecorationWidth(View)
8105          */
getDecoratedRight(View child)8106         public int getDecoratedRight(View child) {
8107             return child.getRight() + getRightDecorationWidth(child);
8108         }
8109 
8110         /**
8111          * Returns the bottom edge of the given child view within its parent, offset by any applied
8112          * {@link ItemDecoration ItemDecorations}.
8113          *
8114          * @param child Child to query
8115          * @return Child bottom edge with offsets applied
8116          * @see #getBottomDecorationHeight(View)
8117          */
getDecoratedBottom(View child)8118         public int getDecoratedBottom(View child) {
8119             return child.getBottom() + getBottomDecorationHeight(child);
8120         }
8121 
8122         /**
8123          * Calculates the item decor insets applied to the given child and updates the provided
8124          * Rect instance with the inset values.
8125          * <ul>
8126          *     <li>The Rect's left is set to the total width of left decorations.</li>
8127          *     <li>The Rect's top is set to the total height of top decorations.</li>
8128          *     <li>The Rect's right is set to the total width of right decorations.</li>
8129          *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
8130          * </ul>
8131          * <p>
8132          * Note that item decorations are automatically calculated when one of the LayoutManager's
8133          * measure child methods is called. If you need to measure the child with custom specs via
8134          * {@link View#measure(int, int)}, you can use this method to get decorations.
8135          *
8136          * @param child The child view whose decorations should be calculated
8137          * @param outRect The Rect to hold result values
8138          */
calculateItemDecorationsForChild(View child, Rect outRect)8139         public void calculateItemDecorationsForChild(View child, Rect outRect) {
8140             if (mRecyclerView == null) {
8141                 outRect.set(0, 0, 0, 0);
8142                 return;
8143             }
8144             Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8145             outRect.set(insets);
8146         }
8147 
8148         /**
8149          * Returns the total height of item decorations applied to child's top.
8150          * <p>
8151          * Note that this value is not updated until the View is measured or
8152          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8153          *
8154          * @param child Child to query
8155          * @return The total height of item decorations applied to the child's top.
8156          * @see #getDecoratedTop(View)
8157          * @see #calculateItemDecorationsForChild(View, Rect)
8158          */
getTopDecorationHeight(View child)8159         public int getTopDecorationHeight(View child) {
8160             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
8161         }
8162 
8163         /**
8164          * Returns the total height of item decorations applied to child's bottom.
8165          * <p>
8166          * Note that this value is not updated until the View is measured or
8167          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8168          *
8169          * @param child Child to query
8170          * @return The total height of item decorations applied to the child's bottom.
8171          * @see #getDecoratedBottom(View)
8172          * @see #calculateItemDecorationsForChild(View, Rect)
8173          */
getBottomDecorationHeight(View child)8174         public int getBottomDecorationHeight(View child) {
8175             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
8176         }
8177 
8178         /**
8179          * Returns the total width of item decorations applied to child's left.
8180          * <p>
8181          * Note that this value is not updated until the View is measured or
8182          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8183          *
8184          * @param child Child to query
8185          * @return The total width of item decorations applied to the child's left.
8186          * @see #getDecoratedLeft(View)
8187          * @see #calculateItemDecorationsForChild(View, Rect)
8188          */
getLeftDecorationWidth(View child)8189         public int getLeftDecorationWidth(View child) {
8190             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
8191         }
8192 
8193         /**
8194          * Returns the total width of item decorations applied to child's right.
8195          * <p>
8196          * Note that this value is not updated until the View is measured or
8197          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8198          *
8199          * @param child Child to query
8200          * @return The total width of item decorations applied to the child's right.
8201          * @see #getDecoratedRight(View)
8202          * @see #calculateItemDecorationsForChild(View, Rect)
8203          */
getRightDecorationWidth(View child)8204         public int getRightDecorationWidth(View child) {
8205             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
8206         }
8207 
8208         /**
8209          * Called when searching for a focusable view in the given direction has failed
8210          * for the current content of the RecyclerView.
8211          *
8212          * <p>This is the LayoutManager's opportunity to populate views in the given direction
8213          * to fulfill the request if it can. The LayoutManager should attach and return
8214          * the view to be focused. The default implementation returns null.</p>
8215          *
8216          * @param focused   The currently focused view
8217          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
8218          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
8219          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
8220          *                  or 0 for not applicable
8221          * @param recycler  The recycler to use for obtaining views for currently offscreen items
8222          * @param state     Transient state of RecyclerView
8223          * @return The chosen view to be focused
8224          */
8225         @Nullable
onFocusSearchFailed(View focused, int direction, Recycler recycler, State state)8226         public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
8227                 State state) {
8228             return null;
8229         }
8230 
8231         /**
8232          * This method gives a LayoutManager an opportunity to intercept the initial focus search
8233          * before the default behavior of {@link FocusFinder} is used. If this method returns
8234          * null FocusFinder will attempt to find a focusable child view. If it fails
8235          * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
8236          * will be called to give the LayoutManager an opportunity to add new views for items
8237          * that did not have attached views representing them. The LayoutManager should not add
8238          * or remove views from this method.
8239          *
8240          * @param focused The currently focused view
8241          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
8242          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
8243          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
8244          * @return A descendant view to focus or null to fall back to default behavior.
8245          *         The default implementation returns null.
8246          */
onInterceptFocusSearch(View focused, int direction)8247         public View onInterceptFocusSearch(View focused, int direction) {
8248             return null;
8249         }
8250 
8251         /**
8252          * Called when a child of the RecyclerView wants a particular rectangle to be positioned
8253          * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
8254          * android.graphics.Rect, boolean)} for more details.
8255          *
8256          * <p>The base implementation will attempt to perform a standard programmatic scroll
8257          * to bring the given rect into view, within the padded area of the RecyclerView.</p>
8258          *
8259          * @param child The direct child making the request.
8260          * @param rect  The rectangle in the child's coordinates the child
8261          *              wishes to be on the screen.
8262          * @param immediate True to forbid animated or delayed scrolling,
8263          *                  false otherwise
8264          * @return Whether the group scrolled to handle the operation
8265          */
requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect, boolean immediate)8266         public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
8267                 boolean immediate) {
8268             final int parentLeft = getPaddingLeft();
8269             final int parentTop = getPaddingTop();
8270             final int parentRight = getWidth() - getPaddingRight();
8271             final int parentBottom = getHeight() - getPaddingBottom();
8272             final int childLeft = child.getLeft() + rect.left - child.getScrollX();
8273             final int childTop = child.getTop() + rect.top - child.getScrollY();
8274             final int childRight = childLeft + rect.width();
8275             final int childBottom = childTop + rect.height();
8276 
8277             final int offScreenLeft = Math.min(0, childLeft - parentLeft);
8278             final int offScreenTop = Math.min(0, childTop - parentTop);
8279             final int offScreenRight = Math.max(0, childRight - parentRight);
8280             final int offScreenBottom = Math.max(0, childBottom - parentBottom);
8281 
8282             // Favor the "start" layout direction over the end when bringing one side or the other
8283             // of a large rect into view. If we decide to bring in end because start is already
8284             // visible, limit the scroll such that start won't go out of bounds.
8285             final int dx;
8286             if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) {
8287                 dx = offScreenRight != 0 ? offScreenRight
8288                         : Math.max(offScreenLeft, childRight - parentRight);
8289             } else {
8290                 dx = offScreenLeft != 0 ? offScreenLeft
8291                         : Math.min(childLeft - parentLeft, offScreenRight);
8292             }
8293 
8294             // Favor bringing the top into view over the bottom. If top is already visible and
8295             // we should scroll to make bottom visible, make sure top does not go out of bounds.
8296             final int dy = offScreenTop != 0 ? offScreenTop
8297                     : Math.min(childTop - parentTop, offScreenBottom);
8298 
8299             if (dx != 0 || dy != 0) {
8300                 if (immediate) {
8301                     parent.scrollBy(dx, dy);
8302                 } else {
8303                     parent.smoothScrollBy(dx, dy);
8304                 }
8305                 return true;
8306             }
8307             return false;
8308         }
8309 
8310         /**
8311          * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
8312          */
8313         @Deprecated
onRequestChildFocus(RecyclerView parent, View child, View focused)8314         public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
8315             // eat the request if we are in the middle of a scroll or layout
8316             return isSmoothScrolling() || parent.isComputingLayout();
8317         }
8318 
8319         /**
8320          * Called when a descendant view of the RecyclerView requests focus.
8321          *
8322          * <p>A LayoutManager wishing to keep focused views aligned in a specific
8323          * portion of the view may implement that behavior in an override of this method.</p>
8324          *
8325          * <p>If the LayoutManager executes different behavior that should override the default
8326          * behavior of scrolling the focused child on screen instead of running alongside it,
8327          * this method should return true.</p>
8328          *
8329          * @param parent  The RecyclerView hosting this LayoutManager
8330          * @param state   Current state of RecyclerView
8331          * @param child   Direct child of the RecyclerView containing the newly focused view
8332          * @param focused The newly focused view. This may be the same view as child or it may be
8333          *                null
8334          * @return true if the default scroll behavior should be suppressed
8335          */
onRequestChildFocus(RecyclerView parent, State state, View child, View focused)8336         public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
8337                 View focused) {
8338             return onRequestChildFocus(parent, child, focused);
8339         }
8340 
8341         /**
8342          * Called if the RecyclerView this LayoutManager is bound to has a different adapter set.
8343          * The LayoutManager may use this opportunity to clear caches and configure state such
8344          * that it can relayout appropriately with the new data and potentially new view types.
8345          *
8346          * <p>The default implementation removes all currently attached views.</p>
8347          *
8348          * @param oldAdapter The previous adapter instance. Will be null if there was previously no
8349          *                   adapter.
8350          * @param newAdapter The new adapter instance. Might be null if
8351          *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
8352          */
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter)8353         public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
8354         }
8355 
8356         /**
8357          * Called to populate focusable views within the RecyclerView.
8358          *
8359          * <p>The LayoutManager implementation should return <code>true</code> if the default
8360          * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
8361          * suppressed.</p>
8362          *
8363          * <p>The default implementation returns <code>false</code> to trigger RecyclerView
8364          * to fall back to the default ViewGroup behavior.</p>
8365          *
8366          * @param recyclerView The RecyclerView hosting this LayoutManager
8367          * @param views List of output views. This method should add valid focusable views
8368          *              to this list.
8369          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
8370          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
8371          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
8372          * @param focusableMode The type of focusables to be added.
8373          *
8374          * @return true to suppress the default behavior, false to add default focusables after
8375          *         this method returns.
8376          *
8377          * @see #FOCUSABLES_ALL
8378          * @see #FOCUSABLES_TOUCH_MODE
8379          */
onAddFocusables(RecyclerView recyclerView, ArrayList<View> views, int direction, int focusableMode)8380         public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,
8381                 int direction, int focusableMode) {
8382             return false;
8383         }
8384 
8385         /**
8386          * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving
8387          * detailed information on what has actually changed.
8388          *
8389          * @param recyclerView
8390          */
onItemsChanged(RecyclerView recyclerView)8391         public void onItemsChanged(RecyclerView recyclerView) {
8392         }
8393 
8394         /**
8395          * Called when items have been added to the adapter. The LayoutManager may choose to
8396          * requestLayout if the inserted items would require refreshing the currently visible set
8397          * of child views. (e.g. currently empty space would be filled by appended items, etc.)
8398          *
8399          * @param recyclerView
8400          * @param positionStart
8401          * @param itemCount
8402          */
onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount)8403         public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
8404         }
8405 
8406         /**
8407          * Called when items have been removed from the adapter.
8408          *
8409          * @param recyclerView
8410          * @param positionStart
8411          * @param itemCount
8412          */
onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount)8413         public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
8414         }
8415 
8416         /**
8417          * Called when items have been changed in the adapter.
8418          * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
8419          * instead, then this callback will not be invoked.
8420          *
8421          * @param recyclerView
8422          * @param positionStart
8423          * @param itemCount
8424          */
onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount)8425         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
8426         }
8427 
8428         /**
8429          * Called when items have been changed in the adapter and with optional payload.
8430          * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
8431          *
8432          * @param recyclerView
8433          * @param positionStart
8434          * @param itemCount
8435          * @param payload
8436          */
onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount, Object payload)8437         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
8438                 Object payload) {
8439             onItemsUpdated(recyclerView, positionStart, itemCount);
8440         }
8441 
8442         /**
8443          * Called when an item is moved withing the adapter.
8444          * <p>
8445          * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
8446          * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
8447          * is called.
8448          *
8449          * @param recyclerView
8450          * @param from
8451          * @param to
8452          * @param itemCount
8453          */
onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount)8454         public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
8455 
8456         }
8457 
8458 
8459         /**
8460          * <p>Override this method if you want to support scroll bars.</p>
8461          *
8462          * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
8463          *
8464          * <p>Default implementation returns 0.</p>
8465          *
8466          * @param state Current state of RecyclerView
8467          * @return The horizontal extent of the scrollbar's thumb
8468          * @see RecyclerView#computeHorizontalScrollExtent()
8469          */
computeHorizontalScrollExtent(State state)8470         public int computeHorizontalScrollExtent(State state) {
8471             return 0;
8472         }
8473 
8474         /**
8475          * <p>Override this method if you want to support scroll bars.</p>
8476          *
8477          * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
8478          *
8479          * <p>Default implementation returns 0.</p>
8480          *
8481          * @param state Current State of RecyclerView where you can find total item count
8482          * @return The horizontal offset of the scrollbar's thumb
8483          * @see RecyclerView#computeHorizontalScrollOffset()
8484          */
computeHorizontalScrollOffset(State state)8485         public int computeHorizontalScrollOffset(State state) {
8486             return 0;
8487         }
8488 
8489         /**
8490          * <p>Override this method if you want to support scroll bars.</p>
8491          *
8492          * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
8493          *
8494          * <p>Default implementation returns 0.</p>
8495          *
8496          * @param state Current State of RecyclerView where you can find total item count
8497          * @return The total horizontal range represented by the vertical scrollbar
8498          * @see RecyclerView#computeHorizontalScrollRange()
8499          */
computeHorizontalScrollRange(State state)8500         public int computeHorizontalScrollRange(State state) {
8501             return 0;
8502         }
8503 
8504         /**
8505          * <p>Override this method if you want to support scroll bars.</p>
8506          *
8507          * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
8508          *
8509          * <p>Default implementation returns 0.</p>
8510          *
8511          * @param state Current state of RecyclerView
8512          * @return The vertical extent of the scrollbar's thumb
8513          * @see RecyclerView#computeVerticalScrollExtent()
8514          */
computeVerticalScrollExtent(State state)8515         public int computeVerticalScrollExtent(State state) {
8516             return 0;
8517         }
8518 
8519         /**
8520          * <p>Override this method if you want to support scroll bars.</p>
8521          *
8522          * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
8523          *
8524          * <p>Default implementation returns 0.</p>
8525          *
8526          * @param state Current State of RecyclerView where you can find total item count
8527          * @return The vertical offset of the scrollbar's thumb
8528          * @see RecyclerView#computeVerticalScrollOffset()
8529          */
computeVerticalScrollOffset(State state)8530         public int computeVerticalScrollOffset(State state) {
8531             return 0;
8532         }
8533 
8534         /**
8535          * <p>Override this method if you want to support scroll bars.</p>
8536          *
8537          * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
8538          *
8539          * <p>Default implementation returns 0.</p>
8540          *
8541          * @param state Current State of RecyclerView where you can find total item count
8542          * @return The total vertical range represented by the vertical scrollbar
8543          * @see RecyclerView#computeVerticalScrollRange()
8544          */
computeVerticalScrollRange(State state)8545         public int computeVerticalScrollRange(State state) {
8546             return 0;
8547         }
8548 
8549         /**
8550          * Measure the attached RecyclerView. Implementations must call
8551          * {@link #setMeasuredDimension(int, int)} before returning.
8552          *
8553          * <p>The default implementation will handle EXACTLY measurements and respect
8554          * the minimum width and height properties of the host RecyclerView if measured
8555          * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
8556          * will consume all available space.</p>
8557          *
8558          * @param recycler Recycler
8559          * @param state Transient state of RecyclerView
8560          * @param widthSpec Width {@link android.view.View.MeasureSpec}
8561          * @param heightSpec Height {@link android.view.View.MeasureSpec}
8562          */
onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec)8563         public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
8564             mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
8565         }
8566 
8567         /**
8568          * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
8569          * host RecyclerView.
8570          *
8571          * @param widthSize Measured width
8572          * @param heightSize Measured height
8573          */
setMeasuredDimension(int widthSize, int heightSize)8574         public void setMeasuredDimension(int widthSize, int heightSize) {
8575             mRecyclerView.setMeasuredDimension(widthSize, heightSize);
8576         }
8577 
8578         /**
8579          * @return The host RecyclerView's {@link View#getMinimumWidth()}
8580          */
getMinimumWidth()8581         public int getMinimumWidth() {
8582             return ViewCompat.getMinimumWidth(mRecyclerView);
8583         }
8584 
8585         /**
8586          * @return The host RecyclerView's {@link View#getMinimumHeight()}
8587          */
getMinimumHeight()8588         public int getMinimumHeight() {
8589             return ViewCompat.getMinimumHeight(mRecyclerView);
8590         }
8591         /**
8592          * <p>Called when the LayoutManager should save its state. This is a good time to save your
8593          * scroll position, configuration and anything else that may be required to restore the same
8594          * layout state if the LayoutManager is recreated.</p>
8595          * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
8596          * restore. This will let you share information between your LayoutManagers but it is also
8597          * your responsibility to make sure they use the same parcelable class.</p>
8598          *
8599          * @return Necessary information for LayoutManager to be able to restore its state
8600          */
onSaveInstanceState()8601         public Parcelable onSaveInstanceState() {
8602             return null;
8603         }
8604 
8605 
onRestoreInstanceState(Parcelable state)8606         public void onRestoreInstanceState(Parcelable state) {
8607 
8608         }
8609 
stopSmoothScroller()8610         void stopSmoothScroller() {
8611             if (mSmoothScroller != null) {
8612                 mSmoothScroller.stop();
8613             }
8614         }
8615 
onSmoothScrollerStopped(SmoothScroller smoothScroller)8616         private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
8617             if (mSmoothScroller == smoothScroller) {
8618                 mSmoothScroller = null;
8619             }
8620         }
8621 
8622         /**
8623          * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
8624          *
8625          * @param state The new scroll state for RecyclerView
8626          */
onScrollStateChanged(int state)8627         public void onScrollStateChanged(int state) {
8628         }
8629 
8630         /**
8631          * Removes all views and recycles them using the given recycler.
8632          * <p>
8633          * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
8634          * <p>
8635          * If a View is marked as "ignored", it is not removed nor recycled.
8636          *
8637          * @param recycler Recycler to use to recycle children
8638          * @see #removeAndRecycleView(View, Recycler)
8639          * @see #removeAndRecycleViewAt(int, Recycler)
8640          * @see #ignoreView(View)
8641          */
removeAndRecycleAllViews(Recycler recycler)8642         public void removeAndRecycleAllViews(Recycler recycler) {
8643             for (int i = getChildCount() - 1; i >= 0; i--) {
8644                 final View view = getChildAt(i);
8645                 if (!getChildViewHolderInt(view).shouldIgnore()) {
8646                     removeAndRecycleViewAt(i, recycler);
8647                 }
8648             }
8649         }
8650 
8651         // called by accessibility delegate
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info)8652         void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
8653             onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
8654         }
8655 
8656         /**
8657          * Called by the AccessibilityDelegate when the information about the current layout should
8658          * be populated.
8659          * <p>
8660          * Default implementation adds a {@link
8661          * android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}.
8662          * <p>
8663          * You should override
8664          * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
8665          * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
8666          * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
8667          * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
8668          * more accurate accessibility information.
8669          *
8670          * @param recycler The Recycler that can be used to convert view positions into adapter
8671          *                 positions
8672          * @param state    The current state of RecyclerView
8673          * @param info     The info that should be filled by the LayoutManager
8674          * @see View#onInitializeAccessibilityNodeInfo(
8675          *android.view.accessibility.AccessibilityNodeInfo)
8676          * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
8677          * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
8678          * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
8679          * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
8680          */
onInitializeAccessibilityNodeInfo(Recycler recycler, State state, AccessibilityNodeInfoCompat info)8681         public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
8682                 AccessibilityNodeInfoCompat info) {
8683             if (ViewCompat.canScrollVertically(mRecyclerView, -1) ||
8684                     ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
8685                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
8686                 info.setScrollable(true);
8687             }
8688             if (ViewCompat.canScrollVertically(mRecyclerView, 1) ||
8689                     ViewCompat.canScrollHorizontally(mRecyclerView, 1)) {
8690                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
8691                 info.setScrollable(true);
8692             }
8693             final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo
8694                     = AccessibilityNodeInfoCompat.CollectionInfoCompat
8695                     .obtain(getRowCountForAccessibility(recycler, state),
8696                             getColumnCountForAccessibility(recycler, state),
8697                             isLayoutHierarchical(recycler, state),
8698                             getSelectionModeForAccessibility(recycler, state));
8699             info.setCollectionInfo(collectionInfo);
8700         }
8701 
8702         // called by accessibility delegate
onInitializeAccessibilityEvent(AccessibilityEvent event)8703         public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
8704             onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
8705         }
8706 
8707         /**
8708          * Called by the accessibility delegate to initialize an accessibility event.
8709          * <p>
8710          * Default implementation adds item count and scroll information to the event.
8711          *
8712          * @param recycler The Recycler that can be used to convert view positions into adapter
8713          *                 positions
8714          * @param state    The current state of RecyclerView
8715          * @param event    The event instance to initialize
8716          * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
8717          */
onInitializeAccessibilityEvent(Recycler recycler, State state, AccessibilityEvent event)8718         public void onInitializeAccessibilityEvent(Recycler recycler, State state,
8719                 AccessibilityEvent event) {
8720             final AccessibilityRecordCompat record = AccessibilityEventCompat
8721                     .asRecord(event);
8722             if (mRecyclerView == null || record == null) {
8723                 return;
8724             }
8725             record.setScrollable(ViewCompat.canScrollVertically(mRecyclerView, 1)
8726                     || ViewCompat.canScrollVertically(mRecyclerView, -1)
8727                     || ViewCompat.canScrollHorizontally(mRecyclerView, -1)
8728                     || ViewCompat.canScrollHorizontally(mRecyclerView, 1));
8729 
8730             if (mRecyclerView.mAdapter != null) {
8731                 record.setItemCount(mRecyclerView.mAdapter.getItemCount());
8732             }
8733         }
8734 
8735         // called by accessibility delegate
onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info)8736         void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) {
8737             final ViewHolder vh = getChildViewHolderInt(host);
8738             // avoid trying to create accessibility node info for removed children
8739             if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
8740                 onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
8741                         mRecyclerView.mState, host, info);
8742             }
8743         }
8744 
8745         /**
8746          * Called by the AccessibilityDelegate when the accessibility information for a specific
8747          * item should be populated.
8748          * <p>
8749          * Default implementation adds basic positioning information about the item.
8750          *
8751          * @param recycler The Recycler that can be used to convert view positions into adapter
8752          *                 positions
8753          * @param state    The current state of RecyclerView
8754          * @param host     The child for which accessibility node info should be populated
8755          * @param info     The info to fill out about the item
8756          * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
8757          * android.view.accessibility.AccessibilityNodeInfo)
8758          */
onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state, View host, AccessibilityNodeInfoCompat info)8759         public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
8760                 View host, AccessibilityNodeInfoCompat info) {
8761             int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
8762             int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
8763             final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo
8764                     = AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
8765                     columnIndexGuess, 1, false, false);
8766             info.setCollectionItemInfo(itemInfo);
8767         }
8768 
8769         /**
8770          * A LayoutManager can call this method to force RecyclerView to run simple animations in
8771          * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
8772          * change).
8773          * <p>
8774          * Note that, calling this method will not guarantee that RecyclerView will run animations
8775          * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
8776          * not run any animations but will still clear this flag after the layout is complete.
8777          *
8778          */
requestSimpleAnimationsInNextLayout()8779         public void requestSimpleAnimationsInNextLayout() {
8780             mRequestedSimpleAnimations = true;
8781         }
8782 
8783         /**
8784          * Returns the selection mode for accessibility. Should be
8785          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE},
8786          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or
8787          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}.
8788          * <p>
8789          * Default implementation returns
8790          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
8791          *
8792          * @param recycler The Recycler that can be used to convert view positions into adapter
8793          *                 positions
8794          * @param state    The current state of RecyclerView
8795          * @return Selection mode for accessibility. Default implementation returns
8796          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
8797          */
getSelectionModeForAccessibility(Recycler recycler, State state)8798         public int getSelectionModeForAccessibility(Recycler recycler, State state) {
8799             return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
8800         }
8801 
8802         /**
8803          * Returns the number of rows for accessibility.
8804          * <p>
8805          * Default implementation returns the number of items in the adapter if LayoutManager
8806          * supports vertical scrolling or 1 if LayoutManager does not support vertical
8807          * scrolling.
8808          *
8809          * @param recycler The Recycler that can be used to convert view positions into adapter
8810          *                 positions
8811          * @param state    The current state of RecyclerView
8812          * @return The number of rows in LayoutManager for accessibility.
8813          */
getRowCountForAccessibility(Recycler recycler, State state)8814         public int getRowCountForAccessibility(Recycler recycler, State state) {
8815             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
8816                 return 1;
8817             }
8818             return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
8819         }
8820 
8821         /**
8822          * Returns the number of columns for accessibility.
8823          * <p>
8824          * Default implementation returns the number of items in the adapter if LayoutManager
8825          * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
8826          * scrolling.
8827          *
8828          * @param recycler The Recycler that can be used to convert view positions into adapter
8829          *                 positions
8830          * @param state    The current state of RecyclerView
8831          * @return The number of rows in LayoutManager for accessibility.
8832          */
getColumnCountForAccessibility(Recycler recycler, State state)8833         public int getColumnCountForAccessibility(Recycler recycler, State state) {
8834             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
8835                 return 1;
8836             }
8837             return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
8838         }
8839 
8840         /**
8841          * Returns whether layout is hierarchical or not to be used for accessibility.
8842          * <p>
8843          * Default implementation returns false.
8844          *
8845          * @param recycler The Recycler that can be used to convert view positions into adapter
8846          *                 positions
8847          * @param state    The current state of RecyclerView
8848          * @return True if layout is hierarchical.
8849          */
isLayoutHierarchical(Recycler recycler, State state)8850         public boolean isLayoutHierarchical(Recycler recycler, State state) {
8851             return false;
8852         }
8853 
8854         // called by accessibility delegate
performAccessibilityAction(int action, Bundle args)8855         boolean performAccessibilityAction(int action, Bundle args) {
8856             return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
8857                     action, args);
8858         }
8859 
8860         /**
8861          * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
8862          *
8863          * @param recycler  The Recycler that can be used to convert view positions into adapter
8864          *                  positions
8865          * @param state     The current state of RecyclerView
8866          * @param action    The action to perform
8867          * @param args      Optional action arguments
8868          * @see View#performAccessibilityAction(int, android.os.Bundle)
8869          */
performAccessibilityAction(Recycler recycler, State state, int action, Bundle args)8870         public boolean performAccessibilityAction(Recycler recycler, State state, int action,
8871                 Bundle args) {
8872             if (mRecyclerView == null) {
8873                 return false;
8874             }
8875             int vScroll = 0, hScroll = 0;
8876             switch (action) {
8877                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
8878                     if (ViewCompat.canScrollVertically(mRecyclerView, -1)) {
8879                         vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
8880                     }
8881                     if (ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
8882                         hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
8883                     }
8884                     break;
8885                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
8886                     if (ViewCompat.canScrollVertically(mRecyclerView, 1)) {
8887                         vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
8888                     }
8889                     if (ViewCompat.canScrollHorizontally(mRecyclerView, 1)) {
8890                         hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
8891                     }
8892                     break;
8893             }
8894             if (vScroll == 0 && hScroll == 0) {
8895                 return false;
8896             }
8897             mRecyclerView.scrollBy(hScroll, vScroll);
8898             return true;
8899         }
8900 
8901         // called by accessibility delegate
performAccessibilityActionForItem(View view, int action, Bundle args)8902         boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
8903             return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
8904                     view, action, args);
8905         }
8906 
8907         /**
8908          * Called by AccessibilityDelegate when an accessibility action is requested on one of the
8909          * children of LayoutManager.
8910          * <p>
8911          * Default implementation does not do anything.
8912          *
8913          * @param recycler The Recycler that can be used to convert view positions into adapter
8914          *                 positions
8915          * @param state    The current state of RecyclerView
8916          * @param view     The child view on which the action is performed
8917          * @param action   The action to perform
8918          * @param args     Optional action arguments
8919          * @return true if action is handled
8920          * @see View#performAccessibilityAction(int, android.os.Bundle)
8921          */
performAccessibilityActionForItem(Recycler recycler, State state, View view, int action, Bundle args)8922         public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
8923                 int action, Bundle args) {
8924             return false;
8925         }
8926 
8927         /**
8928          * Parse the xml attributes to get the most common properties used by layout managers.
8929          *
8930          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
8931          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
8932          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
8933          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
8934          *
8935          * @return an object containing the properties as specified in the attrs.
8936          */
getProperties(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)8937         public static Properties getProperties(Context context, AttributeSet attrs,
8938                 int defStyleAttr, int defStyleRes) {
8939             Properties properties = new Properties();
8940             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
8941                     defStyleAttr, defStyleRes);
8942             properties.orientation = a.getInt(R.styleable.RecyclerView_android_orientation, VERTICAL);
8943             properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
8944             properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
8945             properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
8946             a.recycle();
8947             return properties;
8948         }
8949 
setExactMeasureSpecsFrom(RecyclerView recyclerView)8950         void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
8951             setMeasureSpecs(
8952                     MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
8953                     MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
8954             );
8955         }
8956 
8957         /**
8958          * Internal API to allow LayoutManagers to be measured twice.
8959          * <p>
8960          * This is not public because LayoutManagers should be able to handle their layouts in one
8961          * pass but it is very convenient to make existing LayoutManagers support wrapping content
8962          * when both orientations are undefined.
8963          * <p>
8964          * This API will be removed after default LayoutManagers properly implement wrap content in
8965          * non-scroll orientation.
8966          */
shouldMeasureTwice()8967         boolean shouldMeasureTwice() {
8968             return false;
8969         }
8970 
hasFlexibleChildInBothOrientations()8971         boolean hasFlexibleChildInBothOrientations() {
8972             final int childCount = getChildCount();
8973             for (int i = 0; i < childCount; i++) {
8974                 final View child = getChildAt(i);
8975                 final ViewGroup.LayoutParams lp = child.getLayoutParams();
8976                 if (lp.width < 0 && lp.height < 0) {
8977                     return true;
8978                 }
8979             }
8980             return false;
8981         }
8982 
8983         /**
8984          * Some general properties that a LayoutManager may want to use.
8985          */
8986         public static class Properties {
8987             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation */
8988             public int orientation;
8989             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount */
8990             public int spanCount;
8991             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout */
8992             public boolean reverseLayout;
8993             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd */
8994             public boolean stackFromEnd;
8995         }
8996     }
8997 
8998     /**
8999      * An ItemDecoration allows the application to add a special drawing and layout offset
9000      * to specific item views from the adapter's data set. This can be useful for drawing dividers
9001      * between items, highlights, visual grouping boundaries and more.
9002      *
9003      * <p>All ItemDecorations are drawn in the order they were added, before the item
9004      * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
9005      * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
9006      * RecyclerView.State)}.</p>
9007      */
9008     public static abstract class ItemDecoration {
9009         /**
9010          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
9011          * Any content drawn by this method will be drawn before the item views are drawn,
9012          * and will thus appear underneath the views.
9013          *
9014          * @param c Canvas to draw into
9015          * @param parent RecyclerView this ItemDecoration is drawing into
9016          * @param state The current state of RecyclerView
9017          */
onDraw(Canvas c, RecyclerView parent, State state)9018         public void onDraw(Canvas c, RecyclerView parent, State state) {
9019             onDraw(c, parent);
9020         }
9021 
9022         /**
9023          * @deprecated
9024          * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
9025          */
9026         @Deprecated
onDraw(Canvas c, RecyclerView parent)9027         public void onDraw(Canvas c, RecyclerView parent) {
9028         }
9029 
9030         /**
9031          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
9032          * Any content drawn by this method will be drawn after the item views are drawn
9033          * and will thus appear over the views.
9034          *
9035          * @param c Canvas to draw into
9036          * @param parent RecyclerView this ItemDecoration is drawing into
9037          * @param state The current state of RecyclerView.
9038          */
onDrawOver(Canvas c, RecyclerView parent, State state)9039         public void onDrawOver(Canvas c, RecyclerView parent, State state) {
9040             onDrawOver(c, parent);
9041         }
9042 
9043         /**
9044          * @deprecated
9045          * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
9046          */
9047         @Deprecated
onDrawOver(Canvas c, RecyclerView parent)9048         public void onDrawOver(Canvas c, RecyclerView parent) {
9049         }
9050 
9051 
9052         /**
9053          * @deprecated
9054          * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
9055          */
9056         @Deprecated
getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)9057         public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
9058             outRect.set(0, 0, 0, 0);
9059         }
9060 
9061         /**
9062          * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
9063          * the number of pixels that the item view should be inset by, similar to padding or margin.
9064          * The default implementation sets the bounds of outRect to 0 and returns.
9065          *
9066          * <p>
9067          * If this ItemDecoration does not affect the positioning of item views, it should set
9068          * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
9069          * before returning.
9070          *
9071          * <p>
9072          * If you need to access Adapter for additional data, you can call
9073          * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
9074          * View.
9075          *
9076          * @param outRect Rect to receive the output.
9077          * @param view    The child view to decorate
9078          * @param parent  RecyclerView this ItemDecoration is decorating
9079          * @param state   The current state of RecyclerView.
9080          */
getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)9081         public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
9082             getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
9083                     parent);
9084         }
9085     }
9086 
9087     /**
9088      * An OnItemTouchListener allows the application to intercept touch events in progress at the
9089      * view hierarchy level of the RecyclerView before those touch events are considered for
9090      * RecyclerView's own scrolling behavior.
9091      *
9092      * <p>This can be useful for applications that wish to implement various forms of gestural
9093      * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
9094      * a touch interaction already in progress even if the RecyclerView is already handling that
9095      * gesture stream itself for the purposes of scrolling.</p>
9096      *
9097      * @see SimpleOnItemTouchListener
9098      */
9099     public static interface OnItemTouchListener {
9100         /**
9101          * Silently observe and/or take over touch events sent to the RecyclerView
9102          * before they are handled by either the RecyclerView itself or its child views.
9103          *
9104          * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
9105          * in the order in which each listener was added, before any other touch processing
9106          * by the RecyclerView itself or child views occurs.</p>
9107          *
9108          * @param e MotionEvent describing the touch event. All coordinates are in
9109          *          the RecyclerView's coordinate system.
9110          * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
9111          *         to continue with the current behavior and continue observing future events in
9112          *         the gesture.
9113          */
onInterceptTouchEvent(RecyclerView rv, MotionEvent e)9114         public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
9115 
9116         /**
9117          * Process a touch event as part of a gesture that was claimed by returning true from
9118          * a previous call to {@link #onInterceptTouchEvent}.
9119          *
9120          * @param e MotionEvent describing the touch event. All coordinates are in
9121          *          the RecyclerView's coordinate system.
9122          */
onTouchEvent(RecyclerView rv, MotionEvent e)9123         public void onTouchEvent(RecyclerView rv, MotionEvent e);
9124 
9125         /**
9126          * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
9127          * intercept touch events with
9128          * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
9129          *
9130          * @param disallowIntercept True if the child does not want the parent to
9131          *            intercept touch events.
9132          * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
9133          */
onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)9134         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
9135     }
9136 
9137     /**
9138      * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies and
9139      * default return values.
9140      * <p>
9141      * You may prefer to extend this class if you don't need to override all methods. Another
9142      * benefit of using this class is future compatibility. As the interface may change, we'll
9143      * always provide a default implementation on this class so that your code won't break when
9144      * you update to a new version of the support library.
9145      */
9146     public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
9147         @Override
onInterceptTouchEvent(RecyclerView rv, MotionEvent e)9148         public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
9149             return false;
9150         }
9151 
9152         @Override
onTouchEvent(RecyclerView rv, MotionEvent e)9153         public void onTouchEvent(RecyclerView rv, MotionEvent e) {
9154         }
9155 
9156         @Override
onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)9157         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
9158         }
9159     }
9160 
9161 
9162     /**
9163      * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
9164      * has occurred on that RecyclerView.
9165      * <p>
9166      * @see RecyclerView#addOnScrollListener(OnScrollListener)
9167      * @see RecyclerView#clearOnChildAttachStateChangeListeners()
9168      *
9169      */
9170     public abstract static class OnScrollListener {
9171         /**
9172          * Callback method to be invoked when RecyclerView's scroll state changes.
9173          *
9174          * @param recyclerView The RecyclerView whose scroll state has changed.
9175          * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
9176          *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
9177          */
onScrollStateChanged(RecyclerView recyclerView, int newState)9178         public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
9179 
9180         /**
9181          * Callback method to be invoked when the RecyclerView has been scrolled. This will be
9182          * called after the scroll has completed.
9183          * <p>
9184          * This callback will also be called if visible item range changes after a layout
9185          * calculation. In that case, dx and dy will be 0.
9186          *
9187          * @param recyclerView The RecyclerView which scrolled.
9188          * @param dx The amount of horizontal scroll.
9189          * @param dy The amount of vertical scroll.
9190          */
onScrolled(RecyclerView recyclerView, int dx, int dy)9191         public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
9192     }
9193 
9194     /**
9195      * A RecyclerListener can be set on a RecyclerView to receive messages whenever
9196      * a view is recycled.
9197      *
9198      * @see RecyclerView#setRecyclerListener(RecyclerListener)
9199      */
9200     public interface RecyclerListener {
9201 
9202         /**
9203          * This method is called whenever the view in the ViewHolder is recycled.
9204          *
9205          * RecyclerView calls this method right before clearing ViewHolder's internal data and
9206          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
9207          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
9208          * its adapter position.
9209          *
9210          * @param holder The ViewHolder containing the view that was recycled
9211          */
onViewRecycled(ViewHolder holder)9212         public void onViewRecycled(ViewHolder holder);
9213     }
9214 
9215     /**
9216      * A Listener interface that can be attached to a RecylcerView to get notified
9217      * whenever a ViewHolder is attached to or detached from RecyclerView.
9218      */
9219     public interface OnChildAttachStateChangeListener {
9220 
9221         /**
9222          * Called when a view is attached to the RecyclerView.
9223          *
9224          * @param view The View which is attached to the RecyclerView
9225          */
onChildViewAttachedToWindow(View view)9226         public void onChildViewAttachedToWindow(View view);
9227 
9228         /**
9229          * Called when a view is detached from RecyclerView.
9230          *
9231          * @param view The View which is being detached from the RecyclerView
9232          */
onChildViewDetachedFromWindow(View view)9233         public void onChildViewDetachedFromWindow(View view);
9234     }
9235 
9236     /**
9237      * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
9238      *
9239      * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
9240      * potentially expensive {@link View#findViewById(int)} results.</p>
9241      *
9242      * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
9243      * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
9244      * their own custom ViewHolder implementations to store data that makes binding view contents
9245      * easier. Implementations should assume that individual item views will hold strong references
9246      * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
9247      * strong references to extra off-screen item views for caching purposes</p>
9248      */
9249     public static abstract class ViewHolder {
9250         public final View itemView;
9251         int mPosition = NO_POSITION;
9252         int mOldPosition = NO_POSITION;
9253         long mItemId = NO_ID;
9254         int mItemViewType = INVALID_TYPE;
9255         int mPreLayoutPosition = NO_POSITION;
9256 
9257         // The item that this holder is shadowing during an item change event/animation
9258         ViewHolder mShadowedHolder = null;
9259         // The item that is shadowing this holder during an item change event/animation
9260         ViewHolder mShadowingHolder = null;
9261 
9262         /**
9263          * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
9264          * are all valid.
9265          */
9266         static final int FLAG_BOUND = 1 << 0;
9267 
9268         /**
9269          * The data this ViewHolder's view reflects is stale and needs to be rebound
9270          * by the adapter. mPosition and mItemId are consistent.
9271          */
9272         static final int FLAG_UPDATE = 1 << 1;
9273 
9274         /**
9275          * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
9276          * are not to be trusted and may no longer match the item view type.
9277          * This ViewHolder must be fully rebound to different data.
9278          */
9279         static final int FLAG_INVALID = 1 << 2;
9280 
9281         /**
9282          * This ViewHolder points at data that represents an item previously removed from the
9283          * data set. Its view may still be used for things like outgoing animations.
9284          */
9285         static final int FLAG_REMOVED = 1 << 3;
9286 
9287         /**
9288          * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
9289          * and is intended to keep views around during animations.
9290          */
9291         static final int FLAG_NOT_RECYCLABLE = 1 << 4;
9292 
9293         /**
9294          * This ViewHolder is returned from scrap which means we are expecting an addView call
9295          * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
9296          * the end of the layout pass and then recycled by RecyclerView if it is not added back to
9297          * the RecyclerView.
9298          */
9299         static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
9300 
9301         /**
9302          * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
9303          * it unless LayoutManager is replaced.
9304          * It is still fully visible to the LayoutManager.
9305          */
9306         static final int FLAG_IGNORE = 1 << 7;
9307 
9308         /**
9309          * When the View is detached form the parent, we set this flag so that we can take correct
9310          * action when we need to remove it or add it back.
9311          */
9312         static final int FLAG_TMP_DETACHED = 1 << 8;
9313 
9314         /**
9315          * Set when we can no longer determine the adapter position of this ViewHolder until it is
9316          * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
9317          * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
9318          * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
9319          * re-calculated.
9320          */
9321         static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
9322 
9323         /**
9324          * Set when a addChangePayload(null) is called
9325          */
9326         static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
9327 
9328         /**
9329          * Used by ItemAnimator when a ViewHolder's position changes
9330          */
9331         static final int FLAG_MOVED = 1 << 11;
9332 
9333         /**
9334          * Used by ItemAnimator when a ViewHolder appears in pre-layout
9335          */
9336         static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
9337 
9338         /**
9339          * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
9340          * hidden list (as if it was scrap) without being recycled in between.
9341          *
9342          * When a ViewHolder is hidden, there are 2 paths it can be re-used:
9343          *   a) Animation ends, view is recycled and used from the recycle pool.
9344          *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
9345          *
9346          * This flag is used to represent "case b" where the ViewHolder is reused without being
9347          * recycled (thus "bounced" from the hidden list). This state requires special handling
9348          * because the ViewHolder must be added to pre layout maps for animations as if it was
9349          * already there.
9350          */
9351         static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
9352 
9353         private int mFlags;
9354 
9355         private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
9356 
9357         List<Object> mPayloads = null;
9358         List<Object> mUnmodifiedPayloads = null;
9359 
9360         private int mIsRecyclableCount = 0;
9361 
9362         // If non-null, view is currently considered scrap and may be reused for other data by the
9363         // scrap container.
9364         private Recycler mScrapContainer = null;
9365         // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
9366         private boolean mInChangeScrap = false;
9367 
9368         // Saves isImportantForAccessibility value for the view item while it's in hidden state and
9369         // marked as unimportant for accessibility.
9370         private int mWasImportantForAccessibilityBeforeHidden =
9371                 ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
9372 
9373         /**
9374          * Is set when VH is bound from the adapter and cleaned right before it is sent to
9375          * {@link RecycledViewPool}.
9376          */
9377         RecyclerView mOwnerRecyclerView;
9378 
ViewHolder(View itemView)9379         public ViewHolder(View itemView) {
9380             if (itemView == null) {
9381                 throw new IllegalArgumentException("itemView may not be null");
9382             }
9383             this.itemView = itemView;
9384         }
9385 
flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout)9386         void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
9387             addFlags(ViewHolder.FLAG_REMOVED);
9388             offsetPosition(offset, applyToPreLayout);
9389             mPosition = mNewPosition;
9390         }
9391 
offsetPosition(int offset, boolean applyToPreLayout)9392         void offsetPosition(int offset, boolean applyToPreLayout) {
9393             if (mOldPosition == NO_POSITION) {
9394                 mOldPosition = mPosition;
9395             }
9396             if (mPreLayoutPosition == NO_POSITION) {
9397                 mPreLayoutPosition = mPosition;
9398             }
9399             if (applyToPreLayout) {
9400                 mPreLayoutPosition += offset;
9401             }
9402             mPosition += offset;
9403             if (itemView.getLayoutParams() != null) {
9404                 ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
9405             }
9406         }
9407 
clearOldPosition()9408         void clearOldPosition() {
9409             mOldPosition = NO_POSITION;
9410             mPreLayoutPosition = NO_POSITION;
9411         }
9412 
saveOldPosition()9413         void saveOldPosition() {
9414             if (mOldPosition == NO_POSITION) {
9415                 mOldPosition = mPosition;
9416             }
9417         }
9418 
shouldIgnore()9419         boolean shouldIgnore() {
9420             return (mFlags & FLAG_IGNORE) != 0;
9421         }
9422 
9423         /**
9424          * @deprecated This method is deprecated because its meaning is ambiguous due to the async
9425          * handling of adapter updates. Please use {@link #getLayoutPosition()} or
9426          * {@link #getAdapterPosition()} depending on your use case.
9427          *
9428          * @see #getLayoutPosition()
9429          * @see #getAdapterPosition()
9430          */
9431         @Deprecated
getPosition()9432         public final int getPosition() {
9433             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
9434         }
9435 
9436         /**
9437          * Returns the position of the ViewHolder in terms of the latest layout pass.
9438          * <p>
9439          * This position is mostly used by RecyclerView components to be consistent while
9440          * RecyclerView lazily processes adapter updates.
9441          * <p>
9442          * For performance and animation reasons, RecyclerView batches all adapter updates until the
9443          * next layout pass. This may cause mismatches between the Adapter position of the item and
9444          * the position it had in the latest layout calculations.
9445          * <p>
9446          * LayoutManagers should always call this method while doing calculations based on item
9447          * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
9448          * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
9449          * of the item.
9450          * <p>
9451          * If LayoutManager needs to call an external method that requires the adapter position of
9452          * the item, it can use {@link #getAdapterPosition()} or
9453          * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
9454          *
9455          * @return Returns the adapter position of the ViewHolder in the latest layout pass.
9456          * @see #getAdapterPosition()
9457          */
getLayoutPosition()9458         public final int getLayoutPosition() {
9459             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
9460         }
9461 
9462         /**
9463          * Returns the Adapter position of the item represented by this ViewHolder.
9464          * <p>
9465          * Note that this might be different than the {@link #getLayoutPosition()} if there are
9466          * pending adapter updates but a new layout pass has not happened yet.
9467          * <p>
9468          * RecyclerView does not handle any adapter updates until the next layout traversal. This
9469          * may create temporary inconsistencies between what user sees on the screen and what
9470          * adapter contents have. This inconsistency is not important since it will be less than
9471          * 16ms but it might be a problem if you want to use ViewHolder position to access the
9472          * adapter. Sometimes, you may need to get the exact adapter position to do
9473          * some actions in response to user events. In that case, you should use this method which
9474          * will calculate the Adapter position of the ViewHolder.
9475          * <p>
9476          * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
9477          * next layout pass, the return value of this method will be {@link #NO_POSITION}.
9478          *
9479          * @return The adapter position of the item if it still exists in the adapter.
9480          * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
9481          * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
9482          * layout pass or the ViewHolder has already been recycled.
9483          */
getAdapterPosition()9484         public final int getAdapterPosition() {
9485             if (mOwnerRecyclerView == null) {
9486                 return NO_POSITION;
9487             }
9488             return mOwnerRecyclerView.getAdapterPositionFor(this);
9489         }
9490 
9491         /**
9492          * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
9493          * to perform animations.
9494          * <p>
9495          * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
9496          * adapter index in the previous layout.
9497          *
9498          * @return The previous adapter index of the Item represented by this ViewHolder or
9499          * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
9500          * complete).
9501          */
getOldPosition()9502         public final int getOldPosition() {
9503             return mOldPosition;
9504         }
9505 
9506         /**
9507          * Returns The itemId represented by this ViewHolder.
9508          *
9509          * @return The the item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
9510          * otherwise
9511          */
getItemId()9512         public final long getItemId() {
9513             return mItemId;
9514         }
9515 
9516         /**
9517          * @return The view type of this ViewHolder.
9518          */
getItemViewType()9519         public final int getItemViewType() {
9520             return mItemViewType;
9521         }
9522 
isScrap()9523         boolean isScrap() {
9524             return mScrapContainer != null;
9525         }
9526 
unScrap()9527         void unScrap() {
9528             mScrapContainer.unscrapView(this);
9529         }
9530 
wasReturnedFromScrap()9531         boolean wasReturnedFromScrap() {
9532             return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
9533         }
9534 
clearReturnedFromScrapFlag()9535         void clearReturnedFromScrapFlag() {
9536             mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
9537         }
9538 
clearTmpDetachFlag()9539         void clearTmpDetachFlag() {
9540             mFlags = mFlags & ~FLAG_TMP_DETACHED;
9541         }
9542 
stopIgnoring()9543         void stopIgnoring() {
9544             mFlags = mFlags & ~FLAG_IGNORE;
9545         }
9546 
setScrapContainer(Recycler recycler, boolean isChangeScrap)9547         void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
9548             mScrapContainer = recycler;
9549             mInChangeScrap = isChangeScrap;
9550         }
9551 
isInvalid()9552         boolean isInvalid() {
9553             return (mFlags & FLAG_INVALID) != 0;
9554         }
9555 
needsUpdate()9556         boolean needsUpdate() {
9557             return (mFlags & FLAG_UPDATE) != 0;
9558         }
9559 
isBound()9560         boolean isBound() {
9561             return (mFlags & FLAG_BOUND) != 0;
9562         }
9563 
isRemoved()9564         boolean isRemoved() {
9565             return (mFlags & FLAG_REMOVED) != 0;
9566         }
9567 
hasAnyOfTheFlags(int flags)9568         boolean hasAnyOfTheFlags(int flags) {
9569             return (mFlags & flags) != 0;
9570         }
9571 
isTmpDetached()9572         boolean isTmpDetached() {
9573             return (mFlags & FLAG_TMP_DETACHED) != 0;
9574         }
9575 
isAdapterPositionUnknown()9576         boolean isAdapterPositionUnknown() {
9577             return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
9578         }
9579 
setFlags(int flags, int mask)9580         void setFlags(int flags, int mask) {
9581             mFlags = (mFlags & ~mask) | (flags & mask);
9582         }
9583 
addFlags(int flags)9584         void addFlags(int flags) {
9585             mFlags |= flags;
9586         }
9587 
addChangePayload(Object payload)9588         void addChangePayload(Object payload) {
9589             if (payload == null) {
9590                 addFlags(FLAG_ADAPTER_FULLUPDATE);
9591             } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
9592                 createPayloadsIfNeeded();
9593                 mPayloads.add(payload);
9594             }
9595         }
9596 
createPayloadsIfNeeded()9597         private void createPayloadsIfNeeded() {
9598             if (mPayloads == null) {
9599                 mPayloads = new ArrayList<Object>();
9600                 mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
9601             }
9602         }
9603 
clearPayload()9604         void clearPayload() {
9605             if (mPayloads != null) {
9606                 mPayloads.clear();
9607             }
9608             mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
9609         }
9610 
getUnmodifiedPayloads()9611         List<Object> getUnmodifiedPayloads() {
9612             if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
9613                 if (mPayloads == null || mPayloads.size() == 0) {
9614                     // Initial state,  no update being called.
9615                     return FULLUPDATE_PAYLOADS;
9616                 }
9617                 // there are none-null payloads
9618                 return mUnmodifiedPayloads;
9619             } else {
9620                 // a full update has been called.
9621                 return FULLUPDATE_PAYLOADS;
9622             }
9623         }
9624 
resetInternal()9625         void resetInternal() {
9626             mFlags = 0;
9627             mPosition = NO_POSITION;
9628             mOldPosition = NO_POSITION;
9629             mItemId = NO_ID;
9630             mPreLayoutPosition = NO_POSITION;
9631             mIsRecyclableCount = 0;
9632             mShadowedHolder = null;
9633             mShadowingHolder = null;
9634             clearPayload();
9635             mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
9636         }
9637 
9638         /**
9639          * Called when the child view enters the hidden state
9640          */
onEnteredHiddenState()9641         private void onEnteredHiddenState() {
9642             // While the view item is in hidden state, make it invisible for the accessibility.
9643             mWasImportantForAccessibilityBeforeHidden =
9644                     ViewCompat.getImportantForAccessibility(itemView);
9645             ViewCompat.setImportantForAccessibility(itemView,
9646                     ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
9647         }
9648 
9649         /**
9650          * Called when the child view leaves the hidden state
9651          */
onLeftHiddenState()9652         private void onLeftHiddenState() {
9653             ViewCompat.setImportantForAccessibility(
9654                     itemView, mWasImportantForAccessibilityBeforeHidden);
9655             mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
9656         }
9657 
9658         @Override
toString()9659         public String toString() {
9660             final StringBuilder sb = new StringBuilder("ViewHolder{" +
9661                     Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId +
9662                     ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
9663             if (isScrap()) {
9664                 sb.append(" scrap ")
9665                         .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
9666             }
9667             if (isInvalid()) sb.append(" invalid");
9668             if (!isBound()) sb.append(" unbound");
9669             if (needsUpdate()) sb.append(" update");
9670             if (isRemoved()) sb.append(" removed");
9671             if (shouldIgnore()) sb.append(" ignored");
9672             if (isTmpDetached()) sb.append(" tmpDetached");
9673             if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
9674             if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
9675 
9676             if (itemView.getParent() == null) sb.append(" no parent");
9677             sb.append("}");
9678             return sb.toString();
9679         }
9680 
9681         /**
9682          * Informs the recycler whether this item can be recycled. Views which are not
9683          * recyclable will not be reused for other items until setIsRecyclable() is
9684          * later set to true. Calls to setIsRecyclable() should always be paired (one
9685          * call to setIsRecyclabe(false) should always be matched with a later call to
9686          * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
9687          * reference-counted.
9688          *
9689          * @param recyclable Whether this item is available to be recycled. Default value
9690          * is true.
9691          */
setIsRecyclable(boolean recyclable)9692         public final void setIsRecyclable(boolean recyclable) {
9693             mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
9694             if (mIsRecyclableCount < 0) {
9695                 mIsRecyclableCount = 0;
9696                 if (DEBUG) {
9697                     throw new RuntimeException("isRecyclable decremented below 0: " +
9698                             "unmatched pair of setIsRecyable() calls for " + this);
9699                 }
9700                 Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: " +
9701                         "unmatched pair of setIsRecyable() calls for " + this);
9702             } else if (!recyclable && mIsRecyclableCount == 1) {
9703                 mFlags |= FLAG_NOT_RECYCLABLE;
9704             } else if (recyclable && mIsRecyclableCount == 0) {
9705                 mFlags &= ~FLAG_NOT_RECYCLABLE;
9706             }
9707             if (DEBUG) {
9708                 Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
9709             }
9710         }
9711 
9712         /**
9713          * @see {@link #setIsRecyclable(boolean)}
9714          *
9715          * @return true if this item is available to be recycled, false otherwise.
9716          */
isRecyclable()9717         public final boolean isRecyclable() {
9718             return (mFlags & FLAG_NOT_RECYCLABLE) == 0 &&
9719                     !ViewCompat.hasTransientState(itemView);
9720         }
9721 
9722         /**
9723          * Returns whether we have animations referring to this view holder or not.
9724          * This is similar to isRecyclable flag but does not check transient state.
9725          */
shouldBeKeptAsChild()9726         private boolean shouldBeKeptAsChild() {
9727             return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
9728         }
9729 
9730         /**
9731          * @return True if ViewHolder is not refenrenced by RecyclerView animations but has
9732          * transient state which will prevent it from being recycled.
9733          */
doesTransientStatePreventRecycling()9734         private boolean doesTransientStatePreventRecycling() {
9735             return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView);
9736         }
9737 
isUpdated()9738         boolean isUpdated() {
9739             return (mFlags & FLAG_UPDATE) != 0;
9740         }
9741     }
9742 
getAdapterPositionFor(ViewHolder viewHolder)9743     private int getAdapterPositionFor(ViewHolder viewHolder) {
9744         if (viewHolder.hasAnyOfTheFlags( ViewHolder.FLAG_INVALID |
9745                 ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
9746                 || !viewHolder.isBound()) {
9747             return RecyclerView.NO_POSITION;
9748         }
9749         return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
9750     }
9751 
9752     // NestedScrollingChild
9753 
9754     @Override
setNestedScrollingEnabled(boolean enabled)9755     public void setNestedScrollingEnabled(boolean enabled) {
9756         getScrollingChildHelper().setNestedScrollingEnabled(enabled);
9757     }
9758 
9759     @Override
isNestedScrollingEnabled()9760     public boolean isNestedScrollingEnabled() {
9761         return getScrollingChildHelper().isNestedScrollingEnabled();
9762     }
9763 
9764     @Override
startNestedScroll(int axes)9765     public boolean startNestedScroll(int axes) {
9766         return getScrollingChildHelper().startNestedScroll(axes);
9767     }
9768 
9769     @Override
stopNestedScroll()9770     public void stopNestedScroll() {
9771         getScrollingChildHelper().stopNestedScroll();
9772     }
9773 
9774     @Override
hasNestedScrollingParent()9775     public boolean hasNestedScrollingParent() {
9776         return getScrollingChildHelper().hasNestedScrollingParent();
9777     }
9778 
9779     @Override
dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow)9780     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
9781             int dyUnconsumed, int[] offsetInWindow) {
9782         return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
9783                 dxUnconsumed, dyUnconsumed, offsetInWindow);
9784     }
9785 
9786     @Override
dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow)9787     public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
9788         return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
9789     }
9790 
9791     @Override
dispatchNestedFling(float velocityX, float velocityY, boolean consumed)9792     public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
9793         return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
9794     }
9795 
9796     @Override
dispatchNestedPreFling(float velocityX, float velocityY)9797     public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
9798         return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
9799     }
9800 
9801     /**
9802      * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
9803      * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
9804      * to create their own subclass of this <code>LayoutParams</code> class
9805      * to store any additional required per-child view metadata about the layout.
9806      */
9807     public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
9808         ViewHolder mViewHolder;
9809         final Rect mDecorInsets = new Rect();
9810         boolean mInsetsDirty = true;
9811         // Flag is set to true if the view is bound while it is detached from RV.
9812         // In this case, we need to manually call invalidate after view is added to guarantee that
9813         // invalidation is populated through the View hierarchy
9814         boolean mPendingInvalidate = false;
9815 
LayoutParams(Context c, AttributeSet attrs)9816         public LayoutParams(Context c, AttributeSet attrs) {
9817             super(c, attrs);
9818         }
9819 
LayoutParams(int width, int height)9820         public LayoutParams(int width, int height) {
9821             super(width, height);
9822         }
9823 
LayoutParams(MarginLayoutParams source)9824         public LayoutParams(MarginLayoutParams source) {
9825             super(source);
9826         }
9827 
LayoutParams(ViewGroup.LayoutParams source)9828         public LayoutParams(ViewGroup.LayoutParams source) {
9829             super(source);
9830         }
9831 
LayoutParams(LayoutParams source)9832         public LayoutParams(LayoutParams source) {
9833             super((ViewGroup.LayoutParams) source);
9834         }
9835 
9836         /**
9837          * Returns true if the view this LayoutParams is attached to needs to have its content
9838          * updated from the corresponding adapter.
9839          *
9840          * @return true if the view should have its content updated
9841          */
viewNeedsUpdate()9842         public boolean viewNeedsUpdate() {
9843             return mViewHolder.needsUpdate();
9844         }
9845 
9846         /**
9847          * Returns true if the view this LayoutParams is attached to is now representing
9848          * potentially invalid data. A LayoutManager should scrap/recycle it.
9849          *
9850          * @return true if the view is invalid
9851          */
isViewInvalid()9852         public boolean isViewInvalid() {
9853             return mViewHolder.isInvalid();
9854         }
9855 
9856         /**
9857          * Returns true if the adapter data item corresponding to the view this LayoutParams
9858          * is attached to has been removed from the data set. A LayoutManager may choose to
9859          * treat it differently in order to animate its outgoing or disappearing state.
9860          *
9861          * @return true if the item the view corresponds to was removed from the data set
9862          */
isItemRemoved()9863         public boolean isItemRemoved() {
9864             return mViewHolder.isRemoved();
9865         }
9866 
9867         /**
9868          * Returns true if the adapter data item corresponding to the view this LayoutParams
9869          * is attached to has been changed in the data set. A LayoutManager may choose to
9870          * treat it differently in order to animate its changing state.
9871          *
9872          * @return true if the item the view corresponds to was changed in the data set
9873          */
isItemChanged()9874         public boolean isItemChanged() {
9875             return mViewHolder.isUpdated();
9876         }
9877 
9878         /**
9879          * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
9880          */
9881         @Deprecated
getViewPosition()9882         public int getViewPosition() {
9883             return mViewHolder.getPosition();
9884         }
9885 
9886         /**
9887          * Returns the adapter position that the view this LayoutParams is attached to corresponds
9888          * to as of latest layout calculation.
9889          *
9890          * @return the adapter position this view as of latest layout pass
9891          */
getViewLayoutPosition()9892         public int getViewLayoutPosition() {
9893             return mViewHolder.getLayoutPosition();
9894         }
9895 
9896         /**
9897          * Returns the up-to-date adapter position that the view this LayoutParams is attached to
9898          * corresponds to.
9899          *
9900          * @return the up-to-date adapter position this view. It may return
9901          * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
9902          * its up-to-date position cannot be calculated.
9903          */
getViewAdapterPosition()9904         public int getViewAdapterPosition() {
9905             return mViewHolder.getAdapterPosition();
9906         }
9907     }
9908 
9909     /**
9910      * Observer base class for watching changes to an {@link Adapter}.
9911      * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
9912      */
9913     public static abstract class AdapterDataObserver {
onChanged()9914         public void onChanged() {
9915             // Do nothing
9916         }
9917 
onItemRangeChanged(int positionStart, int itemCount)9918         public void onItemRangeChanged(int positionStart, int itemCount) {
9919             // do nothing
9920         }
9921 
onItemRangeChanged(int positionStart, int itemCount, Object payload)9922         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
9923             // fallback to onItemRangeChanged(positionStart, itemCount) if app
9924             // does not override this method.
9925             onItemRangeChanged(positionStart, itemCount);
9926         }
9927 
onItemRangeInserted(int positionStart, int itemCount)9928         public void onItemRangeInserted(int positionStart, int itemCount) {
9929             // do nothing
9930         }
9931 
onItemRangeRemoved(int positionStart, int itemCount)9932         public void onItemRangeRemoved(int positionStart, int itemCount) {
9933             // do nothing
9934         }
9935 
onItemRangeMoved(int fromPosition, int toPosition, int itemCount)9936         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
9937             // do nothing
9938         }
9939     }
9940 
9941     /**
9942      * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
9943      * provides methods to trigger a programmatic scroll.</p>
9944      *
9945      * @see LinearSmoothScroller
9946      */
9947     public static abstract class SmoothScroller {
9948 
9949         private int mTargetPosition = RecyclerView.NO_POSITION;
9950 
9951         private RecyclerView mRecyclerView;
9952 
9953         private LayoutManager mLayoutManager;
9954 
9955         private boolean mPendingInitialRun;
9956 
9957         private boolean mRunning;
9958 
9959         private View mTargetView;
9960 
9961         private final Action mRecyclingAction;
9962 
SmoothScroller()9963         public SmoothScroller() {
9964             mRecyclingAction = new Action(0, 0);
9965         }
9966 
9967         /**
9968          * Starts a smooth scroll for the given target position.
9969          * <p>In each animation step, {@link RecyclerView} will check
9970          * for the target view and call either
9971          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
9972          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
9973          * SmoothScroller is stopped.</p>
9974          *
9975          * <p>Note that if RecyclerView finds the target view, it will automatically stop the
9976          * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
9977          * stop calling SmoothScroller in each animation step.</p>
9978          */
start(RecyclerView recyclerView, LayoutManager layoutManager)9979         void start(RecyclerView recyclerView, LayoutManager layoutManager) {
9980             mRecyclerView = recyclerView;
9981             mLayoutManager = layoutManager;
9982             if (mTargetPosition == RecyclerView.NO_POSITION) {
9983                 throw new IllegalArgumentException("Invalid target position");
9984             }
9985             mRecyclerView.mState.mTargetPosition = mTargetPosition;
9986             mRunning = true;
9987             mPendingInitialRun = true;
9988             mTargetView = findViewByPosition(getTargetPosition());
9989             onStart();
9990             mRecyclerView.mViewFlinger.postOnAnimation();
9991         }
9992 
setTargetPosition(int targetPosition)9993         public void setTargetPosition(int targetPosition) {
9994             mTargetPosition = targetPosition;
9995         }
9996 
9997         /**
9998          * @return The LayoutManager to which this SmoothScroller is attached. Will return
9999          * <code>null</code> after the SmoothScroller is stopped.
10000          */
10001         @Nullable
getLayoutManager()10002         public LayoutManager getLayoutManager() {
10003             return mLayoutManager;
10004         }
10005 
10006         /**
10007          * Stops running the SmoothScroller in each animation callback. Note that this does not
10008          * cancel any existing {@link Action} updated by
10009          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
10010          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
10011          */
stop()10012         final protected void stop() {
10013             if (!mRunning) {
10014                 return;
10015             }
10016             onStop();
10017             mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
10018             mTargetView = null;
10019             mTargetPosition = RecyclerView.NO_POSITION;
10020             mPendingInitialRun = false;
10021             mRunning = false;
10022             // trigger a cleanup
10023             mLayoutManager.onSmoothScrollerStopped(this);
10024             // clear references to avoid any potential leak by a custom smooth scroller
10025             mLayoutManager = null;
10026             mRecyclerView = null;
10027         }
10028 
10029         /**
10030          * Returns true if SmoothScroller has been started but has not received the first
10031          * animation
10032          * callback yet.
10033          *
10034          * @return True if this SmoothScroller is waiting to start
10035          */
isPendingInitialRun()10036         public boolean isPendingInitialRun() {
10037             return mPendingInitialRun;
10038         }
10039 
10040 
10041         /**
10042          * @return True if SmoothScroller is currently active
10043          */
isRunning()10044         public boolean isRunning() {
10045             return mRunning;
10046         }
10047 
10048         /**
10049          * Returns the adapter position of the target item
10050          *
10051          * @return Adapter position of the target item or
10052          * {@link RecyclerView#NO_POSITION} if no target view is set.
10053          */
getTargetPosition()10054         public int getTargetPosition() {
10055             return mTargetPosition;
10056         }
10057 
onAnimation(int dx, int dy)10058         private void onAnimation(int dx, int dy) {
10059             final RecyclerView recyclerView = mRecyclerView;
10060             if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
10061                 stop();
10062             }
10063             mPendingInitialRun = false;
10064             if (mTargetView != null) {
10065                 // verify target position
10066                 if (getChildPosition(mTargetView) == mTargetPosition) {
10067                     onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
10068                     mRecyclingAction.runIfNecessary(recyclerView);
10069                     stop();
10070                 } else {
10071                     Log.e(TAG, "Passed over target position while smooth scrolling.");
10072                     mTargetView = null;
10073                 }
10074             }
10075             if (mRunning) {
10076                 onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
10077                 boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
10078                 mRecyclingAction.runIfNecessary(recyclerView);
10079                 if (hadJumpTarget) {
10080                     // It is not stopped so needs to be restarted
10081                     if (mRunning) {
10082                         mPendingInitialRun = true;
10083                         recyclerView.mViewFlinger.postOnAnimation();
10084                     } else {
10085                         stop(); // done
10086                     }
10087                 }
10088             }
10089         }
10090 
10091         /**
10092          * @see RecyclerView#getChildLayoutPosition(android.view.View)
10093          */
getChildPosition(View view)10094         public int getChildPosition(View view) {
10095             return mRecyclerView.getChildLayoutPosition(view);
10096         }
10097 
10098         /**
10099          * @see RecyclerView.LayoutManager#getChildCount()
10100          */
getChildCount()10101         public int getChildCount() {
10102             return mRecyclerView.mLayout.getChildCount();
10103         }
10104 
10105         /**
10106          * @see RecyclerView.LayoutManager#findViewByPosition(int)
10107          */
findViewByPosition(int position)10108         public View findViewByPosition(int position) {
10109             return mRecyclerView.mLayout.findViewByPosition(position);
10110         }
10111 
10112         /**
10113          * @see RecyclerView#scrollToPosition(int)
10114          * @deprecated Use {@link Action#jumpTo(int)}.
10115          */
10116         @Deprecated
instantScrollToPosition(int position)10117         public void instantScrollToPosition(int position) {
10118             mRecyclerView.scrollToPosition(position);
10119         }
10120 
onChildAttachedToWindow(View child)10121         protected void onChildAttachedToWindow(View child) {
10122             if (getChildPosition(child) == getTargetPosition()) {
10123                 mTargetView = child;
10124                 if (DEBUG) {
10125                     Log.d(TAG, "smooth scroll target view has been attached");
10126                 }
10127             }
10128         }
10129 
10130         /**
10131          * Normalizes the vector.
10132          * @param scrollVector The vector that points to the target scroll position
10133          */
normalize(PointF scrollVector)10134         protected void normalize(PointF scrollVector) {
10135             final double magnitute = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y *
10136                     scrollVector.y);
10137             scrollVector.x /= magnitute;
10138             scrollVector.y /= magnitute;
10139         }
10140 
10141         /**
10142          * Called when smooth scroll is started. This might be a good time to do setup.
10143          */
onStart()10144         abstract protected void onStart();
10145 
10146         /**
10147          * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
10148          * @see #stop()
10149          */
onStop()10150         abstract protected void onStop();
10151 
10152         /**
10153          * <p>RecyclerView will call this method each time it scrolls until it can find the target
10154          * position in the layout.</p>
10155          * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
10156          * provided {@link Action} to define the next scroll.</p>
10157          *
10158          * @param dx        Last scroll amount horizontally
10159          * @param dy        Last scroll amount verticaully
10160          * @param state     Transient state of RecyclerView
10161          * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
10162          *                  update this object.
10163          */
onSeekTargetStep(int dx, int dy, State state, Action action)10164         abstract protected void onSeekTargetStep(int dx, int dy, State state, Action action);
10165 
10166         /**
10167          * Called when the target position is laid out. This is the last callback SmoothScroller
10168          * will receive and it should update the provided {@link Action} to define the scroll
10169          * details towards the target view.
10170          * @param targetView    The view element which render the target position.
10171          * @param state         Transient state of RecyclerView
10172          * @param action        Action instance that you should update to define final scroll action
10173          *                      towards the targetView
10174          */
onTargetFound(View targetView, State state, Action action)10175         abstract protected void onTargetFound(View targetView, State state, Action action);
10176 
10177         /**
10178          * Holds information about a smooth scroll request by a {@link SmoothScroller}.
10179          */
10180         public static class Action {
10181 
10182             public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
10183 
10184             private int mDx;
10185 
10186             private int mDy;
10187 
10188             private int mDuration;
10189 
10190             private int mJumpToPosition = NO_POSITION;
10191 
10192             private Interpolator mInterpolator;
10193 
10194             private boolean changed = false;
10195 
10196             // we track this variable to inform custom implementer if they are updating the action
10197             // in every animation callback
10198             private int consecutiveUpdates = 0;
10199 
10200             /**
10201              * @param dx Pixels to scroll horizontally
10202              * @param dy Pixels to scroll vertically
10203              */
Action(int dx, int dy)10204             public Action(int dx, int dy) {
10205                 this(dx, dy, UNDEFINED_DURATION, null);
10206             }
10207 
10208             /**
10209              * @param dx       Pixels to scroll horizontally
10210              * @param dy       Pixels to scroll vertically
10211              * @param duration Duration of the animation in milliseconds
10212              */
Action(int dx, int dy, int duration)10213             public Action(int dx, int dy, int duration) {
10214                 this(dx, dy, duration, null);
10215             }
10216 
10217             /**
10218              * @param dx           Pixels to scroll horizontally
10219              * @param dy           Pixels to scroll vertically
10220              * @param duration     Duration of the animation in milliseconds
10221              * @param interpolator Interpolator to be used when calculating scroll position in each
10222              *                     animation step
10223              */
Action(int dx, int dy, int duration, Interpolator interpolator)10224             public Action(int dx, int dy, int duration, Interpolator interpolator) {
10225                 mDx = dx;
10226                 mDy = dy;
10227                 mDuration = duration;
10228                 mInterpolator = interpolator;
10229             }
10230 
10231             /**
10232              * Instead of specifying pixels to scroll, use the target position to jump using
10233              * {@link RecyclerView#scrollToPosition(int)}.
10234              * <p>
10235              * You may prefer using this method if scroll target is really far away and you prefer
10236              * to jump to a location and smooth scroll afterwards.
10237              * <p>
10238              * Note that calling this method takes priority over other update methods such as
10239              * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
10240              * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
10241              * {@link #jumpTo(int)}, the other changes will not be considered for this animation
10242              * frame.
10243              *
10244              * @param targetPosition The target item position to scroll to using instant scrolling.
10245              */
jumpTo(int targetPosition)10246             public void jumpTo(int targetPosition) {
10247                 mJumpToPosition = targetPosition;
10248             }
10249 
hasJumpTarget()10250             boolean hasJumpTarget() {
10251                 return mJumpToPosition >= 0;
10252             }
10253 
runIfNecessary(RecyclerView recyclerView)10254             private void runIfNecessary(RecyclerView recyclerView) {
10255                 if (mJumpToPosition >= 0) {
10256                     final int position = mJumpToPosition;
10257                     mJumpToPosition = NO_POSITION;
10258                     recyclerView.jumpToPositionForSmoothScroller(position);
10259                     changed = false;
10260                     return;
10261                 }
10262                 if (changed) {
10263                     validate();
10264                     if (mInterpolator == null) {
10265                         if (mDuration == UNDEFINED_DURATION) {
10266                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
10267                         } else {
10268                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
10269                         }
10270                     } else {
10271                         recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration, mInterpolator);
10272                     }
10273                     consecutiveUpdates ++;
10274                     if (consecutiveUpdates > 10) {
10275                         // A new action is being set in every animation step. This looks like a bad
10276                         // implementation. Inform developer.
10277                         Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
10278                                 + " you are not changing it unless necessary");
10279                     }
10280                     changed = false;
10281                 } else {
10282                     consecutiveUpdates = 0;
10283                 }
10284             }
10285 
validate()10286             private void validate() {
10287                 if (mInterpolator != null && mDuration < 1) {
10288                     throw new IllegalStateException("If you provide an interpolator, you must"
10289                             + " set a positive duration");
10290                 } else if (mDuration < 1) {
10291                     throw new IllegalStateException("Scroll duration must be a positive number");
10292                 }
10293             }
10294 
getDx()10295             public int getDx() {
10296                 return mDx;
10297             }
10298 
setDx(int dx)10299             public void setDx(int dx) {
10300                 changed = true;
10301                 mDx = dx;
10302             }
10303 
getDy()10304             public int getDy() {
10305                 return mDy;
10306             }
10307 
setDy(int dy)10308             public void setDy(int dy) {
10309                 changed = true;
10310                 mDy = dy;
10311             }
10312 
getDuration()10313             public int getDuration() {
10314                 return mDuration;
10315             }
10316 
setDuration(int duration)10317             public void setDuration(int duration) {
10318                 changed = true;
10319                 mDuration = duration;
10320             }
10321 
getInterpolator()10322             public Interpolator getInterpolator() {
10323                 return mInterpolator;
10324             }
10325 
10326             /**
10327              * Sets the interpolator to calculate scroll steps
10328              * @param interpolator The interpolator to use. If you specify an interpolator, you must
10329              *                     also set the duration.
10330              * @see #setDuration(int)
10331              */
setInterpolator(Interpolator interpolator)10332             public void setInterpolator(Interpolator interpolator) {
10333                 changed = true;
10334                 mInterpolator = interpolator;
10335             }
10336 
10337             /**
10338              * Updates the action with given parameters.
10339              * @param dx Pixels to scroll horizontally
10340              * @param dy Pixels to scroll vertically
10341              * @param duration Duration of the animation in milliseconds
10342              * @param interpolator Interpolator to be used when calculating scroll position in each
10343              *                     animation step
10344              */
update(int dx, int dy, int duration, Interpolator interpolator)10345             public void update(int dx, int dy, int duration, Interpolator interpolator) {
10346                 mDx = dx;
10347                 mDy = dy;
10348                 mDuration = duration;
10349                 mInterpolator = interpolator;
10350                 changed = true;
10351             }
10352         }
10353     }
10354 
10355     static class AdapterDataObservable extends Observable<AdapterDataObserver> {
hasObservers()10356         public boolean hasObservers() {
10357             return !mObservers.isEmpty();
10358         }
10359 
notifyChanged()10360         public void notifyChanged() {
10361             // since onChanged() is implemented by the app, it could do anything, including
10362             // removing itself from {@link mObservers} - and that could cause problems if
10363             // an iterator is used on the ArrayList {@link mObservers}.
10364             // to avoid such problems, just march thru the list in the reverse order.
10365             for (int i = mObservers.size() - 1; i >= 0; i--) {
10366                 mObservers.get(i).onChanged();
10367             }
10368         }
10369 
notifyItemRangeChanged(int positionStart, int itemCount)10370         public void notifyItemRangeChanged(int positionStart, int itemCount) {
10371             notifyItemRangeChanged(positionStart, itemCount, null);
10372         }
10373 
notifyItemRangeChanged(int positionStart, int itemCount, Object payload)10374         public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
10375             // since onItemRangeChanged() is implemented by the app, it could do anything, including
10376             // removing itself from {@link mObservers} - and that could cause problems if
10377             // an iterator is used on the ArrayList {@link mObservers}.
10378             // to avoid such problems, just march thru the list in the reverse order.
10379             for (int i = mObservers.size() - 1; i >= 0; i--) {
10380                 mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
10381             }
10382         }
10383 
notifyItemRangeInserted(int positionStart, int itemCount)10384         public void notifyItemRangeInserted(int positionStart, int itemCount) {
10385             // since onItemRangeInserted() is implemented by the app, it could do anything,
10386             // including removing itself from {@link mObservers} - and that could cause problems if
10387             // an iterator is used on the ArrayList {@link mObservers}.
10388             // to avoid such problems, just march thru the list in the reverse order.
10389             for (int i = mObservers.size() - 1; i >= 0; i--) {
10390                 mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
10391             }
10392         }
10393 
notifyItemRangeRemoved(int positionStart, int itemCount)10394         public void notifyItemRangeRemoved(int positionStart, int itemCount) {
10395             // since onItemRangeRemoved() is implemented by the app, it could do anything, including
10396             // removing itself from {@link mObservers} - and that could cause problems if
10397             // an iterator is used on the ArrayList {@link mObservers}.
10398             // to avoid such problems, just march thru the list in the reverse order.
10399             for (int i = mObservers.size() - 1; i >= 0; i--) {
10400                 mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
10401             }
10402         }
10403 
notifyItemMoved(int fromPosition, int toPosition)10404         public void notifyItemMoved(int fromPosition, int toPosition) {
10405             for (int i = mObservers.size() - 1; i >= 0; i--) {
10406                 mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
10407             }
10408         }
10409     }
10410 
10411     /**
10412      * This is public so that the CREATOR can be access on cold launch.
10413      * @hide
10414      */
10415     public static class SavedState extends AbsSavedState {
10416 
10417         Parcelable mLayoutState;
10418 
10419         /**
10420          * called by CREATOR
10421          */
SavedState(Parcel in, ClassLoader loader)10422         SavedState(Parcel in, ClassLoader loader) {
10423             super(in, loader);
10424             mLayoutState = in.readParcelable(
10425                     loader != null ? loader : LayoutManager.class.getClassLoader());
10426         }
10427 
10428         /**
10429          * Called by onSaveInstanceState
10430          */
SavedState(Parcelable superState)10431         SavedState(Parcelable superState) {
10432             super(superState);
10433         }
10434 
10435         @Override
writeToParcel(Parcel dest, int flags)10436         public void writeToParcel(Parcel dest, int flags) {
10437             super.writeToParcel(dest, flags);
10438             dest.writeParcelable(mLayoutState, 0);
10439         }
10440 
copyFrom(SavedState other)10441         private void copyFrom(SavedState other) {
10442             mLayoutState = other.mLayoutState;
10443         }
10444 
10445         public static final Creator<SavedState> CREATOR = ParcelableCompat.newCreator(
10446                 new ParcelableCompatCreatorCallbacks<SavedState>() {
10447                     @Override
10448                     public SavedState createFromParcel(Parcel in, ClassLoader loader) {
10449                         return new SavedState(in, loader);
10450                     }
10451 
10452                     @Override
10453                     public SavedState[] newArray(int size) {
10454                         return new SavedState[size];
10455                     }
10456                 });
10457     }
10458     /**
10459      * <p>Contains useful information about the current RecyclerView state like target scroll
10460      * position or view focus. State object can also keep arbitrary data, identified by resource
10461      * ids.</p>
10462      * <p>Often times, RecyclerView components will need to pass information between each other.
10463      * To provide a well defined data bus between components, RecyclerView passes the same State
10464      * object to component callbacks and these components can use it to exchange data.</p>
10465      * <p>If you implement custom components, you can use State's put/get/remove methods to pass
10466      * data between your components without needing to manage their lifecycles.</p>
10467      */
10468     public static class State {
10469         static final int STEP_START = 1;
10470         static final int STEP_LAYOUT = 1 << 1;
10471         static final int STEP_ANIMATIONS = 1 << 2;
10472 
assertLayoutStep(int accepted)10473         void assertLayoutStep(int accepted) {
10474             if ((accepted & mLayoutStep) == 0) {
10475                 throw new IllegalStateException("Layout state should be one of "
10476                         + Integer.toBinaryString(accepted) + " but it is "
10477                         + Integer.toBinaryString(mLayoutStep));
10478             }
10479         }
10480 
10481         @IntDef(flag = true, value = {
10482                 STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
10483         })
10484         @Retention(RetentionPolicy.SOURCE)
10485         @interface LayoutState {}
10486 
10487         private int mTargetPosition = RecyclerView.NO_POSITION;
10488 
10489         @LayoutState
10490         private int mLayoutStep = STEP_START;
10491 
10492         private SparseArray<Object> mData;
10493 
10494         /**
10495          * Number of items adapter has.
10496          */
10497         int mItemCount = 0;
10498 
10499         /**
10500          * Number of items adapter had in the previous layout.
10501          */
10502         private int mPreviousLayoutItemCount = 0;
10503 
10504         /**
10505          * Number of items that were NOT laid out but has been deleted from the adapter after the
10506          * previous layout.
10507          */
10508         private int mDeletedInvisibleItemCountSincePreviousLayout = 0;
10509 
10510         private boolean mStructureChanged = false;
10511 
10512         private boolean mInPreLayout = false;
10513 
10514         private boolean mRunSimpleAnimations = false;
10515 
10516         private boolean mRunPredictiveAnimations = false;
10517 
10518         private boolean mTrackOldChangeHolders = false;
10519 
10520         private boolean mIsMeasuring = false;
10521 
10522         /**
10523          * This data is saved before a layout calculation happens. After the layout is finished,
10524          * if the previously focused view has been replaced with another view for the same item, we
10525          * move the focus to the new item automatically.
10526          */
10527         int mFocusedItemPosition;
10528         long mFocusedItemId;
10529         // when a sub child has focus, record its id and see if we can directly request focus on
10530         // that one instead
10531         int mFocusedSubChildId;
10532 
reset()10533         State reset() {
10534             mTargetPosition = RecyclerView.NO_POSITION;
10535             if (mData != null) {
10536                 mData.clear();
10537             }
10538             mItemCount = 0;
10539             mStructureChanged = false;
10540             mIsMeasuring = false;
10541             return this;
10542         }
10543 
10544         /**
10545          * Returns true if the RecyclerView is currently measuring the layout. This value is
10546          * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
10547          * has non-exact measurement specs.
10548          * <p>
10549          * Note that if the LayoutManager supports predictive animations and it is calculating the
10550          * pre-layout step, this value will be {@code false} even if the RecyclerView is in
10551          * {@code onMeasure} call. This is because pre-layout means the previous state of the
10552          * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
10553          * LayoutManager is always guaranteed to receive another call to
10554          * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
10555          *
10556          * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
10557          */
isMeasuring()10558         public boolean isMeasuring() {
10559             return mIsMeasuring;
10560         }
10561 
10562         /**
10563          * Returns true if
10564          * @return
10565          */
isPreLayout()10566         public boolean isPreLayout() {
10567             return mInPreLayout;
10568         }
10569 
10570         /**
10571          * Returns whether RecyclerView will run predictive animations in this layout pass
10572          * or not.
10573          *
10574          * @return true if RecyclerView is calculating predictive animations to be run at the end
10575          *         of the layout pass.
10576          */
willRunPredictiveAnimations()10577         public boolean willRunPredictiveAnimations() {
10578             return mRunPredictiveAnimations;
10579         }
10580 
10581         /**
10582          * Returns whether RecyclerView will run simple animations in this layout pass
10583          * or not.
10584          *
10585          * @return true if RecyclerView is calculating simple animations to be run at the end of
10586          *         the layout pass.
10587          */
willRunSimpleAnimations()10588         public boolean willRunSimpleAnimations() {
10589             return mRunSimpleAnimations;
10590         }
10591 
10592         /**
10593          * Removes the mapping from the specified id, if there was any.
10594          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
10595          *                   preserve cross functionality and avoid conflicts.
10596          */
remove(int resourceId)10597         public void remove(int resourceId) {
10598             if (mData == null) {
10599                 return;
10600             }
10601             mData.remove(resourceId);
10602         }
10603 
10604         /**
10605          * Gets the Object mapped from the specified id, or <code>null</code>
10606          * if no such data exists.
10607          *
10608          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
10609          *                   to
10610          *                   preserve cross functionality and avoid conflicts.
10611          */
get(int resourceId)10612         public <T> T get(int resourceId) {
10613             if (mData == null) {
10614                 return null;
10615             }
10616             return (T) mData.get(resourceId);
10617         }
10618 
10619         /**
10620          * Adds a mapping from the specified id to the specified value, replacing the previous
10621          * mapping from the specified key if there was one.
10622          *
10623          * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
10624          *                   preserve cross functionality and avoid conflicts.
10625          * @param data       The data you want to associate with the resourceId.
10626          */
put(int resourceId, Object data)10627         public void put(int resourceId, Object data) {
10628             if (mData == null) {
10629                 mData = new SparseArray<Object>();
10630             }
10631             mData.put(resourceId, data);
10632         }
10633 
10634         /**
10635          * If scroll is triggered to make a certain item visible, this value will return the
10636          * adapter index of that item.
10637          * @return Adapter index of the target item or
10638          * {@link RecyclerView#NO_POSITION} if there is no target
10639          * position.
10640          */
getTargetScrollPosition()10641         public int getTargetScrollPosition() {
10642             return mTargetPosition;
10643         }
10644 
10645         /**
10646          * Returns if current scroll has a target position.
10647          * @return true if scroll is being triggered to make a certain position visible
10648          * @see #getTargetScrollPosition()
10649          */
hasTargetScrollPosition()10650         public boolean hasTargetScrollPosition() {
10651             return mTargetPosition != RecyclerView.NO_POSITION;
10652         }
10653 
10654         /**
10655          * @return true if the structure of the data set has changed since the last call to
10656          *         onLayoutChildren, false otherwise
10657          */
didStructureChange()10658         public boolean didStructureChange() {
10659             return mStructureChanged;
10660         }
10661 
10662         /**
10663          * Returns the total number of items that can be laid out. Note that this number is not
10664          * necessarily equal to the number of items in the adapter, so you should always use this
10665          * number for your position calculations and never access the adapter directly.
10666          * <p>
10667          * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
10668          * data changes on existing Views. These calculations are used to decide which animations
10669          * should be run.
10670          * <p>
10671          * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
10672          * present the correct state to LayoutManager in pre-layout pass.
10673          * <p>
10674          * For example, a newly added item is not included in pre-layout item count because
10675          * pre-layout reflects the contents of the adapter before the item is added. Behind the
10676          * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
10677          * LayoutManager does not know about the new item's existence in pre-layout. The item will
10678          * be available in second layout pass and will be included in the item count. Similar
10679          * adjustments are made for moved and removed items as well.
10680          * <p>
10681          * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
10682          *
10683          * @return The number of items currently available
10684          * @see LayoutManager#getItemCount()
10685          */
getItemCount()10686         public int getItemCount() {
10687             return mInPreLayout ?
10688                     (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout) :
10689                     mItemCount;
10690         }
10691 
10692         @Override
toString()10693         public String toString() {
10694             return "State{" +
10695                     "mTargetPosition=" + mTargetPosition +
10696                     ", mData=" + mData +
10697                     ", mItemCount=" + mItemCount +
10698                     ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount +
10699                     ", mDeletedInvisibleItemCountSincePreviousLayout="
10700                     + mDeletedInvisibleItemCountSincePreviousLayout +
10701                     ", mStructureChanged=" + mStructureChanged +
10702                     ", mInPreLayout=" + mInPreLayout +
10703                     ", mRunSimpleAnimations=" + mRunSimpleAnimations +
10704                     ", mRunPredictiveAnimations=" + mRunPredictiveAnimations +
10705                     '}';
10706         }
10707     }
10708 
10709     /**
10710      * Internal listener that manages items after animations finish. This is how items are
10711      * retained (not recycled) during animations, but allowed to be recycled afterwards.
10712      * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
10713      * method on the animator's listener when it is done animating any item.
10714      */
10715     private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
10716 
10717         @Override
onAnimationFinished(ViewHolder item)10718         public void onAnimationFinished(ViewHolder item) {
10719             item.setIsRecyclable(true);
10720             if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
10721                 item.mShadowedHolder = null;
10722             }
10723             // always null this because an OldViewHolder can never become NewViewHolder w/o being
10724             // recycled.
10725             item.mShadowingHolder = null;
10726             if (!item.shouldBeKeptAsChild()) {
10727                 if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
10728                     removeDetachedView(item.itemView, false);
10729                 }
10730             }
10731         }
10732     }
10733 
10734     /**
10735      * This class defines the animations that take place on items as changes are made
10736      * to the adapter.
10737      *
10738      * Subclasses of ItemAnimator can be used to implement custom animations for actions on
10739      * ViewHolder items. The RecyclerView will manage retaining these items while they
10740      * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
10741      * when a ViewHolder's animation is finished. In other words, there must be a matching
10742      * {@link #dispatchAnimationFinished(ViewHolder)} call for each
10743      * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
10744      * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
10745      * animateChange()}
10746      * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
10747      * and
10748      * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
10749      * animateDisappearance()} call.
10750      *
10751      * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
10752      *
10753      * @see #setItemAnimator(ItemAnimator)
10754      */
10755     @SuppressWarnings("UnusedParameters")
10756     public static abstract class ItemAnimator {
10757 
10758         /**
10759          * The Item represented by this ViewHolder is updated.
10760          * <p>
10761          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
10762          */
10763         public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
10764 
10765         /**
10766          * The Item represented by this ViewHolder is removed from the adapter.
10767          * <p>
10768          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
10769          */
10770         public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
10771 
10772         /**
10773          * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
10774          * represented by this ViewHolder is invalid.
10775          * <p>
10776          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
10777          */
10778         public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
10779 
10780         /**
10781          * The position of the Item represented by this ViewHolder has been changed. This flag is
10782          * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
10783          * any adapter change that may have a side effect on this item. (e.g. The item before this
10784          * one has been removed from the Adapter).
10785          * <p>
10786          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
10787          */
10788         public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
10789 
10790         /**
10791          * This ViewHolder was not laid out but has been added to the layout in pre-layout state
10792          * by the {@link LayoutManager}. This means that the item was already in the Adapter but
10793          * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
10794          * to add new items in pre-layout to specify their virtual location when they are invisible
10795          * (e.g. to specify the item should <i>animate in</i> from below the visible area).
10796          * <p>
10797          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
10798          */
10799         public static final int FLAG_APPEARED_IN_PRE_LAYOUT
10800                 = ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
10801 
10802         /**
10803          * The set of flags that might be passed to
10804          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
10805          */
10806         @IntDef(flag=true, value={
10807                 FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
10808                 FLAG_APPEARED_IN_PRE_LAYOUT
10809         })
10810         @Retention(RetentionPolicy.SOURCE)
10811         public @interface AdapterChanges {}
10812         private ItemAnimatorListener mListener = null;
10813         private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
10814                 new ArrayList<ItemAnimatorFinishedListener>();
10815 
10816         private long mAddDuration = 120;
10817         private long mRemoveDuration = 120;
10818         private long mMoveDuration = 250;
10819         private long mChangeDuration = 250;
10820 
10821         /**
10822          * Gets the current duration for which all move animations will run.
10823          *
10824          * @return The current move duration
10825          */
getMoveDuration()10826         public long getMoveDuration() {
10827             return mMoveDuration;
10828         }
10829 
10830         /**
10831          * Sets the duration for which all move animations will run.
10832          *
10833          * @param moveDuration The move duration
10834          */
setMoveDuration(long moveDuration)10835         public void setMoveDuration(long moveDuration) {
10836             mMoveDuration = moveDuration;
10837         }
10838 
10839         /**
10840          * Gets the current duration for which all add animations will run.
10841          *
10842          * @return The current add duration
10843          */
getAddDuration()10844         public long getAddDuration() {
10845             return mAddDuration;
10846         }
10847 
10848         /**
10849          * Sets the duration for which all add animations will run.
10850          *
10851          * @param addDuration The add duration
10852          */
setAddDuration(long addDuration)10853         public void setAddDuration(long addDuration) {
10854             mAddDuration = addDuration;
10855         }
10856 
10857         /**
10858          * Gets the current duration for which all remove animations will run.
10859          *
10860          * @return The current remove duration
10861          */
getRemoveDuration()10862         public long getRemoveDuration() {
10863             return mRemoveDuration;
10864         }
10865 
10866         /**
10867          * Sets the duration for which all remove animations will run.
10868          *
10869          * @param removeDuration The remove duration
10870          */
setRemoveDuration(long removeDuration)10871         public void setRemoveDuration(long removeDuration) {
10872             mRemoveDuration = removeDuration;
10873         }
10874 
10875         /**
10876          * Gets the current duration for which all change animations will run.
10877          *
10878          * @return The current change duration
10879          */
getChangeDuration()10880         public long getChangeDuration() {
10881             return mChangeDuration;
10882         }
10883 
10884         /**
10885          * Sets the duration for which all change animations will run.
10886          *
10887          * @param changeDuration The change duration
10888          */
setChangeDuration(long changeDuration)10889         public void setChangeDuration(long changeDuration) {
10890             mChangeDuration = changeDuration;
10891         }
10892 
10893         /**
10894          * Internal only:
10895          * Sets the listener that must be called when the animator is finished
10896          * animating the item (or immediately if no animation happens). This is set
10897          * internally and is not intended to be set by external code.
10898          *
10899          * @param listener The listener that must be called.
10900          */
setListener(ItemAnimatorListener listener)10901         void setListener(ItemAnimatorListener listener) {
10902             mListener = listener;
10903         }
10904 
10905         /**
10906          * Called by the RecyclerView before the layout begins. Item animator should record
10907          * necessary information about the View before it is potentially rebound, moved or removed.
10908          * <p>
10909          * The data returned from this method will be passed to the related <code>animate**</code>
10910          * methods.
10911          * <p>
10912          * Note that this method may be called after pre-layout phase if LayoutManager adds new
10913          * Views to the layout in pre-layout pass.
10914          * <p>
10915          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
10916          * the View and the adapter change flags.
10917          *
10918          * @param state       The current State of RecyclerView which includes some useful data
10919          *                    about the layout that will be calculated.
10920          * @param viewHolder  The ViewHolder whose information should be recorded.
10921          * @param changeFlags Additional information about what changes happened in the Adapter
10922          *                    about the Item represented by this ViewHolder. For instance, if
10923          *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
10924          * @param payloads    The payload list that was previously passed to
10925          *                    {@link Adapter#notifyItemChanged(int, Object)} or
10926          *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
10927          *
10928          * @return An ItemHolderInfo instance that preserves necessary information about the
10929          * ViewHolder. This object will be passed back to related <code>animate**</code> methods
10930          * after layout is complete.
10931          *
10932          * @see #recordPostLayoutInformation(State, ViewHolder)
10933          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
10934          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
10935          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
10936          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
10937          */
recordPreLayoutInformation(@onNull State state, @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags, @NonNull List<Object> payloads)10938         public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
10939                 @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
10940                 @NonNull List<Object> payloads) {
10941             return obtainHolderInfo().setFrom(viewHolder);
10942         }
10943 
10944         /**
10945          * Called by the RecyclerView after the layout is complete. Item animator should record
10946          * necessary information about the View's final state.
10947          * <p>
10948          * The data returned from this method will be passed to the related <code>animate**</code>
10949          * methods.
10950          * <p>
10951          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
10952          * the View.
10953          *
10954          * @param state      The current State of RecyclerView which includes some useful data about
10955          *                   the layout that will be calculated.
10956          * @param viewHolder The ViewHolder whose information should be recorded.
10957          *
10958          * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
10959          * This object will be passed back to related <code>animate**</code> methods when
10960          * RecyclerView decides how items should be animated.
10961          *
10962          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
10963          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
10964          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
10965          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
10966          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
10967          */
recordPostLayoutInformation(@onNull State state, @NonNull ViewHolder viewHolder)10968         public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
10969                 @NonNull ViewHolder viewHolder) {
10970             return obtainHolderInfo().setFrom(viewHolder);
10971         }
10972 
10973         /**
10974          * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
10975          * <p>
10976          * This means that the View was a child of the LayoutManager when layout started but has
10977          * been removed by the LayoutManager. It might have been removed from the adapter or simply
10978          * become invisible due to other factors. You can distinguish these two cases by checking
10979          * the change flags that were passed to
10980          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
10981          * <p>
10982          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
10983          * animation callback method which will be called by the RecyclerView depends on the
10984          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
10985          * LayoutManager's decision whether to layout the changed version of a disappearing
10986          * ViewHolder or not. RecyclerView will call
10987          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
10988          * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
10989          * returns {@code false} from
10990          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
10991          * LayoutManager lays out a new disappearing view that holds the updated information.
10992          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
10993          * <p>
10994          * If LayoutManager supports predictive animations, it might provide a target disappear
10995          * location for the View by laying it out in that location. When that happens,
10996          * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
10997          * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
10998          * <p>
10999          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11000          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11001          * decides not to animate the view).
11002          *
11003          * @param viewHolder    The ViewHolder which should be animated
11004          * @param preLayoutInfo The information that was returned from
11005          *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11006          * @param postLayoutInfo The information that was returned from
11007          *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
11008          *                       null if the LayoutManager did not layout the item.
11009          *
11010          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11011          * false otherwise.
11012          */
animateDisappearance(@onNull ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo)11013         public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
11014                 @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
11015 
11016         /**
11017          * Called by the RecyclerView when a ViewHolder is added to the layout.
11018          * <p>
11019          * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
11020          * but has  been added by the LayoutManager. It might be newly added to the adapter or
11021          * simply become visible due to other factors.
11022          * <p>
11023          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11024          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11025          * decides not to animate the view).
11026          *
11027          * @param viewHolder     The ViewHolder which should be animated
11028          * @param preLayoutInfo  The information that was returned from
11029          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11030          *                       Might be null if Item was just added to the adapter or
11031          *                       LayoutManager does not support predictive animations or it could
11032          *                       not predict that this ViewHolder will become visible.
11033          * @param postLayoutInfo The information that was returned from {@link
11034          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11035          *
11036          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11037          * false otherwise.
11038          */
animateAppearance(@onNull ViewHolder viewHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)11039         public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
11040                 @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11041 
11042         /**
11043          * Called by the RecyclerView when a ViewHolder is present in both before and after the
11044          * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
11045          * for it or a {@link Adapter#notifyDataSetChanged()} call.
11046          * <p>
11047          * This ViewHolder still represents the same data that it was representing when the layout
11048          * started but its position / size may be changed by the LayoutManager.
11049          * <p>
11050          * If the Item's layout position didn't change, RecyclerView still calls this method because
11051          * it does not track this information (or does not necessarily know that an animation is
11052          * not required). Your ItemAnimator should handle this case and if there is nothing to
11053          * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
11054          * <code>false</code>.
11055          * <p>
11056          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11057          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11058          * decides not to animate the view).
11059          *
11060          * @param viewHolder     The ViewHolder which should be animated
11061          * @param preLayoutInfo  The information that was returned from
11062          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11063          * @param postLayoutInfo The information that was returned from {@link
11064          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11065          *
11066          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11067          * false otherwise.
11068          */
animatePersistence(@onNull ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)11069         public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
11070                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11071 
11072         /**
11073          * Called by the RecyclerView when an adapter item is present both before and after the
11074          * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
11075          * for it. This method may also be called when
11076          * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
11077          * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
11078          * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
11079          * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
11080          * called for the new ViewHolder and the old one will be recycled.
11081          * <p>
11082          * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
11083          * a good possibility that item contents didn't really change but it is rebound from the
11084          * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
11085          * screen didn't change and your animator should handle this case as well and avoid creating
11086          * unnecessary animations.
11087          * <p>
11088          * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
11089          * previous presentation of the item as-is and supply a new ViewHolder for the updated
11090          * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
11091          * This is useful if you don't know the contents of the Item and would like
11092          * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
11093          * <p>
11094          * When you are writing a custom item animator for your layout, it might be more performant
11095          * and elegant to re-use the same ViewHolder and animate the content changes manually.
11096          * <p>
11097          * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
11098          * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
11099          * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
11100          * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
11101          * which represent the same Item. In that case, only the new ViewHolder is visible
11102          * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
11103          * <p>
11104          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
11105          * ViewHolder when their animation is complete
11106          * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
11107          * animate the view).
11108          * <p>
11109          *  If oldHolder and newHolder are the same instance, you should call
11110          * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
11111          * <p>
11112          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
11113          * animation callback method which will be called by the RecyclerView depends on the
11114          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
11115          * LayoutManager's decision whether to layout the changed version of a disappearing
11116          * ViewHolder or not. RecyclerView will call
11117          * {@code animateChange} instead of
11118          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11119          * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
11120          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
11121          * LayoutManager lays out a new disappearing view that holds the updated information.
11122          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
11123          *
11124          * @param oldHolder     The ViewHolder before the layout is started, might be the same
11125          *                      instance with newHolder.
11126          * @param newHolder     The ViewHolder after the layout is finished, might be the same
11127          *                      instance with oldHolder.
11128          * @param preLayoutInfo  The information that was returned from
11129          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11130          * @param postLayoutInfo The information that was returned from {@link
11131          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11132          *
11133          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11134          * false otherwise.
11135          */
animateChange(@onNull ViewHolder oldHolder, @NonNull ViewHolder newHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)11136         public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
11137                 @NonNull ViewHolder newHolder,
11138                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11139 
buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder)11140         @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
11141             int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
11142             if (viewHolder.isInvalid()) {
11143                 return FLAG_INVALIDATED;
11144             }
11145             if ((flags & FLAG_INVALIDATED) == 0) {
11146                 final int oldPos = viewHolder.getOldPosition();
11147                 final int pos = viewHolder.getAdapterPosition();
11148                 if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos){
11149                     flags |= FLAG_MOVED;
11150                 }
11151             }
11152             return flags;
11153         }
11154 
11155         /**
11156          * Called when there are pending animations waiting to be started. This state
11157          * is governed by the return values from
11158          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11159          * animateAppearance()},
11160          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11161          * animateChange()}
11162          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11163          * animatePersistence()}, and
11164          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11165          * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
11166          * called later to start the associated animations. runPendingAnimations() will be scheduled
11167          * to be run on the next frame.
11168          */
runPendingAnimations()11169         abstract public void runPendingAnimations();
11170 
11171         /**
11172          * Method called when an animation on a view should be ended immediately.
11173          * This could happen when other events, like scrolling, occur, so that
11174          * animating views can be quickly put into their proper end locations.
11175          * Implementations should ensure that any animations running on the item
11176          * are canceled and affected properties are set to their end values.
11177          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
11178          * animation since the animations are effectively done when this method is called.
11179          *
11180          * @param item The item for which an animation should be stopped.
11181          */
endAnimation(ViewHolder item)11182         abstract public void endAnimation(ViewHolder item);
11183 
11184         /**
11185          * Method called when all item animations should be ended immediately.
11186          * This could happen when other events, like scrolling, occur, so that
11187          * animating views can be quickly put into their proper end locations.
11188          * Implementations should ensure that any animations running on any items
11189          * are canceled and affected properties are set to their end values.
11190          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
11191          * animation since the animations are effectively done when this method is called.
11192          */
endAnimations()11193         abstract public void endAnimations();
11194 
11195         /**
11196          * Method which returns whether there are any item animations currently running.
11197          * This method can be used to determine whether to delay other actions until
11198          * animations end.
11199          *
11200          * @return true if there are any item animations currently running, false otherwise.
11201          */
isRunning()11202         abstract public boolean isRunning();
11203 
11204         /**
11205          * Method to be called by subclasses when an animation is finished.
11206          * <p>
11207          * For each call RecyclerView makes to
11208          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11209          * animateAppearance()},
11210          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11211          * animatePersistence()}, or
11212          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11213          * animateDisappearance()}, there
11214          * should
11215          * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
11216          * <p>
11217          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11218          * animateChange()}, sublcass should call this method for both the <code>oldHolder</code>
11219          * and <code>newHolder</code>  (if they are not the same instance).
11220          *
11221          * @param viewHolder The ViewHolder whose animation is finished.
11222          * @see #onAnimationFinished(ViewHolder)
11223          */
dispatchAnimationFinished(ViewHolder viewHolder)11224         public final void dispatchAnimationFinished(ViewHolder viewHolder) {
11225             onAnimationFinished(viewHolder);
11226             if (mListener != null) {
11227                 mListener.onAnimationFinished(viewHolder);
11228             }
11229         }
11230 
11231         /**
11232          * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
11233          * ItemAniamtor.
11234          *
11235          * @param viewHolder The ViewHolder whose animation is finished. There might still be other
11236          *                   animations running on this ViewHolder.
11237          * @see #dispatchAnimationFinished(ViewHolder)
11238          */
onAnimationFinished(ViewHolder viewHolder)11239         public void onAnimationFinished(ViewHolder viewHolder) {
11240         }
11241 
11242         /**
11243          * Method to be called by subclasses when an animation is started.
11244          * <p>
11245          * For each call RecyclerView makes to
11246          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11247          * animateAppearance()},
11248          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11249          * animatePersistence()}, or
11250          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11251          * animateDisappearance()}, there should be a matching
11252          * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
11253          * <p>
11254          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11255          * animateChange()}, sublcass should call this method for both the <code>oldHolder</code>
11256          * and <code>newHolder</code> (if they are not the same instance).
11257          * <p>
11258          * If your ItemAnimator decides not to animate a ViewHolder, it should call
11259          * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
11260          * {@link #dispatchAnimationStarted(ViewHolder)}.
11261          *
11262          * @param viewHolder The ViewHolder whose animation is starting.
11263          * @see #onAnimationStarted(ViewHolder)
11264          */
dispatchAnimationStarted(ViewHolder viewHolder)11265         public final void dispatchAnimationStarted(ViewHolder viewHolder) {
11266             onAnimationStarted(viewHolder);
11267         }
11268 
11269         /**
11270          * Called when a new animation is started on the given ViewHolder.
11271          *
11272          * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
11273          *                   might already be animating and this might be another animation.
11274          * @see #dispatchAnimationStarted(ViewHolder)
11275          */
onAnimationStarted(ViewHolder viewHolder)11276         public void onAnimationStarted(ViewHolder viewHolder) {
11277 
11278         }
11279 
11280         /**
11281          * Like {@link #isRunning()}, this method returns whether there are any item
11282          * animations currently running. Addtionally, the listener passed in will be called
11283          * when there are no item animations running, either immediately (before the method
11284          * returns) if no animations are currently running, or when the currently running
11285          * animations are {@link #dispatchAnimationsFinished() finished}.
11286          *
11287          * <p>Note that the listener is transient - it is either called immediately and not
11288          * stored at all, or stored only until it is called when running animations
11289          * are finished sometime later.</p>
11290          *
11291          * @param listener A listener to be called immediately if no animations are running
11292          * or later when currently-running animations have finished. A null listener is
11293          * equivalent to calling {@link #isRunning()}.
11294          * @return true if there are any item animations currently running, false otherwise.
11295          */
isRunning(ItemAnimatorFinishedListener listener)11296         public final boolean isRunning(ItemAnimatorFinishedListener listener) {
11297             boolean running = isRunning();
11298             if (listener != null) {
11299                 if (!running) {
11300                     listener.onAnimationsFinished();
11301                 } else {
11302                     mFinishedListeners.add(listener);
11303                 }
11304             }
11305             return running;
11306         }
11307 
11308         /**
11309          * When an item is changed, ItemAnimator can decide whether it wants to re-use
11310          * the same ViewHolder for animations or RecyclerView should create a copy of the
11311          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
11312          * <p>
11313          * Note that this method will only be called if the {@link ViewHolder} still has the same
11314          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
11315          * both {@link ViewHolder}s in the
11316          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
11317          * <p>
11318          * If your application is using change payloads, you can override
11319          * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
11320          *
11321          * @param viewHolder The ViewHolder which represents the changed item's old content.
11322          *
11323          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
11324          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
11325          *         ItemAnimator to animate. Default implementation returns <code>true</code>.
11326          *
11327          * @see #canReuseUpdatedViewHolder(ViewHolder, List)
11328          */
canReuseUpdatedViewHolder(@onNull ViewHolder viewHolder)11329         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
11330             return true;
11331         }
11332 
11333         /**
11334          * When an item is changed, ItemAnimator can decide whether it wants to re-use
11335          * the same ViewHolder for animations or RecyclerView should create a copy of the
11336          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
11337          * <p>
11338          * Note that this method will only be called if the {@link ViewHolder} still has the same
11339          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
11340          * both {@link ViewHolder}s in the
11341          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
11342          *
11343          * @param viewHolder The ViewHolder which represents the changed item's old content.
11344          * @param payloads A non-null list of merged payloads that were sent with change
11345          *                 notifications. Can be empty if the adapter is invalidated via
11346          *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
11347          *                 payloads will be passed into
11348          *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
11349          *                 method <b>if</b> this method returns <code>true</code>.
11350          *
11351          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
11352          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
11353          *         ItemAnimator to animate. Default implementation calls
11354          *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
11355          *
11356          * @see #canReuseUpdatedViewHolder(ViewHolder)
11357          */
canReuseUpdatedViewHolder(@onNull ViewHolder viewHolder, @NonNull List<Object> payloads)11358         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
11359                 @NonNull List<Object> payloads) {
11360             return canReuseUpdatedViewHolder(viewHolder);
11361         }
11362 
11363         /**
11364          * This method should be called by ItemAnimator implementations to notify
11365          * any listeners that all pending and active item animations are finished.
11366          */
dispatchAnimationsFinished()11367         public final void dispatchAnimationsFinished() {
11368             final int count = mFinishedListeners.size();
11369             for (int i = 0; i < count; ++i) {
11370                 mFinishedListeners.get(i).onAnimationsFinished();
11371             }
11372             mFinishedListeners.clear();
11373         }
11374 
11375         /**
11376          * Returns a new {@link ItemHolderInfo} which will be used to store information about the
11377          * ViewHolder. This information will later be passed into <code>animate**</code> methods.
11378          * <p>
11379          * You can override this method if you want to extend {@link ItemHolderInfo} and provide
11380          * your own instances.
11381          *
11382          * @return A new {@link ItemHolderInfo}.
11383          */
obtainHolderInfo()11384         public ItemHolderInfo obtainHolderInfo() {
11385             return new ItemHolderInfo();
11386         }
11387 
11388         /**
11389          * The interface to be implemented by listeners to animation events from this
11390          * ItemAnimator. This is used internally and is not intended for developers to
11391          * create directly.
11392          */
11393         interface ItemAnimatorListener {
onAnimationFinished(ViewHolder item)11394             void onAnimationFinished(ViewHolder item);
11395         }
11396 
11397         /**
11398          * This interface is used to inform listeners when all pending or running animations
11399          * in an ItemAnimator are finished. This can be used, for example, to delay an action
11400          * in a data set until currently-running animations are complete.
11401          *
11402          * @see #isRunning(ItemAnimatorFinishedListener)
11403          */
11404         public interface ItemAnimatorFinishedListener {
onAnimationsFinished()11405             void onAnimationsFinished();
11406         }
11407 
11408         /**
11409          * A simple data structure that holds information about an item's bounds.
11410          * This information is used in calculating item animations. Default implementation of
11411          * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
11412          * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
11413          * structure. You can extend this class if you would like to keep more information about
11414          * the Views.
11415          * <p>
11416          * If you want to provide your own implementation butstill use `super` methods to record
11417          * basic information, you can override {@link #obtainHolderInfo()} to provide your own
11418          * instances.
11419          */
11420         public static class ItemHolderInfo {
11421 
11422             /**
11423              * The left edge of the View (excluding decorations)
11424              */
11425             public int left;
11426 
11427             /**
11428              * The top edge of the View (excluding decorations)
11429              */
11430             public int top;
11431 
11432             /**
11433              * The right edge of the View (excluding decorations)
11434              */
11435             public int right;
11436 
11437             /**
11438              * The bottom edge of the View (excluding decorations)
11439              */
11440             public int bottom;
11441 
11442             /**
11443              * The change flags that were passed to
11444              * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
11445              */
11446             @AdapterChanges
11447             public int changeFlags;
11448 
ItemHolderInfo()11449             public ItemHolderInfo() {
11450             }
11451 
11452             /**
11453              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
11454              * the given ViewHolder. Clears all {@link #changeFlags}.
11455              *
11456              * @param holder The ViewHolder whose bounds should be copied.
11457              * @return This {@link ItemHolderInfo}
11458              */
setFrom(RecyclerView.ViewHolder holder)11459             public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder) {
11460                 return setFrom(holder, 0);
11461             }
11462 
11463             /**
11464              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
11465              * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
11466              *
11467              * @param holder The ViewHolder whose bounds should be copied.
11468              * @param flags  The adapter change flags that were passed into
11469              *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
11470              *               List)}.
11471              * @return This {@link ItemHolderInfo}
11472              */
setFrom(RecyclerView.ViewHolder holder, @AdapterChanges int flags)11473             public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder,
11474                     @AdapterChanges int flags) {
11475                 final View view = holder.itemView;
11476                 this.left = view.getLeft();
11477                 this.top = view.getTop();
11478                 this.right = view.getRight();
11479                 this.bottom = view.getBottom();
11480                 return this;
11481             }
11482         }
11483     }
11484 
11485     @Override
getChildDrawingOrder(int childCount, int i)11486     protected int getChildDrawingOrder(int childCount, int i) {
11487         if (mChildDrawingOrderCallback == null) {
11488             return super.getChildDrawingOrder(childCount, i);
11489         } else {
11490             return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
11491         }
11492     }
11493 
11494     /**
11495      * A callback interface that can be used to alter the drawing order of RecyclerView children.
11496      * <p>
11497      * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
11498      * that applies to that method also applies to this callback. For example, changing the drawing
11499      * order of two views will not have any effect if their elevation values are different since
11500      * elevation overrides the result of this callback.
11501      */
11502     public interface ChildDrawingOrderCallback {
11503         /**
11504          * Returns the index of the child to draw for this iteration. Override this
11505          * if you want to change the drawing order of children. By default, it
11506          * returns i.
11507          *
11508          * @param i The current iteration.
11509          * @return The index of the child to draw this iteration.
11510          *
11511          * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
11512          */
onGetChildDrawingOrder(int childCount, int i)11513         int onGetChildDrawingOrder(int childCount, int i);
11514     }
11515 
getScrollingChildHelper()11516     private NestedScrollingChildHelper getScrollingChildHelper() {
11517         if (mScrollingChildHelper == null) {
11518             mScrollingChildHelper = new NestedScrollingChildHelper(this);
11519         }
11520         return mScrollingChildHelper;
11521     }
11522 }
11523