• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.accessibility.AccessibilityEventCompat;
50 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
51 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
52 import android.support.v4.widget.EdgeEffectCompat;
53 import android.support.v4.widget.ScrollerCompat;
54 import android.support.v7.recyclerview.R;
55 import android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
56 import android.util.AttributeSet;
57 import android.util.Log;
58 import android.util.SparseArray;
59 import android.util.SparseIntArray;
60 import android.util.TypedValue;
61 import android.view.FocusFinder;
62 import android.view.MotionEvent;
63 import android.view.VelocityTracker;
64 import android.view.View;
65 import android.view.ViewConfiguration;
66 import android.view.ViewGroup;
67 import android.view.ViewParent;
68 import android.view.accessibility.AccessibilityEvent;
69 import android.view.accessibility.AccessibilityManager;
70 import android.view.animation.Interpolator;
71 
72 import java.lang.annotation.Retention;
73 import java.lang.annotation.RetentionPolicy;
74 import java.lang.reflect.Constructor;
75 import java.lang.reflect.InvocationTargetException;
76 import java.util.ArrayList;
77 import java.util.Collections;
78 import java.util.List;
79 
80 import static android.support.v7.widget.AdapterHelper.Callback;
81 import static android.support.v7.widget.AdapterHelper.UpdateOp;
82 
83 /**
84  * A flexible view for providing a limited window into a large data set.
85  *
86  * <h3>Glossary of terms:</h3>
87  *
88  * <ul>
89  *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
90  *     that represent items in a data set.</li>
91  *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
92  *     <li><em>Index:</em> The index of an attached child view as used in a call to
93  *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
94  *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
95  *     to a <em>position</em> within the adapter.</li>
96  *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
97  *     position may be placed in a cache for later reuse to display the same type of data again
98  *     later. This can drastically improve performance by skipping initial layout inflation
99  *     or construction.</li>
100  *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
101  *     state during layout. Scrap views may be reused without becoming fully detached
102  *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
103  *     by the adapter if the view was considered <em>dirty</em>.</li>
104  *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
105  *     being displayed.</li>
106  * </ul>
107  *
108  * <h4>Positions in RecyclerView:</h4>
109  * <p>
110  * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
111  * {@link LayoutManager} to be able to detect data set changes in batches during a layout
112  * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
113  * It also helps with performance because all view bindings happen at the same time and unnecessary
114  * bindings are avoided.
115  * <p>
116  * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
117  * <ul>
118  *     <li>layout position: Position of an item in the latest layout calculation. This is the
119  *     position from the LayoutManager's perspective.</li>
120  *     <li>adapter position: Position of an item in the adapter. This is the position from
121  *     the Adapter's perspective.</li>
122  * </ul>
123  * <p>
124  * These two positions are the same except the time between dispatching <code>adapter.notify*
125  * </code> events and calculating the updated layout.
126  * <p>
127  * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
128  * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
129  * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
130  * last layout calculation. You can rely on these positions to be consistent with what user is
131  * currently seeing on the screen. For example, if you have a list of items on the screen and user
132  * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
133  * is seeing.
134  * <p>
135  * The other set of position related methods are in the form of
136  * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
137  * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
138  * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
139  * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
140  * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
141  * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
142  * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
143  * <code>null</code> results from these methods.
144  * <p>
145  * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
146  * writing an {@link Adapter}, you probably want to use adapter positions.
147  *
148  * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_layoutManager
149  */
150 public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
151 
152     private static final String TAG = "RecyclerView";
153 
154     private static final boolean DEBUG = false;
155 
156     private static final int[]  NESTED_SCROLLING_ATTRS
157             = {16843830 /* android.R.attr.nestedScrollingEnabled */};
158 
159     private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
160 
161     /**
162      * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
163      * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
164      * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
165      * recursively traverses itemView and invalidates display list for each ViewGroup that matches
166      * this criteria.
167      */
168     private static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
169             || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
170     /**
171      * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
172      * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
173      * 0 when mode is unspecified.
174      */
175     static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
176 
177     static final boolean DISPATCH_TEMP_DETACH = false;
178     public static final int HORIZONTAL = 0;
179     public static final int VERTICAL = 1;
180 
181     public static final int NO_POSITION = -1;
182     public static final long NO_ID = -1;
183     public static final int INVALID_TYPE = -1;
184 
185     /**
186      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
187      * that the RecyclerView should use the standard touch slop for smooth,
188      * continuous scrolling.
189      */
190     public static final int TOUCH_SLOP_DEFAULT = 0;
191 
192     /**
193      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
194      * that the RecyclerView should use the standard touch slop for scrolling
195      * widgets that snap to a page or other coarse-grained barrier.
196      */
197     public static final int TOUCH_SLOP_PAGING = 1;
198 
199     private static final int MAX_SCROLL_DURATION = 2000;
200 
201     /**
202      * RecyclerView is calculating a scroll.
203      * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
204      * it. Try to avoid using EditText, focusable views or handle them with care.
205      */
206     private static final String TRACE_SCROLL_TAG = "RV Scroll";
207 
208     /**
209      * OnLayout has been called by the View system.
210      * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
211      * update themselves directly. This will cause a full re-layout but when it happens via the
212      * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
213      */
214     private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
215 
216     /**
217      * NotifyDataSetChanged or equal has been called.
218      * If this is taking a long time, try sending granular notify adapter changes instead of just
219      * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
220      * might help.
221      */
222     private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
223 
224     /**
225      * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
226      * If this is taking a long time, you may have dispatched too many Adapter updates causing too
227      * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
228      * methods.
229      */
230     private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
231 
232     /**
233      * RecyclerView is rebinding a View.
234      * If this is taking a lot of time, consider optimizing your layout or make sure you are not
235      * doing extra operations in onBindViewHolder call.
236      */
237     private static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
238 
239     /**
240      * RecyclerView is creating a new View.
241      * If too many of these present in Systrace:
242      * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
243      * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
244      * > Adapter#onFailedToRecycleView(ViewHolder)})
245      *
246      * - There might be too many item view types.
247      * > Try merging them
248      *
249      * - There might be too many itemChange animations and not enough space in RecyclerPool.
250      * >Try increasing your pool size and item cache size.
251      */
252     private static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
253     private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
254             new Class[]{Context.class, AttributeSet.class, int.class, int.class};
255 
256     private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
257 
258     final Recycler mRecycler = new Recycler();
259 
260     private SavedState mPendingSavedState;
261 
262     /**
263      * Handles adapter updates
264      */
265     AdapterHelper mAdapterHelper;
266 
267     /**
268      * Handles abstraction between LayoutManager children and RecyclerView children
269      */
270     ChildHelper mChildHelper;
271 
272     /**
273      * Keeps data about views to be used for animations
274      */
275     final ViewInfoStore mViewInfoStore = new ViewInfoStore();
276 
277     /**
278      * Prior to L, there is no way to query this variable which is why we override the setter and
279      * track it here.
280      */
281     private boolean mClipToPadding;
282 
283     /**
284      * Note: this Runnable is only ever posted if:
285      * 1) We've been through first layout
286      * 2) We know we have a fixed size (mHasFixedSize)
287      * 3) We're attached
288      */
289     private final Runnable mUpdateChildViewsRunnable = new Runnable() {
290         @Override
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     /**
354      * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
355      * (for API compatibility).
356      * <p>
357      * It is a bad practice for a developer to update the data in a scroll callback since it is
358      * potentially called during a layout.
359      */
360     private int mDispatchScrollCounter = 0;
361 
362     private EdgeEffectCompat mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
363 
364     ItemAnimator mItemAnimator = new DefaultItemAnimator();
365 
366     private static final int INVALID_POINTER = -1;
367 
368     /**
369      * The RecyclerView is not currently scrolling.
370      * @see #getScrollState()
371      */
372     public static final int SCROLL_STATE_IDLE = 0;
373 
374     /**
375      * The RecyclerView is currently being dragged by outside input such as user touch input.
376      * @see #getScrollState()
377      */
378     public static final int SCROLL_STATE_DRAGGING = 1;
379 
380     /**
381      * The RecyclerView is currently animating to a final position while not under
382      * outside control.
383      * @see #getScrollState()
384      */
385     public static final int SCROLL_STATE_SETTLING = 2;
386 
387     // Touch/scrolling handling
388 
389     private int mScrollState = SCROLL_STATE_IDLE;
390     private int mScrollPointerId = INVALID_POINTER;
391     private VelocityTracker mVelocityTracker;
392     private int mInitialTouchX;
393     private int mInitialTouchY;
394     private int mLastTouchX;
395     private int mLastTouchY;
396     private int mTouchSlop;
397     private OnFlingListener mOnFlingListener;
398     private final int mMinFlingVelocity;
399     private final int mMaxFlingVelocity;
400     // This value is used when handling generic motion events.
401     private float mScrollFactor = Float.MIN_VALUE;
402     private boolean mPreserveFocusAfterLayout = true;
403 
404     private final ViewFlinger mViewFlinger = new ViewFlinger();
405 
406     final State mState = new State();
407 
408     private OnScrollListener mScrollListener;
409     private List<OnScrollListener> mScrollListeners;
410 
411     // For use in item animations
412     boolean mItemsAddedOrRemoved = false;
413     boolean mItemsChanged = false;
414     private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
415             new ItemAnimatorRestoreListener();
416     private boolean mPostedAnimatorRunner = false;
417     private RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
418     private ChildDrawingOrderCallback mChildDrawingOrderCallback;
419 
420     // simple array to keep min and max child position during a layout calculation
421     // preserved not to create a new one in each layout pass
422     private final int[] mMinMaxLayoutPositions = new int[2];
423 
424     private NestedScrollingChildHelper mScrollingChildHelper;
425     private final int[] mScrollOffset = new int[2];
426     private final int[] mScrollConsumed = new int[2];
427     private final int[] mNestedOffsets = new int[2];
428 
429     private Runnable mItemAnimatorRunner = new Runnable() {
430         @Override
431         public void run() {
432             if (mItemAnimator != null) {
433                 mItemAnimator.runPendingAnimations();
434             }
435             mPostedAnimatorRunner = false;
436         }
437     };
438 
439     private static final Interpolator sQuinticInterpolator = new Interpolator() {
440         @Override
441         public float getInterpolation(float t) {
442             t -= 1.0f;
443             return t * t * t * t * t + 1.0f;
444         }
445     };
446 
447     /**
448      * The callback to convert view info diffs into animations.
449      */
450     private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
451             new ViewInfoStore.ProcessCallback() {
452         @Override
453         public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
454                 @Nullable ItemHolderInfo postInfo) {
455             mRecycler.unscrapView(viewHolder);
456             animateDisappearance(viewHolder, info, postInfo);
457         }
458         @Override
459         public void processAppeared(ViewHolder viewHolder,
460                 ItemHolderInfo preInfo, ItemHolderInfo info) {
461             animateAppearance(viewHolder, preInfo, info);
462         }
463 
464         @Override
465         public void processPersistent(ViewHolder viewHolder,
466                 @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
467             viewHolder.setIsRecyclable(false);
468             if (mDataSetHasChangedAfterLayout) {
469                 // since it was rebound, use change instead as we'll be mapping them from
470                 // stable ids. If stable ids were false, we would not be running any
471                 // animations
472                 if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo, postInfo)) {
473                     postAnimationRunner();
474                 }
475             } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
476                 postAnimationRunner();
477             }
478         }
479         @Override
480         public void unused(ViewHolder viewHolder) {
481             mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
482         }
483     };
484 
RecyclerView(Context context)485     public RecyclerView(Context context) {
486         this(context, null);
487     }
488 
RecyclerView(Context context, @Nullable AttributeSet attrs)489     public RecyclerView(Context context, @Nullable AttributeSet attrs) {
490         this(context, attrs, 0);
491     }
492 
RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle)493     public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
494         super(context, attrs, defStyle);
495         if (attrs != null) {
496             TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
497             mClipToPadding = a.getBoolean(0, true);
498             a.recycle();
499         } else {
500             mClipToPadding = true;
501         }
502         setScrollContainer(true);
503         setFocusableInTouchMode(true);
504         final int version = Build.VERSION.SDK_INT;
505         mPostUpdatesOnAnimation = version >= 16;
506 
507         final ViewConfiguration vc = ViewConfiguration.get(context);
508         mTouchSlop = vc.getScaledTouchSlop();
509         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
510         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
511         setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
512 
513         mItemAnimator.setListener(mItemAnimatorListener);
514         initAdapterManager();
515         initChildrenHelper();
516         // If not explicitly specified this view is important for accessibility.
517         if (ViewCompat.getImportantForAccessibility(this)
518                 == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
519             ViewCompat.setImportantForAccessibility(this,
520                     ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
521         }
522         mAccessibilityManager = (AccessibilityManager) getContext()
523                 .getSystemService(Context.ACCESSIBILITY_SERVICE);
524         setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
525         // Create the layoutManager if specified.
526 
527         boolean nestedScrollingEnabled = true;
528 
529         if (attrs != null) {
530             int defStyleRes = 0;
531             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
532                     defStyle, defStyleRes);
533             String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
534             int descendantFocusability = a.getInt(
535                     R.styleable.RecyclerView_android_descendantFocusability, -1);
536             if (descendantFocusability == -1) {
537                 setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
538             }
539             a.recycle();
540             createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
541 
542             if (Build.VERSION.SDK_INT >= 21) {
543                 a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
544                         defStyle, defStyleRes);
545                 nestedScrollingEnabled = a.getBoolean(0, true);
546                 a.recycle();
547             }
548         } else {
549             setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
550         }
551 
552         // Re-set whether nested scrolling is enabled so that it is set on all API levels
553         setNestedScrollingEnabled(nestedScrollingEnabled);
554     }
555 
556     /**
557      * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
558      * @return An instance of AccessibilityDelegateCompat used by RecyclerView
559      */
getCompatAccessibilityDelegate()560     public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
561         return mAccessibilityDelegate;
562     }
563 
564     /**
565      * Sets the accessibility delegate compatibility implementation used by RecyclerView.
566      * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
567      */
setAccessibilityDelegateCompat( RecyclerViewAccessibilityDelegate accessibilityDelegate)568     public void setAccessibilityDelegateCompat(
569             RecyclerViewAccessibilityDelegate accessibilityDelegate) {
570         mAccessibilityDelegate = accessibilityDelegate;
571         ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
572     }
573 
574     /**
575      * Instantiate and set a LayoutManager, if specified in the attributes.
576      */
createLayoutManager(Context context, String className, AttributeSet attrs, int defStyleAttr, int defStyleRes)577     private void createLayoutManager(Context context, String className, AttributeSet attrs,
578             int defStyleAttr, int defStyleRes) {
579         if (className != null) {
580             className = className.trim();
581             if (className.length() != 0) {  // Can't use isEmpty since it was added in API 9.
582                 className = getFullClassName(context, className);
583                 try {
584                     ClassLoader classLoader;
585                     if (isInEditMode()) {
586                         // Stupid layoutlib cannot handle simple class loaders.
587                         classLoader = this.getClass().getClassLoader();
588                     } else {
589                         classLoader = context.getClassLoader();
590                     }
591                     Class<? extends LayoutManager> layoutManagerClass =
592                             classLoader.loadClass(className).asSubclass(LayoutManager.class);
593                     Constructor<? extends LayoutManager> constructor;
594                     Object[] constructorArgs = null;
595                     try {
596                         constructor = layoutManagerClass
597                                 .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
598                         constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
599                     } catch (NoSuchMethodException e) {
600                         try {
601                             constructor = layoutManagerClass.getConstructor();
602                         } catch (NoSuchMethodException e1) {
603                             e1.initCause(e);
604                             throw new IllegalStateException(attrs.getPositionDescription() +
605                                     ": Error creating LayoutManager " + className, e1);
606                         }
607                     }
608                     constructor.setAccessible(true);
609                     setLayoutManager(constructor.newInstance(constructorArgs));
610                 } catch (ClassNotFoundException e) {
611                     throw new IllegalStateException(attrs.getPositionDescription()
612                             + ": Unable to find LayoutManager " + className, e);
613                 } catch (InvocationTargetException e) {
614                     throw new IllegalStateException(attrs.getPositionDescription()
615                             + ": Could not instantiate the LayoutManager: " + className, e);
616                 } catch (InstantiationException e) {
617                     throw new IllegalStateException(attrs.getPositionDescription()
618                             + ": Could not instantiate the LayoutManager: " + className, e);
619                 } catch (IllegalAccessException e) {
620                     throw new IllegalStateException(attrs.getPositionDescription()
621                             + ": Cannot access non-public constructor " + className, e);
622                 } catch (ClassCastException e) {
623                     throw new IllegalStateException(attrs.getPositionDescription()
624                             + ": Class is not a LayoutManager " + className, e);
625                 }
626             }
627         }
628     }
629 
getFullClassName(Context context, String className)630     private String getFullClassName(Context context, String className) {
631         if (className.charAt(0) == '.') {
632             return context.getPackageName() + className;
633         }
634         if (className.contains(".")) {
635             return className;
636         }
637         return RecyclerView.class.getPackage().getName() + '.' + className;
638     }
639 
initChildrenHelper()640     private void initChildrenHelper() {
641         mChildHelper = new ChildHelper(new ChildHelper.Callback() {
642             @Override
643             public int getChildCount() {
644                 return RecyclerView.this.getChildCount();
645             }
646 
647             @Override
648             public void addView(View child, int index) {
649                 RecyclerView.this.addView(child, index);
650                 dispatchChildAttached(child);
651             }
652 
653             @Override
654             public int indexOfChild(View view) {
655                 return RecyclerView.this.indexOfChild(view);
656             }
657 
658             @Override
659             public void removeViewAt(int index) {
660                 final View child = RecyclerView.this.getChildAt(index);
661                 if (child != null) {
662                     dispatchChildDetached(child);
663                 }
664                 RecyclerView.this.removeViewAt(index);
665             }
666 
667             @Override
668             public View getChildAt(int offset) {
669                 return RecyclerView.this.getChildAt(offset);
670             }
671 
672             @Override
673             public void removeAllViews() {
674                 final int count = getChildCount();
675                 for (int i = 0; i < count; i ++) {
676                     dispatchChildDetached(getChildAt(i));
677                 }
678                 RecyclerView.this.removeAllViews();
679             }
680 
681             @Override
682             public ViewHolder getChildViewHolder(View view) {
683                 return getChildViewHolderInt(view);
684             }
685 
686             @Override
687             public void attachViewToParent(View child, int index,
688                     ViewGroup.LayoutParams layoutParams) {
689                 final ViewHolder vh = getChildViewHolderInt(child);
690                 if (vh != null) {
691                     if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
692                         throw new IllegalArgumentException("Called attach on a child which is not"
693                                 + " detached: " + vh);
694                     }
695                     if (DEBUG) {
696                         Log.d(TAG, "reAttach " + vh);
697                     }
698                     vh.clearTmpDetachFlag();
699                 }
700                 RecyclerView.this.attachViewToParent(child, index, layoutParams);
701             }
702 
703             @Override
704             public void detachViewFromParent(int offset) {
705                 final View view = getChildAt(offset);
706                 if (view != null) {
707                     final ViewHolder vh = getChildViewHolderInt(view);
708                     if (vh != null) {
709                         if (vh.isTmpDetached() && !vh.shouldIgnore()) {
710                             throw new IllegalArgumentException("called detach on an already"
711                                     + " detached child " + vh);
712                         }
713                         if (DEBUG) {
714                             Log.d(TAG, "tmpDetach " + vh);
715                         }
716                         vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
717                     }
718                 }
719                 RecyclerView.this.detachViewFromParent(offset);
720             }
721 
722             @Override
723             public void onEnteredHiddenState(View child) {
724                 final ViewHolder vh = getChildViewHolderInt(child);
725                 if (vh != null) {
726                     vh.onEnteredHiddenState();
727                 }
728             }
729 
730             @Override
731             public void onLeftHiddenState(View child) {
732                 final ViewHolder vh = getChildViewHolderInt(child);
733                 if (vh != null) {
734                     vh.onLeftHiddenState();
735                 }
736             }
737         });
738     }
739 
initAdapterManager()740     void initAdapterManager() {
741         mAdapterHelper = new AdapterHelper(new Callback() {
742             @Override
743             public ViewHolder findViewHolder(int position) {
744                 final ViewHolder vh = findViewHolderForPosition(position, true);
745                 if (vh == null) {
746                     return null;
747                 }
748                 // ensure it is not hidden because for adapter helper, the only thing matter is that
749                 // LM thinks view is a child.
750                 if (mChildHelper.isHidden(vh.itemView)) {
751                     if (DEBUG) {
752                         Log.d(TAG, "assuming view holder cannot be find because it is hidden");
753                     }
754                     return null;
755                 }
756                 return vh;
757             }
758 
759             @Override
760             public void offsetPositionsForRemovingInvisible(int start, int count) {
761                 offsetPositionRecordsForRemove(start, count, true);
762                 mItemsAddedOrRemoved = true;
763                 mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
764             }
765 
766             @Override
767             public void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount) {
768                 offsetPositionRecordsForRemove(positionStart, itemCount, false);
769                 mItemsAddedOrRemoved = true;
770             }
771 
772             @Override
773             public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
774                 viewRangeUpdate(positionStart, itemCount, payload);
775                 mItemsChanged = true;
776             }
777 
778             @Override
779             public void onDispatchFirstPass(UpdateOp op) {
780                 dispatchUpdate(op);
781             }
782 
783             void dispatchUpdate(UpdateOp op) {
784                 switch (op.cmd) {
785                     case UpdateOp.ADD:
786                         mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
787                         break;
788                     case UpdateOp.REMOVE:
789                         mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
790                         break;
791                     case UpdateOp.UPDATE:
792                         mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
793                                 op.payload);
794                         break;
795                     case UpdateOp.MOVE:
796                         mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
797                         break;
798                 }
799             }
800 
801             @Override
802             public void onDispatchSecondPass(UpdateOp op) {
803                 dispatchUpdate(op);
804             }
805 
806             @Override
807             public void offsetPositionsForAdd(int positionStart, int itemCount) {
808                 offsetPositionRecordsForInsert(positionStart, itemCount);
809                 mItemsAddedOrRemoved = true;
810             }
811 
812             @Override
813             public void offsetPositionsForMove(int from, int to) {
814                 offsetPositionRecordsForMove(from, to);
815                 // should we create mItemsMoved ?
816                 mItemsAddedOrRemoved = true;
817             }
818         });
819     }
820 
821     /**
822      * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
823      * size is not affected by the adapter contents. RecyclerView can still change its size based
824      * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
825      * size of its children or contents of its adapter (except the number of items in the adapter).
826      * <p>
827      * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
828      * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
829      *
830      * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
831      */
setHasFixedSize(boolean hasFixedSize)832     public void setHasFixedSize(boolean hasFixedSize) {
833         mHasFixedSize = hasFixedSize;
834     }
835 
836     /**
837      * @return true if the app has specified that changes in adapter content cannot change
838      * the size of the RecyclerView itself.
839      */
hasFixedSize()840     public boolean hasFixedSize() {
841         return mHasFixedSize;
842     }
843 
844     @Override
setClipToPadding(boolean clipToPadding)845     public void setClipToPadding(boolean clipToPadding) {
846         if (clipToPadding != mClipToPadding) {
847             invalidateGlows();
848         }
849         mClipToPadding = clipToPadding;
850         super.setClipToPadding(clipToPadding);
851         if (mFirstLayoutComplete) {
852             requestLayout();
853         }
854     }
855 
856     /**
857      * Returns whether this RecyclerView will clip its children to its padding, and resize (but
858      * not clip) any EdgeEffect to the padded region, if padding is present.
859      * <p>
860      * By default, children are clipped to the padding of their parent
861      * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
862      *
863      * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
864      *         clip) any EdgeEffect to the padded region, false otherwise.
865      *
866      * @attr name android:clipToPadding
867      */
getClipToPadding()868     public boolean getClipToPadding() {
869         return mClipToPadding;
870     }
871 
872     /**
873      * Configure the scrolling touch slop for a specific use case.
874      *
875      * Set up the RecyclerView's scrolling motion threshold based on common usages.
876      * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
877      *
878      * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
879      *                     the intended usage of this RecyclerView
880      */
setScrollingTouchSlop(int slopConstant)881     public void setScrollingTouchSlop(int slopConstant) {
882         final ViewConfiguration vc = ViewConfiguration.get(getContext());
883         switch (slopConstant) {
884             default:
885                 Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
886                       + slopConstant + "; using default value");
887                 // fall-through
888             case TOUCH_SLOP_DEFAULT:
889                 mTouchSlop = vc.getScaledTouchSlop();
890                 break;
891 
892             case TOUCH_SLOP_PAGING:
893                 mTouchSlop = vc.getScaledPagingTouchSlop();
894                 break;
895         }
896     }
897 
898     /**
899      * Swaps the current adapter with the provided one. It is similar to
900      * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
901      * {@link ViewHolder} and does not clear the RecycledViewPool.
902      * <p>
903      * Note that it still calls onAdapterChanged callbacks.
904      *
905      * @param adapter The new adapter to set, or null to set no adapter.
906      * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
907      *                                      Views. If adapters have stable ids and/or you want to
908      *                                      animate the disappearing views, you may prefer to set
909      *                                      this to false.
910      * @see #setAdapter(Adapter)
911      */
swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews)912     public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
913         // bail out if layout is frozen
914         setLayoutFrozen(false);
915         setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
916         setDataSetChangedAfterLayout();
917         requestLayout();
918     }
919     /**
920      * Set a new adapter to provide child views on demand.
921      * <p>
922      * When adapter is changed, all existing views are recycled back to the pool. If the pool has
923      * only one adapter, it will be cleared.
924      *
925      * @param adapter The new adapter to set, or null to set no adapter.
926      * @see #swapAdapter(Adapter, boolean)
927      */
setAdapter(Adapter adapter)928     public void setAdapter(Adapter adapter) {
929         // bail out if layout is frozen
930         setLayoutFrozen(false);
931         setAdapterInternal(adapter, false, true);
932         requestLayout();
933     }
934 
935     /**
936      * Replaces the current adapter with the new one and triggers listeners.
937      * @param adapter The new adapter
938      * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
939      *                               item types with the current adapter (helps us avoid cache
940      *                               invalidation).
941      * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
942      *                               compatibleWithPrevious is false, this parameter is ignored.
943      */
setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews)944     private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
945             boolean removeAndRecycleViews) {
946         if (mAdapter != null) {
947             mAdapter.unregisterAdapterDataObserver(mObserver);
948             mAdapter.onDetachedFromRecyclerView(this);
949         }
950         if (!compatibleWithPrevious || removeAndRecycleViews) {
951             // end all running animations
952             if (mItemAnimator != null) {
953                 mItemAnimator.endAnimations();
954             }
955             // Since animations are ended, mLayout.children should be equal to
956             // recyclerView.children. This may not be true if item animator's end does not work as
957             // expected. (e.g. not release children instantly). It is safer to use mLayout's child
958             // count.
959             if (mLayout != null) {
960                 mLayout.removeAndRecycleAllViews(mRecycler);
961                 mLayout.removeAndRecycleScrapInt(mRecycler);
962             }
963             // we should clear it here before adapters are swapped to ensure correct callbacks.
964             mRecycler.clear();
965         }
966         mAdapterHelper.reset();
967         final Adapter oldAdapter = mAdapter;
968         mAdapter = adapter;
969         if (adapter != null) {
970             adapter.registerAdapterDataObserver(mObserver);
971             adapter.onAttachedToRecyclerView(this);
972         }
973         if (mLayout != null) {
974             mLayout.onAdapterChanged(oldAdapter, mAdapter);
975         }
976         mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
977         mState.mStructureChanged = true;
978         markKnownViewsInvalid();
979     }
980 
981     /**
982      * Retrieves the previously set adapter or null if no adapter is set.
983      *
984      * @return The previously set adapter
985      * @see #setAdapter(Adapter)
986      */
getAdapter()987     public Adapter getAdapter() {
988         return mAdapter;
989     }
990 
991     /**
992      * Register a listener that will be notified whenever a child view is recycled.
993      *
994      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
995      * that a child view is no longer needed. If an application associates expensive
996      * or heavyweight data with item views, this may be a good place to release
997      * or free those resources.</p>
998      *
999      * @param listener Listener to register, or null to clear
1000      */
setRecyclerListener(RecyclerListener listener)1001     public void setRecyclerListener(RecyclerListener listener) {
1002         mRecyclerListener = listener;
1003     }
1004 
1005     /**
1006      * <p>Return the offset of the RecyclerView's text baseline from the its top
1007      * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
1008      * this method returns -1.</p>
1009      *
1010      * @return the offset of the baseline within the RecyclerView's bounds or -1
1011      *         if baseline alignment is not supported
1012      */
1013     @Override
getBaseline()1014     public int getBaseline() {
1015         if (mLayout != null) {
1016             return mLayout.getBaseline();
1017         } else {
1018             return super.getBaseline();
1019         }
1020     }
1021 
1022     /**
1023      * Register a listener that will be notified whenever a child view is attached to or detached
1024      * from RecyclerView.
1025      *
1026      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1027      * that a child view is no longer needed. If an application associates expensive
1028      * or heavyweight data with item views, this may be a good place to release
1029      * or free those resources.</p>
1030      *
1031      * @param listener Listener to register
1032      */
addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener)1033     public void addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1034         if (mOnChildAttachStateListeners == null) {
1035             mOnChildAttachStateListeners = new ArrayList<>();
1036         }
1037         mOnChildAttachStateListeners.add(listener);
1038     }
1039 
1040     /**
1041      * Removes the provided listener from child attached state listeners list.
1042      *
1043      * @param listener Listener to unregister
1044      */
removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener)1045     public void removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1046         if (mOnChildAttachStateListeners == null) {
1047             return;
1048         }
1049         mOnChildAttachStateListeners.remove(listener);
1050     }
1051 
1052     /**
1053      * Removes all listeners that were added via
1054      * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
1055      */
clearOnChildAttachStateChangeListeners()1056     public void clearOnChildAttachStateChangeListeners() {
1057         if (mOnChildAttachStateListeners != null) {
1058             mOnChildAttachStateListeners.clear();
1059         }
1060     }
1061 
1062     /**
1063      * Set the {@link LayoutManager} that this RecyclerView will use.
1064      *
1065      * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
1066      * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
1067      * layout arrangements for child views. These arrangements are controlled by the
1068      * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
1069      *
1070      * <p>Several default strategies are provided for common uses such as lists and grids.</p>
1071      *
1072      * @param layout LayoutManager to use
1073      */
setLayoutManager(LayoutManager layout)1074     public void setLayoutManager(LayoutManager layout) {
1075         if (layout == mLayout) {
1076             return;
1077         }
1078         stopScroll();
1079         // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
1080         // chance that LayoutManagers will re-use views.
1081         if (mLayout != null) {
1082             // end all running animations
1083             if (mItemAnimator != null) {
1084                 mItemAnimator.endAnimations();
1085             }
1086             mLayout.removeAndRecycleAllViews(mRecycler);
1087             mLayout.removeAndRecycleScrapInt(mRecycler);
1088             mRecycler.clear();
1089 
1090             if (mIsAttached) {
1091                 mLayout.dispatchDetachedFromWindow(this, mRecycler);
1092             }
1093             mLayout.setRecyclerView(null);
1094             mLayout = null;
1095         } else {
1096             mRecycler.clear();
1097         }
1098         // this is just a defensive measure for faulty item animators.
1099         mChildHelper.removeAllViewsUnfiltered();
1100         mLayout = layout;
1101         if (layout != null) {
1102             if (layout.mRecyclerView != null) {
1103                 throw new IllegalArgumentException("LayoutManager " + layout +
1104                         " is already attached to a RecyclerView: " + layout.mRecyclerView);
1105             }
1106             mLayout.setRecyclerView(this);
1107             if (mIsAttached) {
1108                 mLayout.dispatchAttachedToWindow(this);
1109             }
1110         }
1111         requestLayout();
1112     }
1113 
1114     /**
1115      * Set a {@link OnFlingListener} for this {@link RecyclerView}.
1116      * <p>
1117      * If the {@link OnFlingListener} is set then it will receive
1118      * calls to {@link #fling(int,int)} and will be able to intercept them.
1119      *
1120      * @param onFlingListener The {@link OnFlingListener} instance.
1121      */
setOnFlingListener(@ullable OnFlingListener onFlingListener)1122     public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
1123         mOnFlingListener = onFlingListener;
1124     }
1125 
1126     /**
1127      * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
1128      *
1129      * @return The {@link OnFlingListener} instance currently set (can be null).
1130      */
1131     @Nullable
getOnFlingListener()1132     public OnFlingListener getOnFlingListener() {
1133         return mOnFlingListener;
1134     }
1135 
1136     @Override
onSaveInstanceState()1137     protected Parcelable onSaveInstanceState() {
1138         SavedState state = new SavedState(super.onSaveInstanceState());
1139         if (mPendingSavedState != null) {
1140             state.copyFrom(mPendingSavedState);
1141         } else if (mLayout != null) {
1142             state.mLayoutState = mLayout.onSaveInstanceState();
1143         } else {
1144             state.mLayoutState = null;
1145         }
1146 
1147         return state;
1148     }
1149 
1150     @Override
onRestoreInstanceState(Parcelable state)1151     protected void onRestoreInstanceState(Parcelable state) {
1152         if (!(state instanceof SavedState)) {
1153             super.onRestoreInstanceState(state);
1154             return;
1155         }
1156 
1157         mPendingSavedState = (SavedState) state;
1158         super.onRestoreInstanceState(mPendingSavedState.getSuperState());
1159         if (mLayout != null && mPendingSavedState.mLayoutState != null) {
1160             mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
1161         }
1162     }
1163 
1164     /**
1165      * Override to prevent freezing of any views created by the adapter.
1166      */
1167     @Override
dispatchSaveInstanceState(SparseArray<Parcelable> container)1168     protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1169         dispatchFreezeSelfOnly(container);
1170     }
1171 
1172     /**
1173      * Override to prevent thawing of any views created by the adapter.
1174      */
1175     @Override
dispatchRestoreInstanceState(SparseArray<Parcelable> container)1176     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1177         dispatchThawSelfOnly(container);
1178     }
1179 
1180     /**
1181      * Adds a view to the animatingViews list.
1182      * mAnimatingViews holds the child views that are currently being kept around
1183      * purely for the purpose of being animated out of view. They are drawn as a regular
1184      * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
1185      * as they are managed separately from the regular child views.
1186      * @param viewHolder The ViewHolder to be removed
1187      */
addAnimatingView(ViewHolder viewHolder)1188     private void addAnimatingView(ViewHolder viewHolder) {
1189         final View view = viewHolder.itemView;
1190         final boolean alreadyParented = view.getParent() == this;
1191         mRecycler.unscrapView(getChildViewHolder(view));
1192         if (viewHolder.isTmpDetached()) {
1193             // re-attach
1194             mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
1195         } else if(!alreadyParented) {
1196             mChildHelper.addView(view, true);
1197         } else {
1198             mChildHelper.hide(view);
1199         }
1200     }
1201 
1202     /**
1203      * Removes a view from the animatingViews list.
1204      * @param view The view to be removed
1205      * @see #addAnimatingView(RecyclerView.ViewHolder)
1206      * @return true if an animating view is removed
1207      */
removeAnimatingView(View view)1208     private boolean removeAnimatingView(View view) {
1209         eatRequestLayout();
1210         final boolean removed = mChildHelper.removeViewIfHidden(view);
1211         if (removed) {
1212             final ViewHolder viewHolder = getChildViewHolderInt(view);
1213             mRecycler.unscrapView(viewHolder);
1214             mRecycler.recycleViewHolderInternal(viewHolder);
1215             if (DEBUG) {
1216                 Log.d(TAG, "after removing animated view: " + view + ", " + this);
1217             }
1218         }
1219         // only clear request eaten flag if we removed the view.
1220         resumeRequestLayout(!removed);
1221         return removed;
1222     }
1223 
1224     /**
1225      * Return the {@link LayoutManager} currently responsible for
1226      * layout policy for this RecyclerView.
1227      *
1228      * @return The currently bound LayoutManager
1229      */
getLayoutManager()1230     public LayoutManager getLayoutManager() {
1231         return mLayout;
1232     }
1233 
1234     /**
1235      * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
1236      * if no pool is set for this view a new one will be created. See
1237      * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
1238      *
1239      * @return The pool used to store recycled item views for reuse.
1240      * @see #setRecycledViewPool(RecycledViewPool)
1241      */
getRecycledViewPool()1242     public RecycledViewPool getRecycledViewPool() {
1243         return mRecycler.getRecycledViewPool();
1244     }
1245 
1246     /**
1247      * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
1248      * This can be useful if you have multiple RecyclerViews with adapters that use the same
1249      * view types, for example if you have several data sets with the same kinds of item views
1250      * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
1251      *
1252      * @param pool Pool to set. If this parameter is null a new pool will be created and used.
1253      */
setRecycledViewPool(RecycledViewPool pool)1254     public void setRecycledViewPool(RecycledViewPool pool) {
1255         mRecycler.setRecycledViewPool(pool);
1256     }
1257 
1258     /**
1259      * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
1260      *
1261      * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
1262      *
1263      * @see {@link ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)}
1264      */
setViewCacheExtension(ViewCacheExtension extension)1265     public void setViewCacheExtension(ViewCacheExtension extension) {
1266         mRecycler.setViewCacheExtension(extension);
1267     }
1268 
1269     /**
1270      * Set the number of offscreen views to retain before adding them to the potentially shared
1271      * {@link #getRecycledViewPool() recycled view pool}.
1272      *
1273      * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
1274      * a LayoutManager to reuse those views unmodified without needing to return to the adapter
1275      * to rebind them.</p>
1276      *
1277      * @param size Number of views to cache offscreen before returning them to the general
1278      *             recycled view pool
1279      */
setItemViewCacheSize(int size)1280     public void setItemViewCacheSize(int size) {
1281         mRecycler.setViewCacheSize(size);
1282     }
1283 
1284     /**
1285      * Return the current scrolling state of the RecyclerView.
1286      *
1287      * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
1288      * {@link #SCROLL_STATE_SETTLING}
1289      */
getScrollState()1290     public int getScrollState() {
1291         return mScrollState;
1292     }
1293 
setScrollState(int state)1294     private void setScrollState(int state) {
1295         if (state == mScrollState) {
1296             return;
1297         }
1298         if (DEBUG) {
1299             Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
1300                     new Exception());
1301         }
1302         mScrollState = state;
1303         if (state != SCROLL_STATE_SETTLING) {
1304             stopScrollersInternal();
1305         }
1306         dispatchOnScrollStateChanged(state);
1307     }
1308 
1309     /**
1310      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1311      * affect both measurement and drawing of individual item views.
1312      *
1313      * <p>Item decorations are ordered. Decorations placed earlier in the list will
1314      * be run/queried/drawn first for their effects on item views. Padding added to views
1315      * will be nested; a padding added by an earlier decoration will mean further
1316      * item decorations in the list will be asked to draw/pad within the previous decoration's
1317      * given area.</p>
1318      *
1319      * @param decor Decoration to add
1320      * @param index Position in the decoration chain to insert this decoration at. If this value
1321      *              is negative the decoration will be added at the end.
1322      */
addItemDecoration(ItemDecoration decor, int index)1323     public void addItemDecoration(ItemDecoration decor, int index) {
1324         if (mLayout != null) {
1325             mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
1326                     + " layout");
1327         }
1328         if (mItemDecorations.isEmpty()) {
1329             setWillNotDraw(false);
1330         }
1331         if (index < 0) {
1332             mItemDecorations.add(decor);
1333         } else {
1334             mItemDecorations.add(index, decor);
1335         }
1336         markItemDecorInsetsDirty();
1337         requestLayout();
1338     }
1339 
1340     /**
1341      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1342      * affect both measurement and drawing of individual item views.
1343      *
1344      * <p>Item decorations are ordered. Decorations placed earlier in the list will
1345      * be run/queried/drawn first for their effects on item views. Padding added to views
1346      * will be nested; a padding added by an earlier decoration will mean further
1347      * item decorations in the list will be asked to draw/pad within the previous decoration's
1348      * given area.</p>
1349      *
1350      * @param decor Decoration to add
1351      */
addItemDecoration(ItemDecoration decor)1352     public void addItemDecoration(ItemDecoration decor) {
1353         addItemDecoration(decor, -1);
1354     }
1355 
1356     /**
1357      * Remove an {@link ItemDecoration} from this RecyclerView.
1358      *
1359      * <p>The given decoration will no longer impact the measurement and drawing of
1360      * item views.</p>
1361      *
1362      * @param decor Decoration to remove
1363      * @see #addItemDecoration(ItemDecoration)
1364      */
removeItemDecoration(ItemDecoration decor)1365     public void removeItemDecoration(ItemDecoration decor) {
1366         if (mLayout != null) {
1367             mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
1368                     + " layout");
1369         }
1370         mItemDecorations.remove(decor);
1371         if (mItemDecorations.isEmpty()) {
1372             setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
1373         }
1374         markItemDecorInsetsDirty();
1375         requestLayout();
1376     }
1377 
1378     /**
1379      * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
1380      * <p>
1381      * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
1382      * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
1383      * true if childDrawingOrderCallback is not null, false otherwise.
1384      * <p>
1385      * Note that child drawing order may be overridden by View's elevation.
1386      *
1387      * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
1388      *                                  system.
1389      */
setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback)1390     public void setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback) {
1391         if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
1392             return;
1393         }
1394         mChildDrawingOrderCallback = childDrawingOrderCallback;
1395         setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
1396     }
1397 
1398     /**
1399      * Set a listener that will be notified of any changes in scroll state or position.
1400      *
1401      * @param listener Listener to set or null to clear
1402      *
1403      * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
1404      *             {@link #removeOnScrollListener(OnScrollListener)}
1405      */
1406     @Deprecated
setOnScrollListener(OnScrollListener listener)1407     public void setOnScrollListener(OnScrollListener listener) {
1408         mScrollListener = listener;
1409     }
1410 
1411     /**
1412      * Add a listener that will be notified of any changes in scroll state or position.
1413      *
1414      * <p>Components that add a listener should take care to remove it when finished.
1415      * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
1416      * to remove all attached listeners.</p>
1417      *
1418      * @param listener listener to set or null to clear
1419      */
addOnScrollListener(OnScrollListener listener)1420     public void addOnScrollListener(OnScrollListener listener) {
1421         if (mScrollListeners == null) {
1422             mScrollListeners = new ArrayList<>();
1423         }
1424         mScrollListeners.add(listener);
1425     }
1426 
1427     /**
1428      * Remove a listener that was notified of any changes in scroll state or position.
1429      *
1430      * @param listener listener to set or null to clear
1431      */
removeOnScrollListener(OnScrollListener listener)1432     public void removeOnScrollListener(OnScrollListener listener) {
1433         if (mScrollListeners != null) {
1434             mScrollListeners.remove(listener);
1435         }
1436     }
1437 
1438     /**
1439      * Remove all secondary listener that were notified of any changes in scroll state or position.
1440      */
clearOnScrollListeners()1441     public void clearOnScrollListeners() {
1442         if (mScrollListeners != null) {
1443             mScrollListeners.clear();
1444         }
1445     }
1446 
1447     /**
1448      * Convenience method to scroll to a certain position.
1449      *
1450      * RecyclerView does not implement scrolling logic, rather forwards the call to
1451      * {@link android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
1452      * @param position Scroll to this adapter position
1453      * @see android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)
1454      */
scrollToPosition(int position)1455     public void scrollToPosition(int position) {
1456         if (mLayoutFrozen) {
1457             return;
1458         }
1459         stopScroll();
1460         if (mLayout == null) {
1461             Log.e(TAG, "Cannot scroll to position a LayoutManager set. " +
1462                     "Call setLayoutManager with a non-null argument.");
1463             return;
1464         }
1465         mLayout.scrollToPosition(position);
1466         awakenScrollBars();
1467     }
1468 
jumpToPositionForSmoothScroller(int position)1469     private void jumpToPositionForSmoothScroller(int position) {
1470         if (mLayout == null) {
1471             return;
1472         }
1473         mLayout.scrollToPosition(position);
1474         awakenScrollBars();
1475     }
1476 
1477     /**
1478      * Starts a smooth scroll to an adapter position.
1479      * <p>
1480      * To support smooth scrolling, you must override
1481      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
1482      * {@link SmoothScroller}.
1483      * <p>
1484      * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
1485      * provide a custom smooth scroll logic, override
1486      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
1487      * LayoutManager.
1488      *
1489      * @param position The adapter position to scroll to
1490      * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
1491      */
smoothScrollToPosition(int position)1492     public void smoothScrollToPosition(int position) {
1493         if (mLayoutFrozen) {
1494             return;
1495         }
1496         if (mLayout == null) {
1497             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
1498                     "Call setLayoutManager with a non-null argument.");
1499             return;
1500         }
1501         mLayout.smoothScrollToPosition(this, mState, position);
1502     }
1503 
1504     @Override
scrollTo(int x, int y)1505     public void scrollTo(int x, int y) {
1506         Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
1507                 + "Use scrollToPosition instead");
1508     }
1509 
1510     @Override
scrollBy(int x, int y)1511     public void scrollBy(int x, int y) {
1512         if (mLayout == null) {
1513             Log.e(TAG, "Cannot scroll without a LayoutManager set. " +
1514                     "Call setLayoutManager with a non-null argument.");
1515             return;
1516         }
1517         if (mLayoutFrozen) {
1518             return;
1519         }
1520         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1521         final boolean canScrollVertical = mLayout.canScrollVertically();
1522         if (canScrollHorizontal || canScrollVertical) {
1523             scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
1524         }
1525     }
1526 
1527     /**
1528      * Helper method reflect data changes to the state.
1529      * <p>
1530      * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
1531      * but data actually changed.
1532      * <p>
1533      * This method consumes all deferred changes to avoid that case.
1534      */
consumePendingUpdateOperations()1535     private void consumePendingUpdateOperations() {
1536         if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
1537             TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1538             dispatchLayout();
1539             TraceCompat.endSection();
1540             return;
1541         }
1542         if (!mAdapterHelper.hasPendingUpdates()) {
1543             return;
1544         }
1545 
1546         // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
1547         // of the visible items is affected and if not, just ignore the change.
1548         if (mAdapterHelper.hasAnyUpdateTypes(UpdateOp.UPDATE) && !mAdapterHelper
1549                 .hasAnyUpdateTypes(UpdateOp.ADD | UpdateOp.REMOVE | UpdateOp.MOVE)) {
1550             TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
1551             eatRequestLayout();
1552             mAdapterHelper.preProcess();
1553             if (!mLayoutRequestEaten) {
1554                 if (hasUpdatedView()) {
1555                     dispatchLayout();
1556                 } else {
1557                     // no need to layout, clean state
1558                     mAdapterHelper.consumePostponedUpdates();
1559                 }
1560             }
1561             resumeRequestLayout(true);
1562             TraceCompat.endSection();
1563         } else if (mAdapterHelper.hasPendingUpdates()) {
1564             TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1565             dispatchLayout();
1566             TraceCompat.endSection();
1567         }
1568     }
1569 
1570     /**
1571      * @return True if an existing view holder needs to be updated
1572      */
hasUpdatedView()1573     private boolean hasUpdatedView() {
1574         final int childCount = mChildHelper.getChildCount();
1575         for (int i = 0; i < childCount; i++) {
1576             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
1577             if (holder == null || holder.shouldIgnore()) {
1578                 continue;
1579             }
1580             if (holder.isUpdated()) {
1581                 return true;
1582             }
1583         }
1584         return false;
1585     }
1586 
1587     /**
1588      * Does not perform bounds checking. Used by internal methods that have already validated input.
1589      * <p>
1590      * It also reports any unused scroll request to the related EdgeEffect.
1591      *
1592      * @param x The amount of horizontal scroll request
1593      * @param y The amount of vertical scroll request
1594      * @param ev The originating MotionEvent, or null if not from a touch event.
1595      *
1596      * @return Whether any scroll was consumed in either direction.
1597      */
scrollByInternal(int x, int y, MotionEvent ev)1598     boolean scrollByInternal(int x, int y, MotionEvent ev) {
1599         int unconsumedX = 0, unconsumedY = 0;
1600         int consumedX = 0, consumedY = 0;
1601 
1602         consumePendingUpdateOperations();
1603         if (mAdapter != null) {
1604             eatRequestLayout();
1605             onEnterLayoutOrScroll();
1606             TraceCompat.beginSection(TRACE_SCROLL_TAG);
1607             if (x != 0) {
1608                 consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
1609                 unconsumedX = x - consumedX;
1610             }
1611             if (y != 0) {
1612                 consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
1613                 unconsumedY = y - consumedY;
1614             }
1615             TraceCompat.endSection();
1616             repositionShadowingViews();
1617             onExitLayoutOrScroll();
1618             resumeRequestLayout(false);
1619         }
1620         if (!mItemDecorations.isEmpty()) {
1621             invalidate();
1622         }
1623 
1624         if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
1625             // Update the last touch co-ords, taking any scroll offset into account
1626             mLastTouchX -= mScrollOffset[0];
1627             mLastTouchY -= mScrollOffset[1];
1628             if (ev != null) {
1629                 ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
1630             }
1631             mNestedOffsets[0] += mScrollOffset[0];
1632             mNestedOffsets[1] += mScrollOffset[1];
1633         } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
1634             if (ev != null) {
1635                 pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
1636             }
1637             considerReleasingGlowsOnScroll(x, y);
1638         }
1639         if (consumedX != 0 || consumedY != 0) {
1640             dispatchOnScrolled(consumedX, consumedY);
1641         }
1642         if (!awakenScrollBars()) {
1643             invalidate();
1644         }
1645         return consumedX != 0 || consumedY != 0;
1646     }
1647 
1648     /**
1649      * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
1650      * range. This value is used to compute the length of the thumb within the scrollbar's track.
1651      * </p>
1652      *
1653      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1654      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
1655      *
1656      * <p>Default implementation returns 0.</p>
1657      *
1658      * <p>If you want to support scroll bars, override
1659      * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
1660      * LayoutManager. </p>
1661      *
1662      * @return The horizontal offset of the scrollbar's thumb
1663      * @see android.support.v7.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset
1664      * (RecyclerView.State)
1665      */
1666     @Override
computeHorizontalScrollOffset()1667     public int computeHorizontalScrollOffset() {
1668         if (mLayout == null) {
1669             return 0;
1670         }
1671         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
1672     }
1673 
1674     /**
1675      * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
1676      * horizontal range. This value is used to compute the length of the thumb within the
1677      * scrollbar's track.</p>
1678      *
1679      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1680      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
1681      *
1682      * <p>Default implementation returns 0.</p>
1683      *
1684      * <p>If you want to support scroll bars, override
1685      * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
1686      * LayoutManager.</p>
1687      *
1688      * @return The horizontal extent of the scrollbar's thumb
1689      * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
1690      */
1691     @Override
computeHorizontalScrollExtent()1692     public int computeHorizontalScrollExtent() {
1693         if (mLayout == null) {
1694             return 0;
1695         }
1696         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
1697     }
1698 
1699     /**
1700      * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
1701      *
1702      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1703      * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
1704      *
1705      * <p>Default implementation returns 0.</p>
1706      *
1707      * <p>If you want to support scroll bars, override
1708      * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
1709      * LayoutManager.</p>
1710      *
1711      * @return The total horizontal range represented by the vertical scrollbar
1712      * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
1713      */
1714     @Override
computeHorizontalScrollRange()1715     public int computeHorizontalScrollRange() {
1716         if (mLayout == null) {
1717             return 0;
1718         }
1719         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
1720     }
1721 
1722     /**
1723      * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
1724      * This value is used to compute the length of the thumb within the scrollbar's track. </p>
1725      *
1726      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1727      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
1728      *
1729      * <p>Default implementation returns 0.</p>
1730      *
1731      * <p>If you want to support scroll bars, override
1732      * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
1733      * LayoutManager.</p>
1734      *
1735      * @return The vertical offset of the scrollbar's thumb
1736      * @see android.support.v7.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset
1737      * (RecyclerView.State)
1738      */
1739     @Override
computeVerticalScrollOffset()1740     public int computeVerticalScrollOffset() {
1741         if (mLayout == null) {
1742             return 0;
1743         }
1744         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
1745     }
1746 
1747     /**
1748      * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
1749      * This value is used to compute the length of the thumb within the scrollbar's track.</p>
1750      *
1751      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1752      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
1753      *
1754      * <p>Default implementation returns 0.</p>
1755      *
1756      * <p>If you want to support scroll bars, override
1757      * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
1758      * LayoutManager.</p>
1759      *
1760      * @return The vertical extent of the scrollbar's thumb
1761      * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
1762      */
1763     @Override
computeVerticalScrollExtent()1764     public int computeVerticalScrollExtent() {
1765         if (mLayout == null) {
1766             return 0;
1767         }
1768         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
1769     }
1770 
1771     /**
1772      * <p>Compute the vertical range that the vertical scrollbar represents.</p>
1773      *
1774      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1775      * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
1776      *
1777      * <p>Default implementation returns 0.</p>
1778      *
1779      * <p>If you want to support scroll bars, override
1780      * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
1781      * LayoutManager.</p>
1782      *
1783      * @return The total vertical range represented by the vertical scrollbar
1784      * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
1785      */
1786     @Override
computeVerticalScrollRange()1787     public int computeVerticalScrollRange() {
1788         if (mLayout == null) {
1789             return 0;
1790         }
1791         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
1792     }
1793 
1794 
eatRequestLayout()1795     void eatRequestLayout() {
1796         mEatRequestLayout++;
1797         if (mEatRequestLayout == 1 && !mLayoutFrozen) {
1798             mLayoutRequestEaten = false;
1799         }
1800     }
1801 
resumeRequestLayout(boolean performLayoutChildren)1802     void resumeRequestLayout(boolean performLayoutChildren) {
1803         if (mEatRequestLayout < 1) {
1804             //noinspection PointlessBooleanExpression
1805             if (DEBUG) {
1806                 throw new IllegalStateException("invalid eat request layout count");
1807             }
1808             mEatRequestLayout = 1;
1809         }
1810         if (!performLayoutChildren) {
1811             // Reset the layout request eaten counter.
1812             // This is necessary since eatRequest calls can be nested in which case the other
1813             // call will override the inner one.
1814             // for instance:
1815             // eat layout for process adapter updates
1816             //   eat layout for dispatchLayout
1817             //     a bunch of req layout calls arrive
1818 
1819             mLayoutRequestEaten = false;
1820         }
1821         if (mEatRequestLayout == 1) {
1822             // when layout is frozen we should delay dispatchLayout()
1823             if (performLayoutChildren && mLayoutRequestEaten && !mLayoutFrozen &&
1824                     mLayout != null && mAdapter != null) {
1825                 dispatchLayout();
1826             }
1827             if (!mLayoutFrozen) {
1828                 mLayoutRequestEaten = false;
1829             }
1830         }
1831         mEatRequestLayout--;
1832     }
1833 
1834     /**
1835      * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
1836      * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
1837      * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
1838      * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
1839      * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
1840      * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
1841      * called.
1842      *
1843      * <p>
1844      * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
1845      * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
1846      * RecyclerView, State, int)}.
1847      * <p>
1848      * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
1849      * stop frozen.
1850      * <p>
1851      * Note: Running ItemAnimator is not stopped automatically,  it's caller's
1852      * responsibility to call ItemAnimator.end().
1853      *
1854      * @param frozen   true to freeze layout and scroll, false to re-enable.
1855      */
setLayoutFrozen(boolean frozen)1856     public void setLayoutFrozen(boolean frozen) {
1857         if (frozen != mLayoutFrozen) {
1858             assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
1859             if (!frozen) {
1860                 mLayoutFrozen = false;
1861                 if (mLayoutRequestEaten && mLayout != null && mAdapter != null) {
1862                     requestLayout();
1863                 }
1864                 mLayoutRequestEaten = false;
1865             } else {
1866                 final long now = SystemClock.uptimeMillis();
1867                 MotionEvent cancelEvent = MotionEvent.obtain(now, now,
1868                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
1869                 onTouchEvent(cancelEvent);
1870                 mLayoutFrozen = true;
1871                 mIgnoreMotionEventTillDown = true;
1872                 stopScroll();
1873             }
1874         }
1875     }
1876 
1877     /**
1878      * Returns true if layout and scroll are frozen.
1879      *
1880      * @return true if layout and scroll are frozen
1881      * @see #setLayoutFrozen(boolean)
1882      */
isLayoutFrozen()1883     public boolean isLayoutFrozen() {
1884         return mLayoutFrozen;
1885     }
1886 
1887     /**
1888      * Animate a scroll by the given amount of pixels along either axis.
1889      *
1890      * @param dx Pixels to scroll horizontally
1891      * @param dy Pixels to scroll vertically
1892      */
smoothScrollBy(int dx, int dy)1893     public void smoothScrollBy(int dx, int dy) {
1894         if (mLayout == null) {
1895             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
1896                     "Call setLayoutManager with a non-null argument.");
1897             return;
1898         }
1899         if (mLayoutFrozen) {
1900             return;
1901         }
1902         if (!mLayout.canScrollHorizontally()) {
1903             dx = 0;
1904         }
1905         if (!mLayout.canScrollVertically()) {
1906             dy = 0;
1907         }
1908         if (dx != 0 || dy != 0) {
1909             mViewFlinger.smoothScrollBy(dx, dy);
1910         }
1911     }
1912 
1913     /**
1914      * Begin a standard fling with an initial velocity along each axis in pixels per second.
1915      * If the velocity given is below the system-defined minimum this method will return false
1916      * and no fling will occur.
1917      *
1918      * @param velocityX Initial horizontal velocity in pixels per second
1919      * @param velocityY Initial vertical velocity in pixels per second
1920      * @return true if the fling was started, false if the velocity was too low to fling or
1921      * LayoutManager does not support scrolling in the axis fling is issued.
1922      *
1923      * @see LayoutManager#canScrollVertically()
1924      * @see LayoutManager#canScrollHorizontally()
1925      */
fling(int velocityX, int velocityY)1926     public boolean fling(int velocityX, int velocityY) {
1927         if (mLayout == null) {
1928             Log.e(TAG, "Cannot fling without a LayoutManager set. " +
1929                     "Call setLayoutManager with a non-null argument.");
1930             return false;
1931         }
1932         if (mLayoutFrozen) {
1933             return false;
1934         }
1935 
1936         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1937         final boolean canScrollVertical = mLayout.canScrollVertically();
1938 
1939         if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
1940             velocityX = 0;
1941         }
1942         if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
1943             velocityY = 0;
1944         }
1945         if (velocityX == 0 && velocityY == 0) {
1946             // If we don't have any velocity, return false
1947             return false;
1948         }
1949 
1950         if (!dispatchNestedPreFling(velocityX, velocityY)) {
1951             final boolean canScroll = canScrollHorizontal || canScrollVertical;
1952             dispatchNestedFling(velocityX, velocityY, canScroll);
1953 
1954             if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
1955                 return true;
1956             }
1957 
1958             if (canScroll) {
1959                 velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
1960                 velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
1961                 mViewFlinger.fling(velocityX, velocityY);
1962                 return true;
1963             }
1964         }
1965         return false;
1966     }
1967 
1968     /**
1969      * Stop any current scroll in progress, such as one started by
1970      * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
1971      */
stopScroll()1972     public void stopScroll() {
1973         setScrollState(SCROLL_STATE_IDLE);
1974         stopScrollersInternal();
1975     }
1976 
1977     /**
1978      * Similar to {@link #stopScroll()} but does not set the state.
1979      */
stopScrollersInternal()1980     private void stopScrollersInternal() {
1981         mViewFlinger.stop();
1982         if (mLayout != null) {
1983             mLayout.stopSmoothScroller();
1984         }
1985     }
1986 
1987     /**
1988      * Returns the minimum velocity to start a fling.
1989      *
1990      * @return The minimum velocity to start a fling
1991      */
getMinFlingVelocity()1992     public int getMinFlingVelocity() {
1993         return mMinFlingVelocity;
1994     }
1995 
1996 
1997     /**
1998      * Returns the maximum fling velocity used by this RecyclerView.
1999      *
2000      * @return The maximum fling velocity used by this RecyclerView.
2001      */
getMaxFlingVelocity()2002     public int getMaxFlingVelocity() {
2003         return mMaxFlingVelocity;
2004     }
2005 
2006     /**
2007      * Apply a pull to relevant overscroll glow effects
2008      */
pullGlows(float x, float overscrollX, float y, float overscrollY)2009     private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
2010         boolean invalidate = false;
2011         if (overscrollX < 0) {
2012             ensureLeftGlow();
2013             if (mLeftGlow.onPull(-overscrollX / getWidth(), 1f - y  / getHeight())) {
2014                 invalidate = true;
2015             }
2016         } else if (overscrollX > 0) {
2017             ensureRightGlow();
2018             if (mRightGlow.onPull(overscrollX / getWidth(), y / getHeight())) {
2019                 invalidate = true;
2020             }
2021         }
2022 
2023         if (overscrollY < 0) {
2024             ensureTopGlow();
2025             if (mTopGlow.onPull(-overscrollY / getHeight(), x / getWidth())) {
2026                 invalidate = true;
2027             }
2028         } else if (overscrollY > 0) {
2029             ensureBottomGlow();
2030             if (mBottomGlow.onPull(overscrollY / getHeight(), 1f - x / getWidth())) {
2031                 invalidate = true;
2032             }
2033         }
2034 
2035         if (invalidate || overscrollX != 0 || overscrollY != 0) {
2036             ViewCompat.postInvalidateOnAnimation(this);
2037         }
2038     }
2039 
releaseGlows()2040     private void releaseGlows() {
2041         boolean needsInvalidate = false;
2042         if (mLeftGlow != null) needsInvalidate = mLeftGlow.onRelease();
2043         if (mTopGlow != null) needsInvalidate |= mTopGlow.onRelease();
2044         if (mRightGlow != null) needsInvalidate |= mRightGlow.onRelease();
2045         if (mBottomGlow != null) needsInvalidate |= mBottomGlow.onRelease();
2046         if (needsInvalidate) {
2047             ViewCompat.postInvalidateOnAnimation(this);
2048         }
2049     }
2050 
considerReleasingGlowsOnScroll(int dx, int dy)2051     private void considerReleasingGlowsOnScroll(int dx, int dy) {
2052         boolean needsInvalidate = false;
2053         if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
2054             needsInvalidate = mLeftGlow.onRelease();
2055         }
2056         if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
2057             needsInvalidate |= mRightGlow.onRelease();
2058         }
2059         if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
2060             needsInvalidate |= mTopGlow.onRelease();
2061         }
2062         if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
2063             needsInvalidate |= mBottomGlow.onRelease();
2064         }
2065         if (needsInvalidate) {
2066             ViewCompat.postInvalidateOnAnimation(this);
2067         }
2068     }
2069 
absorbGlows(int velocityX, int velocityY)2070     void absorbGlows(int velocityX, int velocityY) {
2071         if (velocityX < 0) {
2072             ensureLeftGlow();
2073             mLeftGlow.onAbsorb(-velocityX);
2074         } else if (velocityX > 0) {
2075             ensureRightGlow();
2076             mRightGlow.onAbsorb(velocityX);
2077         }
2078 
2079         if (velocityY < 0) {
2080             ensureTopGlow();
2081             mTopGlow.onAbsorb(-velocityY);
2082         } else if (velocityY > 0) {
2083             ensureBottomGlow();
2084             mBottomGlow.onAbsorb(velocityY);
2085         }
2086 
2087         if (velocityX != 0 || velocityY != 0) {
2088             ViewCompat.postInvalidateOnAnimation(this);
2089         }
2090     }
2091 
ensureLeftGlow()2092     void ensureLeftGlow() {
2093         if (mLeftGlow != null) {
2094             return;
2095         }
2096         mLeftGlow = new EdgeEffectCompat(getContext());
2097         if (mClipToPadding) {
2098             mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2099                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2100         } else {
2101             mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2102         }
2103     }
2104 
ensureRightGlow()2105     void ensureRightGlow() {
2106         if (mRightGlow != null) {
2107             return;
2108         }
2109         mRightGlow = new EdgeEffectCompat(getContext());
2110         if (mClipToPadding) {
2111             mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2112                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2113         } else {
2114             mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2115         }
2116     }
2117 
ensureTopGlow()2118     void ensureTopGlow() {
2119         if (mTopGlow != null) {
2120             return;
2121         }
2122         mTopGlow = new EdgeEffectCompat(getContext());
2123         if (mClipToPadding) {
2124             mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2125                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2126         } else {
2127             mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2128         }
2129 
2130     }
2131 
ensureBottomGlow()2132     void ensureBottomGlow() {
2133         if (mBottomGlow != null) {
2134             return;
2135         }
2136         mBottomGlow = new EdgeEffectCompat(getContext());
2137         if (mClipToPadding) {
2138             mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2139                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2140         } else {
2141             mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2142         }
2143     }
2144 
invalidateGlows()2145     void invalidateGlows() {
2146         mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
2147     }
2148 
2149     /**
2150      * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
2151      * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
2152      * that differs from other ViewGroups.
2153      * <p>
2154      * It first does a focus search within the RecyclerView. If this search finds a View that is in
2155      * the focus direction with respect to the currently focused View, RecyclerView returns that
2156      * child as the next focus target. When it cannot find such child, it calls
2157      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
2158      * in the focus search direction. If LayoutManager adds a View that matches the
2159      * focus search criteria, it will be returned as the focus search result. Otherwise,
2160      * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
2161      * <p>
2162      * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
2163      * is not in the focus direction is still valid focus target which may not be the desired
2164      * behavior if the Adapter has more children in the focus direction. To handle this case,
2165      * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
2166      * focus search in that direction. If there are no Views to gain focus, it will call
2167      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
2168      * focus search with the original (relative) direction. This allows RecyclerView to provide
2169      * better candidates to the focus search while still allowing the view system to take focus from
2170      * the RecyclerView and give it to a more suitable child if such child exists.
2171      *
2172      * @param focused The view that currently has focus
2173      * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
2174      * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
2175      * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
2176      *
2177      * @return A new View that can be the next focus after the focused View
2178      */
2179     @Override
focusSearch(View focused, int direction)2180     public View focusSearch(View focused, int direction) {
2181         View result = mLayout.onInterceptFocusSearch(focused, direction);
2182         if (result != null) {
2183             return result;
2184         }
2185         final boolean canRunFocusFailure = mAdapter != null && mLayout != null
2186                 && !isComputingLayout() && !mLayoutFrozen;
2187 
2188         final FocusFinder ff = FocusFinder.getInstance();
2189         if (canRunFocusFailure
2190                 && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
2191             // convert direction to absolute direction and see if we have a view there and if not
2192             // tell LayoutManager to add if it can.
2193             boolean needsFocusFailureLayout = false;
2194             if (mLayout.canScrollVertically()) {
2195                 final int absDir =
2196                         direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
2197                 final View found = ff.findNextFocus(this, focused, absDir);
2198                 needsFocusFailureLayout = found == null;
2199             }
2200             if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
2201                 boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
2202                 final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
2203                         ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2204                 final View found = ff.findNextFocus(this, focused, absDir);
2205                 needsFocusFailureLayout = found == null;
2206             }
2207             if (needsFocusFailureLayout) {
2208                 consumePendingUpdateOperations();
2209                 final View focusedItemView = findContainingItemView(focused);
2210                 if (focusedItemView == null) {
2211                     // panic, focused view is not a child anymore, cannot call super.
2212                     return null;
2213                 }
2214                 eatRequestLayout();
2215                 mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2216                 resumeRequestLayout(false);
2217             }
2218             result = ff.findNextFocus(this, focused, direction);
2219         } else {
2220             result = ff.findNextFocus(this, focused, direction);
2221             if (result == null && canRunFocusFailure) {
2222                 consumePendingUpdateOperations();
2223                 final View focusedItemView = findContainingItemView(focused);
2224                 if (focusedItemView == null) {
2225                     // panic, focused view is not a child anymore, cannot call super.
2226                     return null;
2227                 }
2228                 eatRequestLayout();
2229                 result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2230                 resumeRequestLayout(false);
2231             }
2232         }
2233         return isPreferredNextFocus(focused, result, direction)
2234                 ? result : super.focusSearch(focused, direction);
2235     }
2236 
2237     /**
2238      * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
2239      * assign it as the next focus View instead of letting view hierarchy decide.
2240      * A good candidate means a View that is aligned in the focus direction wrt the focused View
2241      * and is not the RecyclerView itself.
2242      * When this method returns false, RecyclerView will let the parent make the decision so the
2243      * same View may still get the focus as a result of that search.
2244      */
isPreferredNextFocus(View focused, View next, int direction)2245     private boolean isPreferredNextFocus(View focused, View next, int direction) {
2246         if (next == null || next == this) {
2247             return false;
2248         }
2249         if (focused == null) {
2250             return true;
2251         }
2252 
2253         if(direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
2254             final boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
2255             final int absHorizontal = (direction == View.FOCUS_FORWARD) ^ rtl
2256                     ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2257             if (isPreferredNextFocusAbsolute(focused, next, absHorizontal)) {
2258                 return true;
2259             }
2260             if (direction == View.FOCUS_FORWARD) {
2261                 return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_DOWN);
2262             } else {
2263                 return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_UP);
2264             }
2265         } else {
2266             return isPreferredNextFocusAbsolute(focused, next, direction);
2267         }
2268 
2269     }
2270 
2271     /**
2272      * Logic taken from FocusSearch#isCandidate
2273      */
isPreferredNextFocusAbsolute(View focused, View next, int direction)2274     private boolean isPreferredNextFocusAbsolute(View focused, View next, int direction) {
2275         mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2276         mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
2277         offsetDescendantRectToMyCoords(focused, mTempRect);
2278         offsetDescendantRectToMyCoords(next, mTempRect2);
2279         switch (direction) {
2280             case View.FOCUS_LEFT:
2281                 return (mTempRect.right > mTempRect2.right
2282                         || mTempRect.left >= mTempRect2.right)
2283                         && mTempRect.left > mTempRect2.left;
2284             case View.FOCUS_RIGHT:
2285                 return (mTempRect.left < mTempRect2.left
2286                         || mTempRect.right <= mTempRect2.left)
2287                         && mTempRect.right < mTempRect2.right;
2288             case View.FOCUS_UP:
2289                 return (mTempRect.bottom > mTempRect2.bottom
2290                         || mTempRect.top >= mTempRect2.bottom)
2291                         && mTempRect.top > mTempRect2.top;
2292             case View.FOCUS_DOWN:
2293                 return (mTempRect.top < mTempRect2.top
2294                         || mTempRect.bottom <= mTempRect2.top)
2295                         && mTempRect.bottom < mTempRect2.bottom;
2296         }
2297         throw new IllegalArgumentException("direction must be absolute. received:" + direction);
2298     }
2299 
2300     @Override
requestChildFocus(View child, View focused)2301     public void requestChildFocus(View child, View focused) {
2302         if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
2303             mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2304 
2305             // get item decor offsets w/o refreshing. If they are invalid, there will be another
2306             // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
2307             // View in viewport.
2308             final ViewGroup.LayoutParams focusedLayoutParams = focused.getLayoutParams();
2309             if (focusedLayoutParams instanceof LayoutParams) {
2310                 // if focused child has item decors, use them. Otherwise, ignore.
2311                 final LayoutParams lp = (LayoutParams) focusedLayoutParams;
2312                 if (!lp.mInsetsDirty) {
2313                     final Rect insets = lp.mDecorInsets;
2314                     mTempRect.left -= insets.left;
2315                     mTempRect.right += insets.right;
2316                     mTempRect.top -= insets.top;
2317                     mTempRect.bottom += insets.bottom;
2318                 }
2319             }
2320 
2321             offsetDescendantRectToMyCoords(focused, mTempRect);
2322             offsetRectIntoDescendantCoords(child, mTempRect);
2323             requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete);
2324         }
2325         super.requestChildFocus(child, focused);
2326     }
2327 
2328     @Override
requestChildRectangleOnScreen(View child, Rect rect, boolean immediate)2329     public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
2330         return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
2331     }
2332 
2333     @Override
addFocusables(ArrayList<View> views, int direction, int focusableMode)2334     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
2335         if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
2336             super.addFocusables(views, direction, focusableMode);
2337         }
2338     }
2339 
2340     @Override
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)2341     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
2342         if (isComputingLayout()) {
2343             // if we are in the middle of a layout calculation, don't let any child take focus.
2344             // RV will handle it after layout calculation is finished.
2345             return false;
2346         }
2347         return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
2348     }
2349 
2350     @Override
onAttachedToWindow()2351     protected void onAttachedToWindow() {
2352         super.onAttachedToWindow();
2353         mLayoutOrScrollCounter = 0;
2354         mIsAttached = true;
2355         mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
2356         if (mLayout != null) {
2357             mLayout.dispatchAttachedToWindow(this);
2358         }
2359         mPostedAnimatorRunner = false;
2360     }
2361 
2362     @Override
onDetachedFromWindow()2363     protected void onDetachedFromWindow() {
2364         super.onDetachedFromWindow();
2365         if (mItemAnimator != null) {
2366             mItemAnimator.endAnimations();
2367         }
2368         stopScroll();
2369         mIsAttached = false;
2370         if (mLayout != null) {
2371             mLayout.dispatchDetachedFromWindow(this, mRecycler);
2372         }
2373         removeCallbacks(mItemAnimatorRunner);
2374         mViewInfoStore.onDetach();
2375     }
2376 
2377     /**
2378      * Returns true if RecyclerView is attached to window.
2379      */
2380     // @override
isAttachedToWindow()2381     public boolean isAttachedToWindow() {
2382         return mIsAttached;
2383     }
2384 
2385     /**
2386      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2387      * {@link IllegalStateException} if it <b>is not</b>.
2388      *
2389      * @param message The message for the exception. Can be null.
2390      * @see #assertNotInLayoutOrScroll(String)
2391      */
assertInLayoutOrScroll(String message)2392     void assertInLayoutOrScroll(String message) {
2393         if (!isComputingLayout()) {
2394             if (message == null) {
2395                 throw new IllegalStateException("Cannot call this method unless RecyclerView is "
2396                         + "computing a layout or scrolling");
2397             }
2398             throw new IllegalStateException(message);
2399 
2400         }
2401     }
2402 
2403     /**
2404      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2405      * {@link IllegalStateException} if it <b>is</b>.
2406      *
2407      * @param message The message for the exception. Can be null.
2408      * @see #assertInLayoutOrScroll(String)
2409      */
assertNotInLayoutOrScroll(String message)2410     void assertNotInLayoutOrScroll(String message) {
2411         if (isComputingLayout()) {
2412             if (message == null) {
2413                 throw new IllegalStateException("Cannot call this method while RecyclerView is "
2414                         + "computing a layout or scrolling");
2415             }
2416             throw new IllegalStateException(message);
2417         }
2418         if (mDispatchScrollCounter > 0) {
2419             Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might be run"
2420                     + " during a measure & layout pass where you cannot change the RecyclerView"
2421                     + " data. Any method call that might change the structure of the RecyclerView"
2422                     + " or the adapter contents should be postponed to the next frame.",
2423                     new IllegalStateException(""));
2424         }
2425     }
2426 
2427     /**
2428      * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
2429      * to child views or this view's standard scrolling behavior.
2430      *
2431      * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
2432      * returns true from
2433      * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
2434      * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
2435      * for each incoming MotionEvent until the end of the gesture.</p>
2436      *
2437      * @param listener Listener to add
2438      * @see SimpleOnItemTouchListener
2439      */
addOnItemTouchListener(OnItemTouchListener listener)2440     public void addOnItemTouchListener(OnItemTouchListener listener) {
2441         mOnItemTouchListeners.add(listener);
2442     }
2443 
2444     /**
2445      * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
2446      *
2447      * @param listener Listener to remove
2448      */
removeOnItemTouchListener(OnItemTouchListener listener)2449     public void removeOnItemTouchListener(OnItemTouchListener listener) {
2450         mOnItemTouchListeners.remove(listener);
2451         if (mActiveOnItemTouchListener == listener) {
2452             mActiveOnItemTouchListener = null;
2453         }
2454     }
2455 
dispatchOnItemTouchIntercept(MotionEvent e)2456     private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
2457         final int action = e.getAction();
2458         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
2459             mActiveOnItemTouchListener = null;
2460         }
2461 
2462         final int listenerCount = mOnItemTouchListeners.size();
2463         for (int i = 0; i < listenerCount; i++) {
2464             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2465             if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
2466                 mActiveOnItemTouchListener = listener;
2467                 return true;
2468             }
2469         }
2470         return false;
2471     }
2472 
dispatchOnItemTouch(MotionEvent e)2473     private boolean dispatchOnItemTouch(MotionEvent e) {
2474         final int action = e.getAction();
2475         if (mActiveOnItemTouchListener != null) {
2476             if (action == MotionEvent.ACTION_DOWN) {
2477                 // Stale state from a previous gesture, we're starting a new one. Clear it.
2478                 mActiveOnItemTouchListener = null;
2479             } else {
2480                 mActiveOnItemTouchListener.onTouchEvent(this, e);
2481                 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
2482                     // Clean up for the next gesture.
2483                     mActiveOnItemTouchListener = null;
2484                 }
2485                 return true;
2486             }
2487         }
2488 
2489         // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
2490         // as called from onInterceptTouchEvent; skip it.
2491         if (action != MotionEvent.ACTION_DOWN) {
2492             final int listenerCount = mOnItemTouchListeners.size();
2493             for (int i = 0; i < listenerCount; i++) {
2494                 final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2495                 if (listener.onInterceptTouchEvent(this, e)) {
2496                     mActiveOnItemTouchListener = listener;
2497                     return true;
2498                 }
2499             }
2500         }
2501         return false;
2502     }
2503 
2504     @Override
onInterceptTouchEvent(MotionEvent e)2505     public boolean onInterceptTouchEvent(MotionEvent e) {
2506         if (mLayoutFrozen) {
2507             // When layout is frozen,  RV does not intercept the motion event.
2508             // A child view e.g. a button may still get the click.
2509             return false;
2510         }
2511         if (dispatchOnItemTouchIntercept(e)) {
2512             cancelTouch();
2513             return true;
2514         }
2515 
2516         if (mLayout == null) {
2517             return false;
2518         }
2519 
2520         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2521         final boolean canScrollVertically = mLayout.canScrollVertically();
2522 
2523         if (mVelocityTracker == null) {
2524             mVelocityTracker = VelocityTracker.obtain();
2525         }
2526         mVelocityTracker.addMovement(e);
2527 
2528         final int action = MotionEventCompat.getActionMasked(e);
2529         final int actionIndex = MotionEventCompat.getActionIndex(e);
2530 
2531         switch (action) {
2532             case MotionEvent.ACTION_DOWN:
2533                 if (mIgnoreMotionEventTillDown) {
2534                     mIgnoreMotionEventTillDown = false;
2535                 }
2536                 mScrollPointerId = e.getPointerId(0);
2537                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2538                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2539 
2540                 if (mScrollState == SCROLL_STATE_SETTLING) {
2541                     getParent().requestDisallowInterceptTouchEvent(true);
2542                     setScrollState(SCROLL_STATE_DRAGGING);
2543                 }
2544 
2545                 // Clear the nested offsets
2546                 mNestedOffsets[0] = mNestedOffsets[1] = 0;
2547 
2548                 int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2549                 if (canScrollHorizontally) {
2550                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2551                 }
2552                 if (canScrollVertically) {
2553                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2554                 }
2555                 startNestedScroll(nestedScrollAxis);
2556                 break;
2557 
2558             case MotionEventCompat.ACTION_POINTER_DOWN:
2559                 mScrollPointerId = e.getPointerId(actionIndex);
2560                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2561                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2562                 break;
2563 
2564             case MotionEvent.ACTION_MOVE: {
2565                 final int index = e.findPointerIndex(mScrollPointerId);
2566                 if (index < 0) {
2567                     Log.e(TAG, "Error processing scroll; pointer index for id " +
2568                             mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2569                     return false;
2570                 }
2571 
2572                 final int x = (int) (e.getX(index) + 0.5f);
2573                 final int y = (int) (e.getY(index) + 0.5f);
2574                 if (mScrollState != SCROLL_STATE_DRAGGING) {
2575                     final int dx = x - mInitialTouchX;
2576                     final int dy = y - mInitialTouchY;
2577                     boolean startScroll = false;
2578                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2579                         mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
2580                         startScroll = true;
2581                     }
2582                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2583                         mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
2584                         startScroll = true;
2585                     }
2586                     if (startScroll) {
2587                         setScrollState(SCROLL_STATE_DRAGGING);
2588                     }
2589                 }
2590             } break;
2591 
2592             case MotionEventCompat.ACTION_POINTER_UP: {
2593                 onPointerUp(e);
2594             } break;
2595 
2596             case MotionEvent.ACTION_UP: {
2597                 mVelocityTracker.clear();
2598                 stopNestedScroll();
2599             } break;
2600 
2601             case MotionEvent.ACTION_CANCEL: {
2602                 cancelTouch();
2603             }
2604         }
2605         return mScrollState == SCROLL_STATE_DRAGGING;
2606     }
2607 
2608     @Override
requestDisallowInterceptTouchEvent(boolean disallowIntercept)2609     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2610         final int listenerCount = mOnItemTouchListeners.size();
2611         for (int i = 0; i < listenerCount; i++) {
2612             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2613             listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
2614         }
2615         super.requestDisallowInterceptTouchEvent(disallowIntercept);
2616     }
2617 
2618     @Override
onTouchEvent(MotionEvent e)2619     public boolean onTouchEvent(MotionEvent e) {
2620         if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
2621             return false;
2622         }
2623         if (dispatchOnItemTouch(e)) {
2624             cancelTouch();
2625             return true;
2626         }
2627 
2628         if (mLayout == null) {
2629             return false;
2630         }
2631 
2632         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2633         final boolean canScrollVertically = mLayout.canScrollVertically();
2634 
2635         if (mVelocityTracker == null) {
2636             mVelocityTracker = VelocityTracker.obtain();
2637         }
2638         boolean eventAddedToVelocityTracker = false;
2639 
2640         final MotionEvent vtev = MotionEvent.obtain(e);
2641         final int action = MotionEventCompat.getActionMasked(e);
2642         final int actionIndex = MotionEventCompat.getActionIndex(e);
2643 
2644         if (action == MotionEvent.ACTION_DOWN) {
2645             mNestedOffsets[0] = mNestedOffsets[1] = 0;
2646         }
2647         vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
2648 
2649         switch (action) {
2650             case MotionEvent.ACTION_DOWN: {
2651                 mScrollPointerId = e.getPointerId(0);
2652                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2653                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2654 
2655                 int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
2656                 if (canScrollHorizontally) {
2657                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
2658                 }
2659                 if (canScrollVertically) {
2660                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
2661                 }
2662                 startNestedScroll(nestedScrollAxis);
2663             } break;
2664 
2665             case MotionEventCompat.ACTION_POINTER_DOWN: {
2666                 mScrollPointerId = e.getPointerId(actionIndex);
2667                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2668                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2669             } break;
2670 
2671             case MotionEvent.ACTION_MOVE: {
2672                 final int index = e.findPointerIndex(mScrollPointerId);
2673                 if (index < 0) {
2674                     Log.e(TAG, "Error processing scroll; pointer index for id " +
2675                             mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2676                     return false;
2677                 }
2678 
2679                 final int x = (int) (e.getX(index) + 0.5f);
2680                 final int y = (int) (e.getY(index) + 0.5f);
2681                 int dx = mLastTouchX - x;
2682                 int dy = mLastTouchY - y;
2683 
2684                 if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
2685                     dx -= mScrollConsumed[0];
2686                     dy -= mScrollConsumed[1];
2687                     vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
2688                     // Updated the nested offsets
2689                     mNestedOffsets[0] += mScrollOffset[0];
2690                     mNestedOffsets[1] += mScrollOffset[1];
2691                 }
2692 
2693                 if (mScrollState != SCROLL_STATE_DRAGGING) {
2694                     boolean startScroll = false;
2695                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2696                         if (dx > 0) {
2697                             dx -= mTouchSlop;
2698                         } else {
2699                             dx += mTouchSlop;
2700                         }
2701                         startScroll = true;
2702                     }
2703                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2704                         if (dy > 0) {
2705                             dy -= mTouchSlop;
2706                         } else {
2707                             dy += mTouchSlop;
2708                         }
2709                         startScroll = true;
2710                     }
2711                     if (startScroll) {
2712                         setScrollState(SCROLL_STATE_DRAGGING);
2713                     }
2714                 }
2715 
2716                 if (mScrollState == SCROLL_STATE_DRAGGING) {
2717                     mLastTouchX = x - mScrollOffset[0];
2718                     mLastTouchY = y - mScrollOffset[1];
2719 
2720                     if (scrollByInternal(
2721                             canScrollHorizontally ? dx : 0,
2722                             canScrollVertically ? dy : 0,
2723                             vtev)) {
2724                         getParent().requestDisallowInterceptTouchEvent(true);
2725                     }
2726                 }
2727             } break;
2728 
2729             case MotionEventCompat.ACTION_POINTER_UP: {
2730                 onPointerUp(e);
2731             } break;
2732 
2733             case MotionEvent.ACTION_UP: {
2734                 mVelocityTracker.addMovement(vtev);
2735                 eventAddedToVelocityTracker = true;
2736                 mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
2737                 final float xvel = canScrollHorizontally ?
2738                         -VelocityTrackerCompat.getXVelocity(mVelocityTracker, mScrollPointerId) : 0;
2739                 final float yvel = canScrollVertically ?
2740                         -VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId) : 0;
2741                 if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
2742                     setScrollState(SCROLL_STATE_IDLE);
2743                 }
2744                 resetTouch();
2745             } break;
2746 
2747             case MotionEvent.ACTION_CANCEL: {
2748                 cancelTouch();
2749             } break;
2750         }
2751 
2752         if (!eventAddedToVelocityTracker) {
2753             mVelocityTracker.addMovement(vtev);
2754         }
2755         vtev.recycle();
2756 
2757         return true;
2758     }
2759 
resetTouch()2760     private void resetTouch() {
2761         if (mVelocityTracker != null) {
2762             mVelocityTracker.clear();
2763         }
2764         stopNestedScroll();
2765         releaseGlows();
2766     }
2767 
cancelTouch()2768     private void cancelTouch() {
2769         resetTouch();
2770         setScrollState(SCROLL_STATE_IDLE);
2771     }
2772 
onPointerUp(MotionEvent e)2773     private void onPointerUp(MotionEvent e) {
2774         final int actionIndex = MotionEventCompat.getActionIndex(e);
2775         if (e.getPointerId(actionIndex) == mScrollPointerId) {
2776             // Pick a new pointer to pick up the slack.
2777             final int newIndex = actionIndex == 0 ? 1 : 0;
2778             mScrollPointerId = e.getPointerId(newIndex);
2779             mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
2780             mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
2781         }
2782     }
2783 
2784     // @Override
onGenericMotionEvent(MotionEvent event)2785     public boolean onGenericMotionEvent(MotionEvent event) {
2786         if (mLayout == null) {
2787             return false;
2788         }
2789         if (mLayoutFrozen) {
2790             return false;
2791         }
2792         if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
2793             if (event.getAction() == MotionEventCompat.ACTION_SCROLL) {
2794                 final float vScroll, hScroll;
2795                 if (mLayout.canScrollVertically()) {
2796                     // Inverse the sign of the vertical scroll to align the scroll orientation
2797                     // with AbsListView.
2798                     vScroll = -MotionEventCompat
2799                             .getAxisValue(event, MotionEventCompat.AXIS_VSCROLL);
2800                 } else {
2801                     vScroll = 0f;
2802                 }
2803                 if (mLayout.canScrollHorizontally()) {
2804                     hScroll = MotionEventCompat
2805                             .getAxisValue(event, MotionEventCompat.AXIS_HSCROLL);
2806                 } else {
2807                     hScroll = 0f;
2808                 }
2809 
2810                 if (vScroll != 0 || hScroll != 0) {
2811                     final float scrollFactor = getScrollFactor();
2812                     scrollByInternal((int) (hScroll * scrollFactor),
2813                             (int) (vScroll * scrollFactor), event);
2814                 }
2815             }
2816         }
2817         return false;
2818     }
2819 
2820     /**
2821      * Ported from View.getVerticalScrollFactor.
2822      */
getScrollFactor()2823     private float getScrollFactor() {
2824         if (mScrollFactor == Float.MIN_VALUE) {
2825             TypedValue outValue = new TypedValue();
2826             if (getContext().getTheme().resolveAttribute(
2827                     android.R.attr.listPreferredItemHeight, outValue, true)) {
2828                 mScrollFactor = outValue.getDimension(
2829                         getContext().getResources().getDisplayMetrics());
2830             } else {
2831                 return 0; //listPreferredItemHeight is not defined, no generic scrolling
2832             }
2833         }
2834         return mScrollFactor;
2835     }
2836 
2837     @Override
onMeasure(int widthSpec, int heightSpec)2838     protected void onMeasure(int widthSpec, int heightSpec) {
2839         if (mLayout == null) {
2840             defaultOnMeasure(widthSpec, heightSpec);
2841             return;
2842         }
2843         if (mLayout.mAutoMeasure) {
2844             final int widthMode = MeasureSpec.getMode(widthSpec);
2845             final int heightMode = MeasureSpec.getMode(heightSpec);
2846             final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
2847                     && heightMode == MeasureSpec.EXACTLY;
2848             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
2849             if (skipMeasure || mAdapter == null) {
2850                 return;
2851             }
2852             if (mState.mLayoutStep == State.STEP_START) {
2853                 dispatchLayoutStep1();
2854             }
2855             // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
2856             // consistency
2857             mLayout.setMeasureSpecs(widthSpec, heightSpec);
2858             mState.mIsMeasuring = true;
2859             dispatchLayoutStep2();
2860 
2861             // now we can get the width and height from the children.
2862             mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
2863 
2864             // if RecyclerView has non-exact width and height and if there is at least one child
2865             // which also has non-exact width & height, we have to re-measure.
2866             if (mLayout.shouldMeasureTwice()) {
2867                 mLayout.setMeasureSpecs(
2868                         MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
2869                         MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
2870                 mState.mIsMeasuring = true;
2871                 dispatchLayoutStep2();
2872                 // now we can get the width and height from the children.
2873                 mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
2874             }
2875         } else {
2876             if (mHasFixedSize) {
2877                 mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
2878                 return;
2879             }
2880             // custom onMeasure
2881             if (mAdapterUpdateDuringMeasure) {
2882                 eatRequestLayout();
2883                 processAdapterUpdatesAndSetAnimationFlags();
2884 
2885                 if (mState.mRunPredictiveAnimations) {
2886                     mState.mInPreLayout = true;
2887                 } else {
2888                     // consume remaining updates to provide a consistent state with the layout pass.
2889                     mAdapterHelper.consumeUpdatesInOnePass();
2890                     mState.mInPreLayout = false;
2891                 }
2892                 mAdapterUpdateDuringMeasure = false;
2893                 resumeRequestLayout(false);
2894             }
2895 
2896             if (mAdapter != null) {
2897                 mState.mItemCount = mAdapter.getItemCount();
2898             } else {
2899                 mState.mItemCount = 0;
2900             }
2901             eatRequestLayout();
2902             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
2903             resumeRequestLayout(false);
2904             mState.mInPreLayout = false; // clear
2905         }
2906     }
2907 
2908     /**
2909      * Used when onMeasure is called before layout manager is set
2910      */
defaultOnMeasure(int widthSpec, int heightSpec)2911     void defaultOnMeasure(int widthSpec, int heightSpec) {
2912         // calling LayoutManager here is not pretty but that API is already public and it is better
2913         // than creating another method since this is internal.
2914         final int width = LayoutManager.chooseSize(widthSpec,
2915                 getPaddingLeft() + getPaddingRight(),
2916                 ViewCompat.getMinimumWidth(this));
2917         final int height = LayoutManager.chooseSize(heightSpec,
2918                 getPaddingTop() + getPaddingBottom(),
2919                 ViewCompat.getMinimumHeight(this));
2920 
2921         setMeasuredDimension(width, height);
2922     }
2923 
2924     @Override
onSizeChanged(int w, int h, int oldw, int oldh)2925     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
2926         super.onSizeChanged(w, h, oldw, oldh);
2927         if (w != oldw || h != oldh) {
2928             invalidateGlows();
2929             // layout's w/h are updated during measure/layout steps.
2930         }
2931     }
2932 
2933     /**
2934      * Sets the {@link ItemAnimator} that will handle animations involving changes
2935      * to the items in this RecyclerView. By default, RecyclerView instantiates and
2936      * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
2937      * enabled for the RecyclerView depends on the ItemAnimator and whether
2938      * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
2939      * supports item animations}.
2940      *
2941      * @param animator The ItemAnimator being set. If null, no animations will occur
2942      * when changes occur to the items in this RecyclerView.
2943      */
setItemAnimator(ItemAnimator animator)2944     public void setItemAnimator(ItemAnimator animator) {
2945         if (mItemAnimator != null) {
2946             mItemAnimator.endAnimations();
2947             mItemAnimator.setListener(null);
2948         }
2949         mItemAnimator = animator;
2950         if (mItemAnimator != null) {
2951             mItemAnimator.setListener(mItemAnimatorListener);
2952         }
2953     }
2954 
onEnterLayoutOrScroll()2955     private void onEnterLayoutOrScroll() {
2956         mLayoutOrScrollCounter ++;
2957     }
2958 
onExitLayoutOrScroll()2959     private void onExitLayoutOrScroll() {
2960         mLayoutOrScrollCounter --;
2961         if (mLayoutOrScrollCounter < 1) {
2962             if (DEBUG && mLayoutOrScrollCounter < 0) {
2963                 throw new IllegalStateException("layout or scroll counter cannot go below zero."
2964                         + "Some calls are not matching");
2965             }
2966             mLayoutOrScrollCounter = 0;
2967             dispatchContentChangedIfNecessary();
2968         }
2969     }
2970 
isAccessibilityEnabled()2971     boolean isAccessibilityEnabled() {
2972         return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
2973     }
2974 
dispatchContentChangedIfNecessary()2975     private void dispatchContentChangedIfNecessary() {
2976         final int flags = mEatenAccessibilityChangeFlags;
2977         mEatenAccessibilityChangeFlags = 0;
2978         if (flags != 0 && isAccessibilityEnabled()) {
2979             final AccessibilityEvent event = AccessibilityEvent.obtain();
2980             event.setEventType(AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
2981             AccessibilityEventCompat.setContentChangeTypes(event, flags);
2982             sendAccessibilityEventUnchecked(event);
2983         }
2984     }
2985 
2986     /**
2987      * Returns whether RecyclerView is currently computing a layout.
2988      * <p>
2989      * If this method returns true, it means that RecyclerView is in a lockdown state and any
2990      * attempt to update adapter contents will result in an exception because adapter contents
2991      * cannot be changed while RecyclerView is trying to compute the layout.
2992      * <p>
2993      * It is very unlikely that your code will be running during this state as it is
2994      * called by the framework when a layout traversal happens or RecyclerView starts to scroll
2995      * in response to system events (touch, accessibility etc).
2996      * <p>
2997      * This case may happen if you have some custom logic to change adapter contents in
2998      * response to a View callback (e.g. focus change callback) which might be triggered during a
2999      * layout calculation. In these cases, you should just postpone the change using a Handler or a
3000      * similar mechanism.
3001      *
3002      * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
3003      *         otherwise
3004      */
isComputingLayout()3005     public boolean isComputingLayout() {
3006         return mLayoutOrScrollCounter > 0;
3007     }
3008 
3009     /**
3010      * Returns true if an accessibility event should not be dispatched now. This happens when an
3011      * accessibility request arrives while RecyclerView does not have a stable state which is very
3012      * hard to handle for a LayoutManager. Instead, this method records necessary information about
3013      * the event and dispatches a window change event after the critical section is finished.
3014      *
3015      * @return True if the accessibility event should be postponed.
3016      */
shouldDeferAccessibilityEvent(AccessibilityEvent event)3017     boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
3018         if (isComputingLayout()) {
3019             int type = 0;
3020             if (event != null) {
3021                 type = AccessibilityEventCompat.getContentChangeTypes(event);
3022             }
3023             if (type == 0) {
3024                 type = AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED;
3025             }
3026             mEatenAccessibilityChangeFlags |= type;
3027             return true;
3028         }
3029         return false;
3030     }
3031 
3032     @Override
sendAccessibilityEventUnchecked(AccessibilityEvent event)3033     public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
3034         if (shouldDeferAccessibilityEvent(event)) {
3035             return;
3036         }
3037         super.sendAccessibilityEventUnchecked(event);
3038     }
3039 
3040     /**
3041      * Gets the current ItemAnimator for this RecyclerView. A null return value
3042      * indicates that there is no animator and that item changes will happen without
3043      * any animations. By default, RecyclerView instantiates and
3044      * uses an instance of {@link DefaultItemAnimator}.
3045      *
3046      * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
3047      * when changes occur to the items in this RecyclerView.
3048      */
getItemAnimator()3049     public ItemAnimator getItemAnimator() {
3050         return mItemAnimator;
3051     }
3052 
3053     /**
3054      * Post a runnable to the next frame to run pending item animations. Only the first such
3055      * request will be posted, governed by the mPostedAnimatorRunner flag.
3056      */
postAnimationRunner()3057     private void postAnimationRunner() {
3058         if (!mPostedAnimatorRunner && mIsAttached) {
3059             ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
3060             mPostedAnimatorRunner = true;
3061         }
3062     }
3063 
predictiveItemAnimationsEnabled()3064     private boolean predictiveItemAnimationsEnabled() {
3065         return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
3066     }
3067 
3068     /**
3069      * Consumes adapter updates and calculates which type of animations we want to run.
3070      * Called in onMeasure and dispatchLayout.
3071      * <p>
3072      * This method may process only the pre-layout state of updates or all of them.
3073      */
processAdapterUpdatesAndSetAnimationFlags()3074     private void processAdapterUpdatesAndSetAnimationFlags() {
3075         if (mDataSetHasChangedAfterLayout) {
3076             // Processing these items have no value since data set changed unexpectedly.
3077             // Instead, we just reset it.
3078             mAdapterHelper.reset();
3079             markKnownViewsInvalid();
3080             mLayout.onItemsChanged(this);
3081         }
3082         // simple animations are a subset of advanced animations (which will cause a
3083         // pre-layout step)
3084         // If layout supports predictive animations, pre-process to decide if we want to run them
3085         if (predictiveItemAnimationsEnabled()) {
3086             mAdapterHelper.preProcess();
3087         } else {
3088             mAdapterHelper.consumeUpdatesInOnePass();
3089         }
3090         boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
3091         mState.mRunSimpleAnimations = mFirstLayoutComplete && mItemAnimator != null &&
3092                 (mDataSetHasChangedAfterLayout || animationTypeSupported ||
3093                         mLayout.mRequestedSimpleAnimations) &&
3094                 (!mDataSetHasChangedAfterLayout || mAdapter.hasStableIds());
3095         mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations &&
3096                 animationTypeSupported && !mDataSetHasChangedAfterLayout &&
3097                 predictiveItemAnimationsEnabled();
3098     }
3099 
3100     /**
3101      * Wrapper around layoutChildren() that handles animating changes caused by layout.
3102      * Animations work on the assumption that there are five different kinds of items
3103      * in play:
3104      * PERSISTENT: items are visible before and after layout
3105      * REMOVED: items were visible before layout and were removed by the app
3106      * ADDED: items did not exist before layout and were added by the app
3107      * DISAPPEARING: items exist in the data set before/after, but changed from
3108      * visible to non-visible in the process of layout (they were moved off
3109      * screen as a side-effect of other changes)
3110      * APPEARING: items exist in the data set before/after, but changed from
3111      * non-visible to visible in the process of layout (they were moved on
3112      * screen as a side-effect of other changes)
3113      * The overall approach figures out what items exist before/after layout and
3114      * infers one of the five above states for each of the items. Then the animations
3115      * are set up accordingly:
3116      * PERSISTENT views are animated via
3117      * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3118      * DISAPPEARING views are animated via
3119      * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3120      * APPEARING views are animated via
3121      * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3122      * and changed views are animated via
3123      * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
3124      */
dispatchLayout()3125     void dispatchLayout() {
3126         if (mAdapter == null) {
3127             Log.e(TAG, "No adapter attached; skipping layout");
3128             // leave the state in START
3129             return;
3130         }
3131         if (mLayout == null) {
3132             Log.e(TAG, "No layout manager attached; skipping layout");
3133             // leave the state in START
3134             return;
3135         }
3136         mState.mIsMeasuring = false;
3137         if (mState.mLayoutStep == State.STEP_START) {
3138             dispatchLayoutStep1();
3139             mLayout.setExactMeasureSpecsFrom(this);
3140             dispatchLayoutStep2();
3141         } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth() ||
3142                 mLayout.getHeight() != getHeight()) {
3143             // First 2 steps are done in onMeasure but looks like we have to run again due to
3144             // changed size.
3145             mLayout.setExactMeasureSpecsFrom(this);
3146             dispatchLayoutStep2();
3147         } else {
3148             // always make sure we sync them (to ensure mode is exact)
3149             mLayout.setExactMeasureSpecsFrom(this);
3150         }
3151         dispatchLayoutStep3();
3152     }
3153 
saveFocusInfo()3154     private void saveFocusInfo() {
3155         View child = null;
3156         if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
3157             child = getFocusedChild();
3158         }
3159 
3160         final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
3161         if (focusedVh == null) {
3162             resetFocusInfo();
3163         } else {
3164             mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
3165             mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION :
3166                     focusedVh.getAdapterPosition();
3167             mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
3168         }
3169     }
3170 
resetFocusInfo()3171     private void resetFocusInfo() {
3172         mState.mFocusedItemId = NO_ID;
3173         mState.mFocusedItemPosition = NO_POSITION;
3174         mState.mFocusedSubChildId = View.NO_ID;
3175     }
3176 
recoverFocusFromState()3177     private void recoverFocusFromState() {
3178         if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()) {
3179             return;
3180         }
3181         // only recover focus if RV itself has the focus or the focused view is hidden
3182         if (!isFocused()) {
3183             final View focusedChild = getFocusedChild();
3184             if (focusedChild == null || !mChildHelper.isHidden(focusedChild)) {
3185                 return;
3186             }
3187         }
3188         ViewHolder focusTarget = null;
3189         if (mState.mFocusedItemPosition != NO_POSITION) {
3190             focusTarget = findViewHolderForAdapterPosition(mState.mFocusedItemPosition);
3191         }
3192         if (focusTarget == null && mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
3193             focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
3194         }
3195         if (focusTarget == null || focusTarget.itemView.hasFocus() ||
3196                 !focusTarget.itemView.hasFocusable()) {
3197             return;
3198         }
3199         // looks like the focused item has been replaced with another view that represents the
3200         // same item in the adapter. Request focus on that.
3201         View viewToFocus = focusTarget.itemView;
3202         if (mState.mFocusedSubChildId != NO_ID) {
3203             View child = focusTarget.itemView.findViewById(mState.mFocusedSubChildId);
3204             if (child != null && child.isFocusable()) {
3205                 viewToFocus = child;
3206             }
3207         }
3208         viewToFocus.requestFocus();
3209     }
3210 
getDeepestFocusedViewWithId(View view)3211     private int getDeepestFocusedViewWithId(View view) {
3212         int lastKnownId = view.getId();
3213         while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
3214             view = ((ViewGroup) view).getFocusedChild();
3215             final int id = view.getId();
3216             if (id != View.NO_ID) {
3217                 lastKnownId = view.getId();
3218             }
3219         }
3220         return lastKnownId;
3221     }
3222 
3223     /**
3224      * The first step of a layout where we;
3225      * - process adapter updates
3226      * - decide which animation should run
3227      * - save information about current views
3228      * - If necessary, run predictive layout and save its information
3229      */
dispatchLayoutStep1()3230     private void dispatchLayoutStep1() {
3231         mState.assertLayoutStep(State.STEP_START);
3232         mState.mIsMeasuring = false;
3233         eatRequestLayout();
3234         mViewInfoStore.clear();
3235         onEnterLayoutOrScroll();
3236         saveFocusInfo();
3237         processAdapterUpdatesAndSetAnimationFlags();
3238         mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
3239         mItemsAddedOrRemoved = mItemsChanged = false;
3240         mState.mInPreLayout = mState.mRunPredictiveAnimations;
3241         mState.mItemCount = mAdapter.getItemCount();
3242         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3243 
3244         if (mState.mRunSimpleAnimations) {
3245             // Step 0: Find out where all non-removed items are, pre-layout
3246             int count = mChildHelper.getChildCount();
3247             for (int i = 0; i < count; ++i) {
3248                 final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3249                 if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
3250                     continue;
3251                 }
3252                 final ItemHolderInfo animationInfo = mItemAnimator
3253                         .recordPreLayoutInformation(mState, holder,
3254                                 ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
3255                                 holder.getUnmodifiedPayloads());
3256                 mViewInfoStore.addToPreLayout(holder, animationInfo);
3257                 if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
3258                         && !holder.shouldIgnore() && !holder.isInvalid()) {
3259                     long key = getChangedHolderKey(holder);
3260                     // This is NOT the only place where a ViewHolder is added to old change holders
3261                     // list. There is another case where:
3262                     //    * A VH is currently hidden but not deleted
3263                     //    * The hidden item is changed in the adapter
3264                     //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
3265                     // When this case is detected, RV will un-hide that view and add to the old
3266                     // change holders list.
3267                     mViewInfoStore.addToOldChangeHolders(key, holder);
3268                 }
3269             }
3270         }
3271         if (mState.mRunPredictiveAnimations) {
3272             // Step 1: run prelayout: This will use the old positions of items. The layout manager
3273             // is expected to layout everything, even removed items (though not to add removed
3274             // items back to the container). This gives the pre-layout position of APPEARING views
3275             // which come into existence as part of the real layout.
3276 
3277             // Save old positions so that LayoutManager can run its mapping logic.
3278             saveOldPositions();
3279             final boolean didStructureChange = mState.mStructureChanged;
3280             mState.mStructureChanged = false;
3281             // temporarily disable flag because we are asking for previous layout
3282             mLayout.onLayoutChildren(mRecycler, mState);
3283             mState.mStructureChanged = didStructureChange;
3284 
3285             for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
3286                 final View child = mChildHelper.getChildAt(i);
3287                 final ViewHolder viewHolder = getChildViewHolderInt(child);
3288                 if (viewHolder.shouldIgnore()) {
3289                     continue;
3290                 }
3291                 if (!mViewInfoStore.isInPreLayout(viewHolder)) {
3292                     int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
3293                     boolean wasHidden = viewHolder
3294                             .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3295                     if (!wasHidden) {
3296                         flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
3297                     }
3298                     final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
3299                             mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
3300                     if (wasHidden) {
3301                         recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
3302                     } else {
3303                         mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
3304                     }
3305                 }
3306             }
3307             // we don't process disappearing list because they may re-appear in post layout pass.
3308             clearOldPositions();
3309         } else {
3310             clearOldPositions();
3311         }
3312         onExitLayoutOrScroll();
3313         resumeRequestLayout(false);
3314         mState.mLayoutStep = State.STEP_LAYOUT;
3315     }
3316 
3317     /**
3318      * The second layout step where we do the actual layout of the views for the final state.
3319      * This step might be run multiple times if necessary (e.g. measure).
3320      */
dispatchLayoutStep2()3321     private void dispatchLayoutStep2() {
3322         eatRequestLayout();
3323         onEnterLayoutOrScroll();
3324         mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
3325         mAdapterHelper.consumeUpdatesInOnePass();
3326         mState.mItemCount = mAdapter.getItemCount();
3327         mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
3328 
3329         // Step 2: Run layout
3330         mState.mInPreLayout = false;
3331         mLayout.onLayoutChildren(mRecycler, mState);
3332 
3333         mState.mStructureChanged = false;
3334         mPendingSavedState = null;
3335 
3336         // onLayoutChildren may have caused client code to disable item animations; re-check
3337         mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
3338         mState.mLayoutStep = State.STEP_ANIMATIONS;
3339         onExitLayoutOrScroll();
3340         resumeRequestLayout(false);
3341     }
3342 
3343     /**
3344      * The final step of the layout where we save the information about views for animations,
3345      * trigger animations and do any necessary cleanup.
3346      */
dispatchLayoutStep3()3347     private void dispatchLayoutStep3() {
3348         mState.assertLayoutStep(State.STEP_ANIMATIONS);
3349         eatRequestLayout();
3350         onEnterLayoutOrScroll();
3351         mState.mLayoutStep = State.STEP_START;
3352         if (mState.mRunSimpleAnimations) {
3353             // Step 3: Find out where things are now, and process change animations.
3354             // traverse list in reverse because we may call animateChange in the loop which may
3355             // remove the target view holder.
3356             for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
3357                 ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3358                 if (holder.shouldIgnore()) {
3359                     continue;
3360                 }
3361                 long key = getChangedHolderKey(holder);
3362                 final ItemHolderInfo animationInfo = mItemAnimator
3363                         .recordPostLayoutInformation(mState, holder);
3364                 ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
3365                 if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
3366                     // run a change animation
3367 
3368                     // If an Item is CHANGED but the updated version is disappearing, it creates
3369                     // a conflicting case.
3370                     // Since a view that is marked as disappearing is likely to be going out of
3371                     // bounds, we run a change animation. Both views will be cleaned automatically
3372                     // once their animations finish.
3373                     // On the other hand, if it is the same view holder instance, we run a
3374                     // disappearing animation instead because we are not going to rebind the updated
3375                     // VH unless it is enforced by the layout manager.
3376                     final boolean oldDisappearing = mViewInfoStore.isDisappearing(
3377                             oldChangeViewHolder);
3378                     final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
3379                     if (oldDisappearing && oldChangeViewHolder == holder) {
3380                         // run disappear animation instead of change
3381                         mViewInfoStore.addToPostLayout(holder, animationInfo);
3382                     } else {
3383                         final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
3384                                 oldChangeViewHolder);
3385                         // we add and remove so that any post info is merged.
3386                         mViewInfoStore.addToPostLayout(holder, animationInfo);
3387                         ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
3388                         if (preInfo == null) {
3389                             handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
3390                         } else {
3391                             animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
3392                                     oldDisappearing, newDisappearing);
3393                         }
3394                     }
3395                 } else {
3396                     mViewInfoStore.addToPostLayout(holder, animationInfo);
3397                 }
3398             }
3399 
3400             // Step 4: Process view info lists and trigger animations
3401             mViewInfoStore.process(mViewInfoProcessCallback);
3402         }
3403 
3404         mLayout.removeAndRecycleScrapInt(mRecycler);
3405         mState.mPreviousLayoutItemCount = mState.mItemCount;
3406         mDataSetHasChangedAfterLayout = false;
3407         mState.mRunSimpleAnimations = false;
3408 
3409         mState.mRunPredictiveAnimations = false;
3410         mLayout.mRequestedSimpleAnimations = false;
3411         if (mRecycler.mChangedScrap != null) {
3412             mRecycler.mChangedScrap.clear();
3413         }
3414         mLayout.onLayoutCompleted(mState);
3415         onExitLayoutOrScroll();
3416         resumeRequestLayout(false);
3417         mViewInfoStore.clear();
3418         if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
3419             dispatchOnScrolled(0, 0);
3420         }
3421         recoverFocusFromState();
3422         resetFocusInfo();
3423     }
3424 
3425     /**
3426      * This handles the case where there is an unexpected VH missing in the pre-layout map.
3427      * <p>
3428      * We might be able to detect the error in the application which will help the developer to
3429      * resolve the issue.
3430      * <p>
3431      * If it is not an expected error, we at least print an error to notify the developer and ignore
3432      * the animation.
3433      *
3434      * https://code.google.com/p/android/issues/detail?id=193958
3435      *
3436      * @param key The change key
3437      * @param holder Current ViewHolder
3438      * @param oldChangeViewHolder Changed ViewHolder
3439      */
handleMissingPreInfoForChangeError(long key, ViewHolder holder, ViewHolder oldChangeViewHolder)3440     private void handleMissingPreInfoForChangeError(long key,
3441             ViewHolder holder, ViewHolder oldChangeViewHolder) {
3442         // check if two VH have the same key, if so, print that as an error
3443         final int childCount = mChildHelper.getChildCount();
3444         for (int i = 0; i < childCount; i++) {
3445             View view = mChildHelper.getChildAt(i);
3446             ViewHolder other = getChildViewHolderInt(view);
3447             if (other == holder) {
3448                 continue;
3449             }
3450             final long otherKey = getChangedHolderKey(other);
3451             if (otherKey == key) {
3452                 if (mAdapter != null && mAdapter.hasStableIds()) {
3453                     throw new IllegalStateException("Two different ViewHolders have the same stable"
3454                             + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
3455                             + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
3456                 } else {
3457                     throw new IllegalStateException("Two different ViewHolders have the same change"
3458                             + " ID. This might happen due to inconsistent Adapter update events or"
3459                             + " if the LayoutManager lays out the same View multiple times."
3460                             + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
3461                 }
3462             }
3463         }
3464         // Very unlikely to happen but if it does, notify the developer.
3465         Log.e(TAG, "Problem while matching changed view holders with the new"
3466                 + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
3467                 + " cannot be found but it is necessary for " + holder);
3468     }
3469 
3470     /**
3471      * Records the animation information for a view holder that was bounced from hidden list. It
3472      * also clears the bounce back flag.
3473      */
recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder, ItemHolderInfo animationInfo)3474     private void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
3475             ItemHolderInfo animationInfo) {
3476         // looks like this view bounced back from hidden list!
3477         viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3478         if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
3479                 && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
3480             long key = getChangedHolderKey(viewHolder);
3481             mViewInfoStore.addToOldChangeHolders(key, viewHolder);
3482         }
3483         mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
3484     }
3485 
findMinMaxChildLayoutPositions(int[] into)3486     private void findMinMaxChildLayoutPositions(int[] into) {
3487         final int count = mChildHelper.getChildCount();
3488         if (count == 0) {
3489             into[0] = NO_POSITION;
3490             into[1] = NO_POSITION;
3491             return;
3492         }
3493         int minPositionPreLayout = Integer.MAX_VALUE;
3494         int maxPositionPreLayout = Integer.MIN_VALUE;
3495         for (int i = 0; i < count; ++i) {
3496             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3497             if (holder.shouldIgnore()) {
3498                 continue;
3499             }
3500             final int pos = holder.getLayoutPosition();
3501             if (pos < minPositionPreLayout) {
3502                 minPositionPreLayout = pos;
3503             }
3504             if (pos > maxPositionPreLayout) {
3505                 maxPositionPreLayout = pos;
3506             }
3507         }
3508         into[0] = minPositionPreLayout;
3509         into[1] = maxPositionPreLayout;
3510     }
3511 
didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout)3512     private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
3513         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3514         return mMinMaxLayoutPositions[0] != minPositionPreLayout ||
3515                 mMinMaxLayoutPositions[1] != maxPositionPreLayout;
3516     }
3517 
3518     @Override
removeDetachedView(View child, boolean animate)3519     protected void removeDetachedView(View child, boolean animate) {
3520         ViewHolder vh = getChildViewHolderInt(child);
3521         if (vh != null) {
3522             if (vh.isTmpDetached()) {
3523                 vh.clearTmpDetachFlag();
3524             } else if (!vh.shouldIgnore()) {
3525                 throw new IllegalArgumentException("Called removeDetachedView with a view which"
3526                         + " is not flagged as tmp detached." + vh);
3527             }
3528         }
3529         dispatchChildDetached(child);
3530         super.removeDetachedView(child, animate);
3531     }
3532 
3533     /**
3534      * Returns a unique key to be used while handling change animations.
3535      * It might be child's position or stable id depending on the adapter type.
3536      */
getChangedHolderKey(ViewHolder holder)3537     long getChangedHolderKey(ViewHolder holder) {
3538         return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
3539     }
3540 
animateAppearance(@onNull ViewHolder itemHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)3541     private void animateAppearance(@NonNull ViewHolder itemHolder,
3542             @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
3543         itemHolder.setIsRecyclable(false);
3544         if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
3545             postAnimationRunner();
3546         }
3547     }
3548 
animateDisappearance(@onNull ViewHolder holder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo)3549     private void animateDisappearance(@NonNull ViewHolder holder,
3550             @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
3551         addAnimatingView(holder);
3552         holder.setIsRecyclable(false);
3553         if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
3554             postAnimationRunner();
3555         }
3556     }
3557 
animateChange(@onNull ViewHolder oldHolder, @NonNull ViewHolder newHolder, @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo, boolean oldHolderDisappearing, boolean newHolderDisappearing)3558     private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
3559             @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
3560             boolean oldHolderDisappearing, boolean newHolderDisappearing) {
3561         oldHolder.setIsRecyclable(false);
3562         if (oldHolderDisappearing) {
3563             addAnimatingView(oldHolder);
3564         }
3565         if (oldHolder != newHolder) {
3566             if (newHolderDisappearing) {
3567                 addAnimatingView(newHolder);
3568             }
3569             oldHolder.mShadowedHolder = newHolder;
3570             // old holder should disappear after animation ends
3571             addAnimatingView(oldHolder);
3572             mRecycler.unscrapView(oldHolder);
3573             newHolder.setIsRecyclable(false);
3574             newHolder.mShadowingHolder = oldHolder;
3575         }
3576         if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
3577             postAnimationRunner();
3578         }
3579     }
3580 
3581     @Override
onLayout(boolean changed, int l, int t, int r, int b)3582     protected void onLayout(boolean changed, int l, int t, int r, int b) {
3583         TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
3584         dispatchLayout();
3585         TraceCompat.endSection();
3586         mFirstLayoutComplete = true;
3587     }
3588 
3589     @Override
requestLayout()3590     public void requestLayout() {
3591         if (mEatRequestLayout == 0 && !mLayoutFrozen) {
3592             super.requestLayout();
3593         } else {
3594             mLayoutRequestEaten = true;
3595         }
3596     }
3597 
markItemDecorInsetsDirty()3598     void markItemDecorInsetsDirty() {
3599         final int childCount = mChildHelper.getUnfilteredChildCount();
3600         for (int i = 0; i < childCount; i++) {
3601             final View child = mChildHelper.getUnfilteredChildAt(i);
3602             ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
3603         }
3604         mRecycler.markItemDecorInsetsDirty();
3605     }
3606 
3607     @Override
draw(Canvas c)3608     public void draw(Canvas c) {
3609         super.draw(c);
3610 
3611         final int count = mItemDecorations.size();
3612         for (int i = 0; i < count; i++) {
3613             mItemDecorations.get(i).onDrawOver(c, this, mState);
3614         }
3615         // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
3616         // need find children closest to edges. Not sure if it is worth the effort.
3617         boolean needsInvalidate = false;
3618         if (mLeftGlow != null && !mLeftGlow.isFinished()) {
3619             final int restore = c.save();
3620             final int padding = mClipToPadding ? getPaddingBottom() : 0;
3621             c.rotate(270);
3622             c.translate(-getHeight() + padding, 0);
3623             needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
3624             c.restoreToCount(restore);
3625         }
3626         if (mTopGlow != null && !mTopGlow.isFinished()) {
3627             final int restore = c.save();
3628             if (mClipToPadding) {
3629                 c.translate(getPaddingLeft(), getPaddingTop());
3630             }
3631             needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
3632             c.restoreToCount(restore);
3633         }
3634         if (mRightGlow != null && !mRightGlow.isFinished()) {
3635             final int restore = c.save();
3636             final int width = getWidth();
3637             final int padding = mClipToPadding ? getPaddingTop() : 0;
3638             c.rotate(90);
3639             c.translate(-padding, -width);
3640             needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
3641             c.restoreToCount(restore);
3642         }
3643         if (mBottomGlow != null && !mBottomGlow.isFinished()) {
3644             final int restore = c.save();
3645             c.rotate(180);
3646             if (mClipToPadding) {
3647                 c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
3648             } else {
3649                 c.translate(-getWidth(), -getHeight());
3650             }
3651             needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
3652             c.restoreToCount(restore);
3653         }
3654 
3655         // If some views are animating, ItemDecorators are likely to move/change with them.
3656         // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
3657         // display lists are not invalidated.
3658         if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0 &&
3659                 mItemAnimator.isRunning()) {
3660             needsInvalidate = true;
3661         }
3662 
3663         if (needsInvalidate) {
3664             ViewCompat.postInvalidateOnAnimation(this);
3665         }
3666     }
3667 
3668     @Override
onDraw(Canvas c)3669     public void onDraw(Canvas c) {
3670         super.onDraw(c);
3671 
3672         final int count = mItemDecorations.size();
3673         for (int i = 0; i < count; i++) {
3674             mItemDecorations.get(i).onDraw(c, this, mState);
3675         }
3676     }
3677 
3678     @Override
checkLayoutParams(ViewGroup.LayoutParams p)3679     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
3680         return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
3681     }
3682 
3683     @Override
generateDefaultLayoutParams()3684     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
3685         if (mLayout == null) {
3686             throw new IllegalStateException("RecyclerView has no LayoutManager");
3687         }
3688         return mLayout.generateDefaultLayoutParams();
3689     }
3690 
3691     @Override
generateLayoutParams(AttributeSet attrs)3692     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
3693         if (mLayout == null) {
3694             throw new IllegalStateException("RecyclerView has no LayoutManager");
3695         }
3696         return mLayout.generateLayoutParams(getContext(), attrs);
3697     }
3698 
3699     @Override
generateLayoutParams(ViewGroup.LayoutParams p)3700     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
3701         if (mLayout == null) {
3702             throw new IllegalStateException("RecyclerView has no LayoutManager");
3703         }
3704         return mLayout.generateLayoutParams(p);
3705     }
3706 
3707     /**
3708      * Returns true if RecyclerView is currently running some animations.
3709      * <p>
3710      * If you want to be notified when animations are finished, use
3711      * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
3712      *
3713      * @return True if there are some item animations currently running or waiting to be started.
3714      */
isAnimating()3715     public boolean isAnimating() {
3716         return mItemAnimator != null && mItemAnimator.isRunning();
3717     }
3718 
saveOldPositions()3719     void saveOldPositions() {
3720         final int childCount = mChildHelper.getUnfilteredChildCount();
3721         for (int i = 0; i < childCount; i++) {
3722             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3723             if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
3724                 throw new IllegalStateException("view holder cannot have position -1 unless it"
3725                         + " is removed");
3726             }
3727             if (!holder.shouldIgnore()) {
3728                 holder.saveOldPosition();
3729             }
3730         }
3731     }
3732 
clearOldPositions()3733     void clearOldPositions() {
3734         final int childCount = mChildHelper.getUnfilteredChildCount();
3735         for (int i = 0; i < childCount; i++) {
3736             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3737             if (!holder.shouldIgnore()) {
3738                 holder.clearOldPosition();
3739             }
3740         }
3741         mRecycler.clearOldPositions();
3742     }
3743 
offsetPositionRecordsForMove(int from, int to)3744     void offsetPositionRecordsForMove(int from, int to) {
3745         final int childCount = mChildHelper.getUnfilteredChildCount();
3746         final int start, end, inBetweenOffset;
3747         if (from < to) {
3748             start = from;
3749             end = to;
3750             inBetweenOffset = -1;
3751         } else {
3752             start = to;
3753             end = from;
3754             inBetweenOffset = 1;
3755         }
3756 
3757         for (int i = 0; i < childCount; i++) {
3758             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3759             if (holder == null || holder.mPosition < start || holder.mPosition > end) {
3760                 continue;
3761             }
3762             if (DEBUG) {
3763                 Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder " +
3764                         holder);
3765             }
3766             if (holder.mPosition == from) {
3767                 holder.offsetPosition(to - from, false);
3768             } else {
3769                 holder.offsetPosition(inBetweenOffset, false);
3770             }
3771 
3772             mState.mStructureChanged = true;
3773         }
3774         mRecycler.offsetPositionRecordsForMove(from, to);
3775         requestLayout();
3776     }
3777 
offsetPositionRecordsForInsert(int positionStart, int itemCount)3778     void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
3779         final int childCount = mChildHelper.getUnfilteredChildCount();
3780         for (int i = 0; i < childCount; i++) {
3781             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3782             if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
3783                 if (DEBUG) {
3784                     Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder " +
3785                             holder + " now at position " + (holder.mPosition + itemCount));
3786                 }
3787                 holder.offsetPosition(itemCount, false);
3788                 mState.mStructureChanged = true;
3789             }
3790         }
3791         mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
3792         requestLayout();
3793     }
3794 
offsetPositionRecordsForRemove(int positionStart, int itemCount, boolean applyToPreLayout)3795     void offsetPositionRecordsForRemove(int positionStart, int itemCount,
3796             boolean applyToPreLayout) {
3797         final int positionEnd = positionStart + itemCount;
3798         final int childCount = mChildHelper.getUnfilteredChildCount();
3799         for (int i = 0; i < childCount; i++) {
3800             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3801             if (holder != null && !holder.shouldIgnore()) {
3802                 if (holder.mPosition >= positionEnd) {
3803                     if (DEBUG) {
3804                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +
3805                                 " holder " + holder + " now at position " +
3806                                 (holder.mPosition - itemCount));
3807                     }
3808                     holder.offsetPosition(-itemCount, applyToPreLayout);
3809                     mState.mStructureChanged = true;
3810                 } else if (holder.mPosition >= positionStart) {
3811                     if (DEBUG) {
3812                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +
3813                                 " holder " + holder + " now REMOVED");
3814                     }
3815                     holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
3816                             applyToPreLayout);
3817                     mState.mStructureChanged = true;
3818                 }
3819             }
3820         }
3821         mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
3822         requestLayout();
3823     }
3824 
3825     /**
3826      * Rebind existing views for the given range, or create as needed.
3827      *
3828      * @param positionStart Adapter position to start at
3829      * @param itemCount Number of views that must explicitly be rebound
3830      */
viewRangeUpdate(int positionStart, int itemCount, Object payload)3831     void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
3832         final int childCount = mChildHelper.getUnfilteredChildCount();
3833         final int positionEnd = positionStart + itemCount;
3834 
3835         for (int i = 0; i < childCount; i++) {
3836             final View child = mChildHelper.getUnfilteredChildAt(i);
3837             final ViewHolder holder = getChildViewHolderInt(child);
3838             if (holder == null || holder.shouldIgnore()) {
3839                 continue;
3840             }
3841             if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
3842                 // We re-bind these view holders after pre-processing is complete so that
3843                 // ViewHolders have their final positions assigned.
3844                 holder.addFlags(ViewHolder.FLAG_UPDATE);
3845                 holder.addChangePayload(payload);
3846                 // lp cannot be null since we get ViewHolder from it.
3847                 ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
3848             }
3849         }
3850         mRecycler.viewRangeUpdate(positionStart, itemCount);
3851     }
3852 
canReuseUpdatedViewHolder(ViewHolder viewHolder)3853     private boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
3854         return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
3855                 viewHolder.getUnmodifiedPayloads());
3856     }
3857 
setDataSetChangedAfterLayout()3858     private void setDataSetChangedAfterLayout() {
3859         if (mDataSetHasChangedAfterLayout) {
3860             return;
3861         }
3862         mDataSetHasChangedAfterLayout = true;
3863         final int childCount = mChildHelper.getUnfilteredChildCount();
3864         for (int i = 0; i < childCount; i++) {
3865             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3866             if (holder != null && !holder.shouldIgnore()) {
3867                 holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
3868             }
3869         }
3870         mRecycler.setAdapterPositionsAsUnknown();
3871     }
3872 
3873     /**
3874      * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
3875      * data change event.
3876      */
markKnownViewsInvalid()3877     void markKnownViewsInvalid() {
3878         final int childCount = mChildHelper.getUnfilteredChildCount();
3879         for (int i = 0; i < childCount; i++) {
3880             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3881             if (holder != null && !holder.shouldIgnore()) {
3882                 holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
3883             }
3884         }
3885         markItemDecorInsetsDirty();
3886         mRecycler.markKnownViewsInvalid();
3887     }
3888 
3889     /**
3890      * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
3891      * will trigger a {@link #requestLayout()} call.
3892      */
invalidateItemDecorations()3893     public void invalidateItemDecorations() {
3894         if (mItemDecorations.size() == 0) {
3895             return;
3896         }
3897         if (mLayout != null) {
3898             mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
3899                     + " or layout");
3900         }
3901         markItemDecorInsetsDirty();
3902         requestLayout();
3903     }
3904 
3905     /**
3906      * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
3907      * focus even if the View representing the Item is replaced during a layout calculation.
3908      * <p>
3909      * By default, this value is {@code true}.
3910      *
3911      * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
3912      * focus.
3913      *
3914      * @see #setPreserveFocusAfterLayout(boolean)
3915      */
getPreserveFocusAfterLayout()3916     public boolean getPreserveFocusAfterLayout() {
3917         return mPreserveFocusAfterLayout;
3918     }
3919 
3920     /**
3921      * Set whether the RecyclerView should try to keep the same Item focused after a layout
3922      * calculation or not.
3923      * <p>
3924      * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
3925      * views may lose focus during a layout calculation as their state changes or they are replaced
3926      * with another view due to type change or animation. In these cases, RecyclerView can request
3927      * focus on the new view automatically.
3928      *
3929      * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
3930      *                                 layout calculations. Defaults to true.
3931      *
3932      * @see #getPreserveFocusAfterLayout()
3933      */
setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout)3934     public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
3935         mPreserveFocusAfterLayout = preserveFocusAfterLayout;
3936     }
3937 
3938     /**
3939      * Retrieve the {@link ViewHolder} for the given child view.
3940      *
3941      * @param child Child of this RecyclerView to query for its ViewHolder
3942      * @return The child view's ViewHolder
3943      */
getChildViewHolder(View child)3944     public ViewHolder getChildViewHolder(View child) {
3945         final ViewParent parent = child.getParent();
3946         if (parent != null && parent != this) {
3947             throw new IllegalArgumentException("View " + child + " is not a direct child of " +
3948                     this);
3949         }
3950         return getChildViewHolderInt(child);
3951     }
3952 
3953     /**
3954      * Traverses the ancestors of the given view and returns the item view that contains it and
3955      * also a direct child of the RecyclerView. This returned view can be used to get the
3956      * ViewHolder by calling {@link #getChildViewHolder(View)}.
3957      *
3958      * @param view The view that is a descendant of the RecyclerView.
3959      *
3960      * @return The direct child of the RecyclerView which contains the given view or null if the
3961      * provided view is not a descendant of this RecyclerView.
3962      *
3963      * @see #getChildViewHolder(View)
3964      * @see #findContainingViewHolder(View)
3965      */
3966     @Nullable
findContainingItemView(View view)3967     public View findContainingItemView(View view) {
3968         ViewParent parent = view.getParent();
3969         while (parent != null && parent != this && parent instanceof View) {
3970             view = (View) parent;
3971             parent = view.getParent();
3972         }
3973         return parent == this ? view : null;
3974     }
3975 
3976     /**
3977      * Returns the ViewHolder that contains the given view.
3978      *
3979      * @param view The view that is a descendant of the RecyclerView.
3980      *
3981      * @return The ViewHolder that contains the given view or null if the provided view is not a
3982      * descendant of this RecyclerView.
3983      */
3984     @Nullable
findContainingViewHolder(View view)3985     public ViewHolder findContainingViewHolder(View view) {
3986         View itemView = findContainingItemView(view);
3987         return itemView == null ? null : getChildViewHolder(itemView);
3988     }
3989 
3990 
getChildViewHolderInt(View child)3991     static ViewHolder getChildViewHolderInt(View child) {
3992         if (child == null) {
3993             return null;
3994         }
3995         return ((LayoutParams) child.getLayoutParams()).mViewHolder;
3996     }
3997 
3998     /**
3999      * @deprecated use {@link #getChildAdapterPosition(View)} or
4000      * {@link #getChildLayoutPosition(View)}.
4001      */
4002     @Deprecated
getChildPosition(View child)4003     public int getChildPosition(View child) {
4004         return getChildAdapterPosition(child);
4005     }
4006 
4007     /**
4008      * Return the adapter position that the given child view corresponds to.
4009      *
4010      * @param child Child View to query
4011      * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
4012      */
getChildAdapterPosition(View child)4013     public int getChildAdapterPosition(View child) {
4014         final ViewHolder holder = getChildViewHolderInt(child);
4015         return holder != null ? holder.getAdapterPosition() : NO_POSITION;
4016     }
4017 
4018     /**
4019      * Return the adapter position of the given child view as of the latest completed layout pass.
4020      * <p>
4021      * This position may not be equal to Item's adapter position if there are pending changes
4022      * in the adapter which have not been reflected to the layout yet.
4023      *
4024      * @param child Child View to query
4025      * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
4026      * the View is representing a removed item.
4027      */
getChildLayoutPosition(View child)4028     public int getChildLayoutPosition(View child) {
4029         final ViewHolder holder = getChildViewHolderInt(child);
4030         return holder != null ? holder.getLayoutPosition() : NO_POSITION;
4031     }
4032 
4033     /**
4034      * Return the stable item id that the given child view corresponds to.
4035      *
4036      * @param child Child View to query
4037      * @return Item id corresponding to the given view or {@link #NO_ID}
4038      */
getChildItemId(View child)4039     public long getChildItemId(View child) {
4040         if (mAdapter == null || !mAdapter.hasStableIds()) {
4041             return NO_ID;
4042         }
4043         final ViewHolder holder = getChildViewHolderInt(child);
4044         return holder != null ? holder.getItemId() : NO_ID;
4045     }
4046 
4047     /**
4048      * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
4049      * {@link #findViewHolderForAdapterPosition(int)}
4050      */
4051     @Deprecated
findViewHolderForPosition(int position)4052     public ViewHolder findViewHolderForPosition(int position) {
4053         return findViewHolderForPosition(position, false);
4054     }
4055 
4056     /**
4057      * Return the ViewHolder for the item in the given position of the data set as of the latest
4058      * layout pass.
4059      * <p>
4060      * This method checks only the children of RecyclerView. If the item at the given
4061      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4062      * <p>
4063      * Note that when Adapter contents change, ViewHolder positions are not updated until the
4064      * next layout calculation. If there are pending adapter updates, the return value of this
4065      * method may not match your adapter contents. You can use
4066      * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
4067      * <p>
4068      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4069      * with the same layout position representing the same Item. In this case, the updated
4070      * ViewHolder will be returned.
4071      *
4072      * @param position The position of the item in the data set of the adapter
4073      * @return The ViewHolder at <code>position</code> or null if there is no such item
4074      */
findViewHolderForLayoutPosition(int position)4075     public ViewHolder findViewHolderForLayoutPosition(int position) {
4076         return findViewHolderForPosition(position, false);
4077     }
4078 
4079     /**
4080      * Return the ViewHolder for the item in the given position of the data set. Unlike
4081      * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
4082      * adapter changes that may not be reflected to the layout yet. On the other hand, if
4083      * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
4084      * calculated yet, this method will return <code>null</code> since the new positions of views
4085      * are unknown until the layout is calculated.
4086      * <p>
4087      * This method checks only the children of RecyclerView. If the item at the given
4088      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4089      * <p>
4090      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4091      * representing the same Item. In this case, the updated ViewHolder will be returned.
4092      *
4093      * @param position The position of the item in the data set of the adapter
4094      * @return The ViewHolder at <code>position</code> or null if there is no such item
4095      */
findViewHolderForAdapterPosition(int position)4096     public ViewHolder findViewHolderForAdapterPosition(int position) {
4097         if (mDataSetHasChangedAfterLayout) {
4098             return null;
4099         }
4100         final int childCount = mChildHelper.getUnfilteredChildCount();
4101         // hidden VHs are not preferred but if that is the only one we find, we rather return it
4102         ViewHolder hidden = null;
4103         for (int i = 0; i < childCount; i++) {
4104             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4105             if (holder != null && !holder.isRemoved() && getAdapterPositionFor(holder) == position) {
4106                 if (mChildHelper.isHidden(holder.itemView)) {
4107                     hidden = holder;
4108                 } else {
4109                     return holder;
4110                 }
4111             }
4112         }
4113         return hidden;
4114     }
4115 
findViewHolderForPosition(int position, boolean checkNewPosition)4116     ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
4117         final int childCount = mChildHelper.getUnfilteredChildCount();
4118         ViewHolder hidden = null;
4119         for (int i = 0; i < childCount; i++) {
4120             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4121             if (holder != null && !holder.isRemoved()) {
4122                 if (checkNewPosition) {
4123                     if (holder.mPosition != position) {
4124                         continue;
4125                     }
4126                 } else if (holder.getLayoutPosition() != position) {
4127                     continue;
4128                 }
4129                 if (mChildHelper.isHidden(holder.itemView)) {
4130                     hidden = holder;
4131                 } else {
4132                     return holder;
4133                 }
4134             }
4135         }
4136         // This method should not query cached views. It creates a problem during adapter updates
4137         // when we are dealing with already laid out views. Also, for the public method, it is more
4138         // reasonable to return null if position is not laid out.
4139         return hidden;
4140     }
4141 
4142     /**
4143      * Return the ViewHolder for the item with the given id. The RecyclerView must
4144      * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
4145      * return a non-null value.
4146      * <p>
4147      * This method checks only the children of RecyclerView. If the item with the given
4148      * <code>id</code> is not laid out, it <em>will not</em> create a new one.
4149      *
4150      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
4151      * same id. In this case, the updated ViewHolder will be returned.
4152      *
4153      * @param id The id for the requested item
4154      * @return The ViewHolder with the given <code>id</code> or null if there is no such item
4155      */
findViewHolderForItemId(long id)4156     public ViewHolder findViewHolderForItemId(long id) {
4157         if (mAdapter == null || !mAdapter.hasStableIds()) {
4158             return null;
4159         }
4160         final int childCount = mChildHelper.getUnfilteredChildCount();
4161         ViewHolder hidden = null;
4162         for (int i = 0; i < childCount; i++) {
4163             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4164             if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
4165                 if (mChildHelper.isHidden(holder.itemView)) {
4166                     hidden = holder;
4167                 } else {
4168                     return holder;
4169                 }
4170             }
4171         }
4172         return hidden;
4173     }
4174 
4175     /**
4176      * Find the topmost view under the given point.
4177      *
4178      * @param x Horizontal position in pixels to search
4179      * @param y Vertical position in pixels to search
4180      * @return The child view under (x, y) or null if no matching child is found
4181      */
findChildViewUnder(float x, float y)4182     public View findChildViewUnder(float x, float y) {
4183         final int count = mChildHelper.getChildCount();
4184         for (int i = count - 1; i >= 0; i--) {
4185             final View child = mChildHelper.getChildAt(i);
4186             final float translationX = ViewCompat.getTranslationX(child);
4187             final float translationY = ViewCompat.getTranslationY(child);
4188             if (x >= child.getLeft() + translationX &&
4189                     x <= child.getRight() + translationX &&
4190                     y >= child.getTop() + translationY &&
4191                     y <= child.getBottom() + translationY) {
4192                 return child;
4193             }
4194         }
4195         return null;
4196     }
4197 
4198     @Override
drawChild(Canvas canvas, View child, long drawingTime)4199     public boolean drawChild(Canvas canvas, View child, long drawingTime) {
4200         return super.drawChild(canvas, child, drawingTime);
4201     }
4202 
4203     /**
4204      * Offset the bounds of all child views by <code>dy</code> pixels.
4205      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4206      *
4207      * @param dy Vertical pixel offset to apply to the bounds of all child views
4208      */
offsetChildrenVertical(int dy)4209     public void offsetChildrenVertical(int dy) {
4210         final int childCount = mChildHelper.getChildCount();
4211         for (int i = 0; i < childCount; i++) {
4212             mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
4213         }
4214     }
4215 
4216     /**
4217      * Called when an item view is attached to this RecyclerView.
4218      *
4219      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4220      * of child views as they become attached. This will be called before a
4221      * {@link LayoutManager} measures or lays out the view and is a good time to perform these
4222      * changes.</p>
4223      *
4224      * @param child Child view that is now attached to this RecyclerView and its associated window
4225      */
onChildAttachedToWindow(View child)4226     public void onChildAttachedToWindow(View child) {
4227     }
4228 
4229     /**
4230      * Called when an item view is detached from this RecyclerView.
4231      *
4232      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4233      * of child views as they become detached. This will be called as a
4234      * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
4235      *
4236      * @param child Child view that is now detached from this RecyclerView and its associated window
4237      */
onChildDetachedFromWindow(View child)4238     public void onChildDetachedFromWindow(View child) {
4239     }
4240 
4241     /**
4242      * Offset the bounds of all child views by <code>dx</code> pixels.
4243      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4244      *
4245      * @param dx Horizontal pixel offset to apply to the bounds of all child views
4246      */
offsetChildrenHorizontal(int dx)4247     public void offsetChildrenHorizontal(int dx) {
4248         final int childCount = mChildHelper.getChildCount();
4249         for (int i = 0; i < childCount; i++) {
4250             mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
4251         }
4252     }
4253 
4254     /**
4255      * Returns the bounds of the view including its decoration and margins.
4256      *
4257      * @param view The view element to check
4258      * @param outBounds A rect that will receive the bounds of the element including its
4259      *                  decoration and margins.
4260      */
getDecoratedBoundsWithMargins(View view, Rect outBounds)4261     public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
4262         getDecoratedBoundsWithMarginsInt(view, outBounds);
4263     }
4264 
getDecoratedBoundsWithMarginsInt(View view, Rect outBounds)4265     static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
4266         final LayoutParams lp = (LayoutParams) view.getLayoutParams();
4267         final Rect insets = lp.mDecorInsets;
4268         outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
4269                 view.getTop() - insets.top - lp.topMargin,
4270                 view.getRight() + insets.right + lp.rightMargin,
4271                 view.getBottom() + insets.bottom + lp.bottomMargin);
4272     }
4273 
getItemDecorInsetsForChild(View child)4274     Rect getItemDecorInsetsForChild(View child) {
4275         final LayoutParams lp = (LayoutParams) child.getLayoutParams();
4276         if (!lp.mInsetsDirty) {
4277             return lp.mDecorInsets;
4278         }
4279 
4280         if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
4281             // changed/invalid items should not be updated until they are rebound.
4282             return lp.mDecorInsets;
4283         }
4284         final Rect insets = lp.mDecorInsets;
4285         insets.set(0, 0, 0, 0);
4286         final int decorCount = mItemDecorations.size();
4287         for (int i = 0; i < decorCount; i++) {
4288             mTempRect.set(0, 0, 0, 0);
4289             mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
4290             insets.left += mTempRect.left;
4291             insets.top += mTempRect.top;
4292             insets.right += mTempRect.right;
4293             insets.bottom += mTempRect.bottom;
4294         }
4295         lp.mInsetsDirty = false;
4296         return insets;
4297     }
4298 
4299     /**
4300      * Called when the scroll position of this RecyclerView changes. Subclasses should use
4301      * this method to respond to scrolling within the adapter's data set instead of an explicit
4302      * listener.
4303      *
4304      * <p>This method will always be invoked before listeners. If a subclass needs to perform
4305      * any additional upkeep or bookkeeping after scrolling but before listeners run,
4306      * this is a good place to do so.</p>
4307      *
4308      * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
4309      * the distance scrolled in either direction within the adapter's data set instead of absolute
4310      * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
4311      * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
4312      * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
4313      * do not correspond to the data set scroll position. However, some subclasses may choose
4314      * to use these fields as special offsets.</p>
4315      *
4316      * @param dx horizontal distance scrolled in pixels
4317      * @param dy vertical distance scrolled in pixels
4318      */
onScrolled(int dx, int dy)4319     public void onScrolled(int dx, int dy) {
4320         // Do nothing
4321     }
4322 
dispatchOnScrolled(int hresult, int vresult)4323     void dispatchOnScrolled(int hresult, int vresult) {
4324         mDispatchScrollCounter ++;
4325         // Pass the current scrollX/scrollY values; no actual change in these properties occurred
4326         // but some general-purpose code may choose to respond to changes this way.
4327         final int scrollX = getScrollX();
4328         final int scrollY = getScrollY();
4329         onScrollChanged(scrollX, scrollY, scrollX, scrollY);
4330 
4331         // Pass the real deltas to onScrolled, the RecyclerView-specific method.
4332         onScrolled(hresult, vresult);
4333 
4334         // Invoke listeners last. Subclassed view methods always handle the event first.
4335         // All internal state is consistent by the time listeners are invoked.
4336         if (mScrollListener != null) {
4337             mScrollListener.onScrolled(this, hresult, vresult);
4338         }
4339         if (mScrollListeners != null) {
4340             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4341                 mScrollListeners.get(i).onScrolled(this, hresult, vresult);
4342             }
4343         }
4344         mDispatchScrollCounter --;
4345     }
4346 
4347     /**
4348      * Called when the scroll state of this RecyclerView changes. Subclasses should use this
4349      * method to respond to state changes instead of an explicit listener.
4350      *
4351      * <p>This method will always be invoked before listeners, but after the LayoutManager
4352      * responds to the scroll state change.</p>
4353      *
4354      * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
4355      *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
4356      */
onScrollStateChanged(int state)4357     public void onScrollStateChanged(int state) {
4358         // Do nothing
4359     }
4360 
dispatchOnScrollStateChanged(int state)4361     void dispatchOnScrollStateChanged(int state) {
4362         // Let the LayoutManager go first; this allows it to bring any properties into
4363         // a consistent state before the RecyclerView subclass responds.
4364         if (mLayout != null) {
4365             mLayout.onScrollStateChanged(state);
4366         }
4367 
4368         // Let the RecyclerView subclass handle this event next; any LayoutManager property
4369         // changes will be reflected by this time.
4370         onScrollStateChanged(state);
4371 
4372         // Listeners go last. All other internal state is consistent by this point.
4373         if (mScrollListener != null) {
4374             mScrollListener.onScrollStateChanged(this, state);
4375         }
4376         if (mScrollListeners != null) {
4377             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4378                 mScrollListeners.get(i).onScrollStateChanged(this, state);
4379             }
4380         }
4381     }
4382 
4383     /**
4384      * Returns whether there are pending adapter updates which are not yet applied to the layout.
4385      * <p>
4386      * If this method returns <code>true</code>, it means that what user is currently seeing may not
4387      * reflect them adapter contents (depending on what has changed).
4388      * You may use this information to defer or cancel some operations.
4389      * <p>
4390      * This method returns true if RecyclerView has not yet calculated the first layout after it is
4391      * attached to the Window or the Adapter has been replaced.
4392      *
4393      * @return True if there are some adapter updates which are not yet reflected to layout or false
4394      * if layout is up to date.
4395      */
hasPendingAdapterUpdates()4396     public boolean hasPendingAdapterUpdates() {
4397         return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
4398                 || mAdapterHelper.hasPendingUpdates();
4399     }
4400 
4401     private class ViewFlinger implements Runnable {
4402         private int mLastFlingX;
4403         private int mLastFlingY;
4404         private ScrollerCompat mScroller;
4405         private Interpolator mInterpolator = sQuinticInterpolator;
4406 
4407 
4408         // When set to true, postOnAnimation callbacks are delayed until the run method completes
4409         private boolean mEatRunOnAnimationRequest = false;
4410 
4411         // Tracks if postAnimationCallback should be re-attached when it is done
4412         private boolean mReSchedulePostAnimationCallback = false;
4413 
ViewFlinger()4414         public ViewFlinger() {
4415             mScroller = ScrollerCompat.create(getContext(), sQuinticInterpolator);
4416         }
4417 
4418         @Override
run()4419         public void run() {
4420             if (mLayout == null) {
4421                 stop();
4422                 return; // no layout, cannot scroll.
4423             }
4424             disableRunOnAnimationRequests();
4425             consumePendingUpdateOperations();
4426             // keep a local reference so that if it is changed during onAnimation method, it won't
4427             // cause unexpected behaviors
4428             final ScrollerCompat scroller = mScroller;
4429             final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
4430             if (scroller.computeScrollOffset()) {
4431                 final int x = scroller.getCurrX();
4432                 final int y = scroller.getCurrY();
4433                 final int dx = x - mLastFlingX;
4434                 final int dy = y - mLastFlingY;
4435                 int hresult = 0;
4436                 int vresult = 0;
4437                 mLastFlingX = x;
4438                 mLastFlingY = y;
4439                 int overscrollX = 0, overscrollY = 0;
4440                 if (mAdapter != null) {
4441                     eatRequestLayout();
4442                     onEnterLayoutOrScroll();
4443                     TraceCompat.beginSection(TRACE_SCROLL_TAG);
4444                     if (dx != 0) {
4445                         hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
4446                         overscrollX = dx - hresult;
4447                     }
4448                     if (dy != 0) {
4449                         vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
4450                         overscrollY = dy - vresult;
4451                     }
4452                     TraceCompat.endSection();
4453                     repositionShadowingViews();
4454 
4455                     onExitLayoutOrScroll();
4456                     resumeRequestLayout(false);
4457 
4458                     if (smoothScroller != null && !smoothScroller.isPendingInitialRun() &&
4459                             smoothScroller.isRunning()) {
4460                         final int adapterSize = mState.getItemCount();
4461                         if (adapterSize == 0) {
4462                             smoothScroller.stop();
4463                         } else if (smoothScroller.getTargetPosition() >= adapterSize) {
4464                             smoothScroller.setTargetPosition(adapterSize - 1);
4465                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4466                         } else {
4467                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4468                         }
4469                     }
4470                 }
4471                 if (!mItemDecorations.isEmpty()) {
4472                     invalidate();
4473                 }
4474                 if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
4475                     considerReleasingGlowsOnScroll(dx, dy);
4476                 }
4477                 if (overscrollX != 0 || overscrollY != 0) {
4478                     final int vel = (int) scroller.getCurrVelocity();
4479 
4480                     int velX = 0;
4481                     if (overscrollX != x) {
4482                         velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
4483                     }
4484 
4485                     int velY = 0;
4486                     if (overscrollY != y) {
4487                         velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
4488                     }
4489 
4490                     if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
4491                         absorbGlows(velX, velY);
4492                     }
4493                     if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0) &&
4494                             (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
4495                         scroller.abortAnimation();
4496                     }
4497                 }
4498                 if (hresult != 0 || vresult != 0) {
4499                     dispatchOnScrolled(hresult, vresult);
4500                 }
4501 
4502                 if (!awakenScrollBars()) {
4503                     invalidate();
4504                 }
4505 
4506                 final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
4507                         && vresult == dy;
4508                 final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
4509                         && hresult == dx;
4510                 final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
4511                         || fullyConsumedVertical;
4512 
4513                 if (scroller.isFinished() || !fullyConsumedAny) {
4514                     setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this.
4515                 } else {
4516                     postOnAnimation();
4517                 }
4518             }
4519             // call this after the onAnimation is complete not to have inconsistent callbacks etc.
4520             if (smoothScroller != null) {
4521                 if (smoothScroller.isPendingInitialRun()) {
4522                     smoothScroller.onAnimation(0, 0);
4523                 }
4524                 if (!mReSchedulePostAnimationCallback) {
4525                     smoothScroller.stop(); //stop if it does not trigger any scroll
4526                 }
4527             }
4528             enableRunOnAnimationRequests();
4529         }
4530 
disableRunOnAnimationRequests()4531         private void disableRunOnAnimationRequests() {
4532             mReSchedulePostAnimationCallback = false;
4533             mEatRunOnAnimationRequest = true;
4534         }
4535 
enableRunOnAnimationRequests()4536         private void enableRunOnAnimationRequests() {
4537             mEatRunOnAnimationRequest = false;
4538             if (mReSchedulePostAnimationCallback) {
4539                 postOnAnimation();
4540             }
4541         }
4542 
postOnAnimation()4543         void postOnAnimation() {
4544             if (mEatRunOnAnimationRequest) {
4545                 mReSchedulePostAnimationCallback = true;
4546             } else {
4547                 removeCallbacks(this);
4548                 ViewCompat.postOnAnimation(RecyclerView.this, this);
4549             }
4550         }
4551 
fling(int velocityX, int velocityY)4552         public void fling(int velocityX, int velocityY) {
4553             setScrollState(SCROLL_STATE_SETTLING);
4554             mLastFlingX = mLastFlingY = 0;
4555             mScroller.fling(0, 0, velocityX, velocityY,
4556                     Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
4557             postOnAnimation();
4558         }
4559 
smoothScrollBy(int dx, int dy)4560         public void smoothScrollBy(int dx, int dy) {
4561             smoothScrollBy(dx, dy, 0, 0);
4562         }
4563 
smoothScrollBy(int dx, int dy, int vx, int vy)4564         public void smoothScrollBy(int dx, int dy, int vx, int vy) {
4565             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
4566         }
4567 
distanceInfluenceForSnapDuration(float f)4568         private float distanceInfluenceForSnapDuration(float f) {
4569             f -= 0.5f; // center the values about 0.
4570             f *= 0.3f * Math.PI / 2.0f;
4571             return (float) Math.sin(f);
4572         }
4573 
computeScrollDuration(int dx, int dy, int vx, int vy)4574         private int computeScrollDuration(int dx, int dy, int vx, int vy) {
4575             final int absDx = Math.abs(dx);
4576             final int absDy = Math.abs(dy);
4577             final boolean horizontal = absDx > absDy;
4578             final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
4579             final int delta = (int) Math.sqrt(dx * dx + dy * dy);
4580             final int containerSize = horizontal ? getWidth() : getHeight();
4581             final int halfContainerSize = containerSize / 2;
4582             final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
4583             final float distance = halfContainerSize + halfContainerSize *
4584                     distanceInfluenceForSnapDuration(distanceRatio);
4585 
4586             final int duration;
4587             if (velocity > 0) {
4588                 duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
4589             } else {
4590                 float absDelta = (float) (horizontal ? absDx : absDy);
4591                 duration = (int) (((absDelta / containerSize) + 1) * 300);
4592             }
4593             return Math.min(duration, MAX_SCROLL_DURATION);
4594         }
4595 
smoothScrollBy(int dx, int dy, int duration)4596         public void smoothScrollBy(int dx, int dy, int duration) {
4597             smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
4598         }
4599 
smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator)4600         public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
4601             if (mInterpolator != interpolator) {
4602                 mInterpolator = interpolator;
4603                 mScroller = ScrollerCompat.create(getContext(), interpolator);
4604             }
4605             setScrollState(SCROLL_STATE_SETTLING);
4606             mLastFlingX = mLastFlingY = 0;
4607             mScroller.startScroll(0, 0, dx, dy, duration);
4608             postOnAnimation();
4609         }
4610 
stop()4611         public void stop() {
4612             removeCallbacks(this);
4613             mScroller.abortAnimation();
4614         }
4615 
4616     }
4617 
repositionShadowingViews()4618     private void repositionShadowingViews() {
4619         // Fix up shadow views used by change animations
4620         int count = mChildHelper.getChildCount();
4621         for (int i = 0; i < count; i++) {
4622             View view = mChildHelper.getChildAt(i);
4623             ViewHolder holder = getChildViewHolder(view);
4624             if (holder != null && holder.mShadowingHolder != null) {
4625                 View shadowingView = holder.mShadowingHolder.itemView;
4626                 int left = view.getLeft();
4627                 int top = view.getTop();
4628                 if (left != shadowingView.getLeft() ||
4629                         top != shadowingView.getTop()) {
4630                     shadowingView.layout(left, top,
4631                             left + shadowingView.getWidth(),
4632                             top + shadowingView.getHeight());
4633                 }
4634             }
4635         }
4636     }
4637 
4638     private class RecyclerViewDataObserver extends AdapterDataObserver {
4639         @Override
onChanged()4640         public void onChanged() {
4641             assertNotInLayoutOrScroll(null);
4642             if (mAdapter.hasStableIds()) {
4643                 // TODO Determine what actually changed.
4644                 // This is more important to implement now since this callback will disable all
4645                 // animations because we cannot rely on positions.
4646                 mState.mStructureChanged = true;
4647                 setDataSetChangedAfterLayout();
4648             } else {
4649                 mState.mStructureChanged = true;
4650                 setDataSetChangedAfterLayout();
4651             }
4652             if (!mAdapterHelper.hasPendingUpdates()) {
4653                 requestLayout();
4654             }
4655         }
4656 
4657         @Override
onItemRangeChanged(int positionStart, int itemCount, Object payload)4658         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
4659             assertNotInLayoutOrScroll(null);
4660             if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
4661                 triggerUpdateProcessor();
4662             }
4663         }
4664 
4665         @Override
onItemRangeInserted(int positionStart, int itemCount)4666         public void onItemRangeInserted(int positionStart, int itemCount) {
4667             assertNotInLayoutOrScroll(null);
4668             if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
4669                 triggerUpdateProcessor();
4670             }
4671         }
4672 
4673         @Override
onItemRangeRemoved(int positionStart, int itemCount)4674         public void onItemRangeRemoved(int positionStart, int itemCount) {
4675             assertNotInLayoutOrScroll(null);
4676             if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
4677                 triggerUpdateProcessor();
4678             }
4679         }
4680 
4681         @Override
onItemRangeMoved(int fromPosition, int toPosition, int itemCount)4682         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
4683             assertNotInLayoutOrScroll(null);
4684             if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
4685                 triggerUpdateProcessor();
4686             }
4687         }
4688 
triggerUpdateProcessor()4689         void triggerUpdateProcessor() {
4690             if (mPostUpdatesOnAnimation && mHasFixedSize && mIsAttached) {
4691                 ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
4692             } else {
4693                 mAdapterUpdateDuringMeasure = true;
4694                 requestLayout();
4695             }
4696         }
4697     }
4698 
4699     /**
4700      * RecycledViewPool lets you share Views between multiple RecyclerViews.
4701      * <p>
4702      * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
4703      * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
4704      * <p>
4705      * RecyclerView automatically creates a pool for itself if you don't provide one.
4706      *
4707      */
4708     public static class RecycledViewPool {
4709         private SparseArray<ArrayList<ViewHolder>> mScrap =
4710                 new SparseArray<ArrayList<ViewHolder>>();
4711         private SparseIntArray mMaxScrap = new SparseIntArray();
4712         private int mAttachCount = 0;
4713 
4714         private static final int DEFAULT_MAX_SCRAP = 5;
4715 
clear()4716         public void clear() {
4717             mScrap.clear();
4718         }
4719 
setMaxRecycledViews(int viewType, int max)4720         public void setMaxRecycledViews(int viewType, int max) {
4721             mMaxScrap.put(viewType, max);
4722             final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
4723             if (scrapHeap != null) {
4724                 while (scrapHeap.size() > max) {
4725                     scrapHeap.remove(scrapHeap.size() - 1);
4726                 }
4727             }
4728         }
4729 
getRecycledView(int viewType)4730         public ViewHolder getRecycledView(int viewType) {
4731             final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
4732             if (scrapHeap != null && !scrapHeap.isEmpty()) {
4733                 final int index = scrapHeap.size() - 1;
4734                 final ViewHolder scrap = scrapHeap.get(index);
4735                 scrapHeap.remove(index);
4736                 return scrap;
4737             }
4738             return null;
4739         }
4740 
size()4741         int size() {
4742             int count = 0;
4743             for (int i = 0; i < mScrap.size(); i ++) {
4744                 ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i);
4745                 if (viewHolders != null) {
4746                     count += viewHolders.size();
4747                 }
4748             }
4749             return count;
4750         }
4751 
putRecycledView(ViewHolder scrap)4752         public void putRecycledView(ViewHolder scrap) {
4753             final int viewType = scrap.getItemViewType();
4754             final ArrayList scrapHeap = getScrapHeapForType(viewType);
4755             if (mMaxScrap.get(viewType) <= scrapHeap.size()) {
4756                 return;
4757             }
4758             if (DEBUG && scrapHeap.contains(scrap)) {
4759                 throw new IllegalArgumentException("this scrap item already exists");
4760             }
4761             scrap.resetInternal();
4762             scrapHeap.add(scrap);
4763         }
4764 
attach(Adapter adapter)4765         void attach(Adapter adapter) {
4766             mAttachCount++;
4767         }
4768 
detach()4769         void detach() {
4770             mAttachCount--;
4771         }
4772 
4773 
4774         /**
4775          * Detaches the old adapter and attaches the new one.
4776          * <p>
4777          * RecycledViewPool will clear its cache if it has only one adapter attached and the new
4778          * adapter uses a different ViewHolder than the oldAdapter.
4779          *
4780          * @param oldAdapter The previous adapter instance. Will be detached.
4781          * @param newAdapter The new adapter instance. Will be attached.
4782          * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
4783          *                               ViewHolder and view types.
4784          */
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, boolean compatibleWithPrevious)4785         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
4786                 boolean compatibleWithPrevious) {
4787             if (oldAdapter != null) {
4788                 detach();
4789             }
4790             if (!compatibleWithPrevious && mAttachCount == 0) {
4791                 clear();
4792             }
4793             if (newAdapter != null) {
4794                 attach(newAdapter);
4795             }
4796         }
4797 
getScrapHeapForType(int viewType)4798         private ArrayList<ViewHolder> getScrapHeapForType(int viewType) {
4799             ArrayList<ViewHolder> scrap = mScrap.get(viewType);
4800             if (scrap == null) {
4801                 scrap = new ArrayList<>();
4802                 mScrap.put(viewType, scrap);
4803                 if (mMaxScrap.indexOfKey(viewType) < 0) {
4804                     mMaxScrap.put(viewType, DEFAULT_MAX_SCRAP);
4805                 }
4806             }
4807             return scrap;
4808         }
4809     }
4810 
4811     /**
4812      * A Recycler is responsible for managing scrapped or detached item views for reuse.
4813      *
4814      * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
4815      * that has been marked for removal or reuse.</p>
4816      *
4817      * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
4818      * an adapter's data set representing the data at a given position or item ID.
4819      * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
4820      * If not, the view can be quickly reused by the LayoutManager with no further work.
4821      * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
4822      * may be repositioned by a LayoutManager without remeasurement.</p>
4823      */
4824     public final class Recycler {
4825         final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
4826         private ArrayList<ViewHolder> mChangedScrap = null;
4827 
4828         final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
4829 
4830         private final List<ViewHolder>
4831                 mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
4832 
4833         private int mViewCacheMax = DEFAULT_CACHE_SIZE;
4834 
4835         private RecycledViewPool mRecyclerPool;
4836 
4837         private ViewCacheExtension mViewCacheExtension;
4838 
4839         private static final int DEFAULT_CACHE_SIZE = 2;
4840 
4841         /**
4842          * Clear scrap views out of this recycler. Detached views contained within a
4843          * recycled view pool will remain.
4844          */
clear()4845         public void clear() {
4846             mAttachedScrap.clear();
4847             recycleAndClearCachedViews();
4848         }
4849 
4850         /**
4851          * Set the maximum number of detached, valid views we should retain for later use.
4852          *
4853          * @param viewCount Number of views to keep before sending views to the shared pool
4854          */
setViewCacheSize(int viewCount)4855         public void setViewCacheSize(int viewCount) {
4856             mViewCacheMax = viewCount;
4857             // first, try the views that can be recycled
4858             for (int i = mCachedViews.size() - 1; i >= 0 && mCachedViews.size() > viewCount; i--) {
4859                 recycleCachedViewAt(i);
4860             }
4861         }
4862 
4863         /**
4864          * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
4865          *
4866          * @return List of ViewHolders in the scrap list.
4867          */
getScrapList()4868         public List<ViewHolder> getScrapList() {
4869             return mUnmodifiableAttachedScrap;
4870         }
4871 
4872         /**
4873          * Helper method for getViewForPosition.
4874          * <p>
4875          * Checks whether a given view holder can be used for the provided position.
4876          *
4877          * @param holder ViewHolder
4878          * @return true if ViewHolder matches the provided position, false otherwise
4879          */
validateViewHolderForOffsetPosition(ViewHolder holder)4880         boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
4881             // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
4882             // if it is not removed, verify the type and id.
4883             if (holder.isRemoved()) {
4884                 if (DEBUG && !mState.isPreLayout()) {
4885                     throw new IllegalStateException("should not receive a removed view unless it"
4886                             + " is pre layout");
4887                 }
4888                 return mState.isPreLayout();
4889             }
4890             if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
4891                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
4892                         + "adapter position" + holder);
4893             }
4894             if (!mState.isPreLayout()) {
4895                 // don't check type if it is pre-layout.
4896                 final int type = mAdapter.getItemViewType(holder.mPosition);
4897                 if (type != holder.getItemViewType()) {
4898                     return false;
4899                 }
4900             }
4901             if (mAdapter.hasStableIds()) {
4902                 return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
4903             }
4904             return true;
4905         }
4906 
4907         /**
4908          * Binds the given View to the position. The View can be a View previously retrieved via
4909          * {@link #getViewForPosition(int)} or created by
4910          * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
4911          * <p>
4912          * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
4913          * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
4914          * wants to handle its own recycling logic.
4915          * <p>
4916          * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
4917          * you don't need to call this method unless you want to bind this View to another position.
4918          *
4919          * @param view The view to update.
4920          * @param position The position of the item to bind to this View.
4921          */
bindViewToPosition(View view, int position)4922         public void bindViewToPosition(View view, int position) {
4923             ViewHolder holder = getChildViewHolderInt(view);
4924             if (holder == null) {
4925                 throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
4926                         + " pass arbitrary views to this method, they should be created by the "
4927                         + "Adapter");
4928             }
4929             final int offsetPosition = mAdapterHelper.findPositionOffset(position);
4930             if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
4931                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
4932                         + "position " + position + "(offset:" + offsetPosition + ")."
4933                         + "state:" + mState.getItemCount());
4934             }
4935             holder.mOwnerRecyclerView = RecyclerView.this;
4936             mAdapter.bindViewHolder(holder, offsetPosition);
4937             attachAccessibilityDelegate(view);
4938             if (mState.isPreLayout()) {
4939                 holder.mPreLayoutPosition = position;
4940             }
4941 
4942             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
4943             final LayoutParams rvLayoutParams;
4944             if (lp == null) {
4945                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
4946                 holder.itemView.setLayoutParams(rvLayoutParams);
4947             } else if (!checkLayoutParams(lp)) {
4948                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
4949                 holder.itemView.setLayoutParams(rvLayoutParams);
4950             } else {
4951                 rvLayoutParams = (LayoutParams) lp;
4952             }
4953 
4954             rvLayoutParams.mInsetsDirty = true;
4955             rvLayoutParams.mViewHolder = holder;
4956             rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
4957         }
4958 
4959         /**
4960          * RecyclerView provides artificial position range (item count) in pre-layout state and
4961          * automatically maps these positions to {@link Adapter} positions when
4962          * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
4963          * <p>
4964          * Usually, LayoutManager does not need to worry about this. However, in some cases, your
4965          * LayoutManager may need to call some custom component with item positions in which
4966          * case you need the actual adapter position instead of the pre layout position. You
4967          * can use this method to convert a pre-layout position to adapter (post layout) position.
4968          * <p>
4969          * Note that if the provided position belongs to a deleted ViewHolder, this method will
4970          * return -1.
4971          * <p>
4972          * Calling this method in post-layout state returns the same value back.
4973          *
4974          * @param position The pre-layout position to convert. Must be greater or equal to 0 and
4975          *                 less than {@link State#getItemCount()}.
4976          */
convertPreLayoutPositionToPostLayout(int position)4977         public int convertPreLayoutPositionToPostLayout(int position) {
4978             if (position < 0 || position >= mState.getItemCount()) {
4979                 throw new IndexOutOfBoundsException("invalid position " + position + ". State "
4980                         + "item count is " + mState.getItemCount());
4981             }
4982             if (!mState.isPreLayout()) {
4983                 return position;
4984             }
4985             return mAdapterHelper.findPositionOffset(position);
4986         }
4987 
4988         /**
4989          * Obtain a view initialized for the given position.
4990          *
4991          * This method should be used by {@link LayoutManager} implementations to obtain
4992          * views to represent data from an {@link Adapter}.
4993          * <p>
4994          * The Recycler may reuse a scrap or detached view from a shared pool if one is
4995          * available for the correct view type. If the adapter has not indicated that the
4996          * data at the given position has changed, the Recycler will attempt to hand back
4997          * a scrap view that was previously initialized for that data without rebinding.
4998          *
4999          * @param position Position to obtain a view for
5000          * @return A view representing the data at <code>position</code> from <code>adapter</code>
5001          */
getViewForPosition(int position)5002         public View getViewForPosition(int position) {
5003             return getViewForPosition(position, false);
5004         }
5005 
getViewForPosition(int position, boolean dryRun)5006         View getViewForPosition(int position, boolean dryRun) {
5007             if (position < 0 || position >= mState.getItemCount()) {
5008                 throw new IndexOutOfBoundsException("Invalid item position " + position
5009                         + "(" + position + "). Item count:" + mState.getItemCount());
5010             }
5011             boolean fromScrap = false;
5012             ViewHolder holder = null;
5013             // 0) If there is a changed scrap, try to find from there
5014             if (mState.isPreLayout()) {
5015                 holder = getChangedScrapViewForPosition(position);
5016                 fromScrap = holder != null;
5017             }
5018             // 1) Find from scrap by position
5019             if (holder == null) {
5020                 holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);
5021                 if (holder != null) {
5022                     if (!validateViewHolderForOffsetPosition(holder)) {
5023                         // recycle this scrap
5024                         if (!dryRun) {
5025                             // we would like to recycle this but need to make sure it is not used by
5026                             // animation logic etc.
5027                             holder.addFlags(ViewHolder.FLAG_INVALID);
5028                             if (holder.isScrap()) {
5029                                 removeDetachedView(holder.itemView, false);
5030                                 holder.unScrap();
5031                             } else if (holder.wasReturnedFromScrap()) {
5032                                 holder.clearReturnedFromScrapFlag();
5033                             }
5034                             recycleViewHolderInternal(holder);
5035                         }
5036                         holder = null;
5037                     } else {
5038                         fromScrap = true;
5039                     }
5040                 }
5041             }
5042             if (holder == null) {
5043                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5044                 if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5045                     throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5046                             + "position " + position + "(offset:" + offsetPosition + ")."
5047                             + "state:" + mState.getItemCount());
5048                 }
5049 
5050                 final int type = mAdapter.getItemViewType(offsetPosition);
5051                 // 2) Find from scrap via stable ids, if exists
5052                 if (mAdapter.hasStableIds()) {
5053                     holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
5054                     if (holder != null) {
5055                         // update position
5056                         holder.mPosition = offsetPosition;
5057                         fromScrap = true;
5058                     }
5059                 }
5060                 if (holder == null && mViewCacheExtension != null) {
5061                     // We are NOT sending the offsetPosition because LayoutManager does not
5062                     // know it.
5063                     final View view = mViewCacheExtension
5064                             .getViewForPositionAndType(this, position, type);
5065                     if (view != null) {
5066                         holder = getChildViewHolder(view);
5067                         if (holder == null) {
5068                             throw new IllegalArgumentException("getViewForPositionAndType returned"
5069                                     + " a view which does not have a ViewHolder");
5070                         } else if (holder.shouldIgnore()) {
5071                             throw new IllegalArgumentException("getViewForPositionAndType returned"
5072                                     + " a view that is ignored. You must call stopIgnoring before"
5073                                     + " returning this view.");
5074                         }
5075                     }
5076                 }
5077                 if (holder == null) { // fallback to recycler
5078                     // try recycler.
5079                     // Head to the shared pool.
5080                     if (DEBUG) {
5081                         Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "
5082                                 + "pool");
5083                     }
5084                     holder = getRecycledViewPool().getRecycledView(type);
5085                     if (holder != null) {
5086                         holder.resetInternal();
5087                         if (FORCE_INVALIDATE_DISPLAY_LIST) {
5088                             invalidateDisplayListInt(holder);
5089                         }
5090                     }
5091                 }
5092                 if (holder == null) {
5093                     holder = mAdapter.createViewHolder(RecyclerView.this, type);
5094                     if (DEBUG) {
5095                         Log.d(TAG, "getViewForPosition created new ViewHolder");
5096                     }
5097                 }
5098             }
5099 
5100             // This is very ugly but the only place we can grab this information
5101             // before the View is rebound and returned to the LayoutManager for post layout ops.
5102             // We don't need this in pre-layout since the VH is not updated by the LM.
5103             if (fromScrap && !mState.isPreLayout() && holder
5104                     .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
5105                 holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5106                 if (mState.mRunSimpleAnimations) {
5107                     int changeFlags = ItemAnimator
5108                             .buildAdapterChangeFlagsForAnimations(holder);
5109                     changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
5110                     final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
5111                             holder, changeFlags, holder.getUnmodifiedPayloads());
5112                     recordAnimationInfoIfBouncedHiddenView(holder, info);
5113                 }
5114             }
5115 
5116             boolean bound = false;
5117             if (mState.isPreLayout() && holder.isBound()) {
5118                 // do not update unless we absolutely have to.
5119                 holder.mPreLayoutPosition = position;
5120             } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
5121                 if (DEBUG && holder.isRemoved()) {
5122                     throw new IllegalStateException("Removed holder should be bound and it should"
5123                             + " come here only in pre-layout. Holder: " + holder);
5124                 }
5125                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5126                 holder.mOwnerRecyclerView = RecyclerView.this;
5127                 mAdapter.bindViewHolder(holder, offsetPosition);
5128                 attachAccessibilityDelegate(holder.itemView);
5129                 bound = true;
5130                 if (mState.isPreLayout()) {
5131                     holder.mPreLayoutPosition = position;
5132                 }
5133             }
5134 
5135             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5136             final LayoutParams rvLayoutParams;
5137             if (lp == null) {
5138                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5139                 holder.itemView.setLayoutParams(rvLayoutParams);
5140             } else if (!checkLayoutParams(lp)) {
5141                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5142                 holder.itemView.setLayoutParams(rvLayoutParams);
5143             } else {
5144                 rvLayoutParams = (LayoutParams) lp;
5145             }
5146             rvLayoutParams.mViewHolder = holder;
5147             rvLayoutParams.mPendingInvalidate = fromScrap && bound;
5148             return holder.itemView;
5149         }
5150 
attachAccessibilityDelegate(View itemView)5151         private void attachAccessibilityDelegate(View itemView) {
5152             if (isAccessibilityEnabled()) {
5153                 if (ViewCompat.getImportantForAccessibility(itemView) ==
5154                         ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
5155                     ViewCompat.setImportantForAccessibility(itemView,
5156                             ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
5157                 }
5158                 if (!ViewCompat.hasAccessibilityDelegate(itemView)) {
5159                     ViewCompat.setAccessibilityDelegate(itemView,
5160                             mAccessibilityDelegate.getItemDelegate());
5161                 }
5162             }
5163         }
5164 
invalidateDisplayListInt(ViewHolder holder)5165         private void invalidateDisplayListInt(ViewHolder holder) {
5166             if (holder.itemView instanceof ViewGroup) {
5167                 invalidateDisplayListInt((ViewGroup) holder.itemView, false);
5168             }
5169         }
5170 
invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis)5171         private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
5172             for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
5173                 final View view = viewGroup.getChildAt(i);
5174                 if (view instanceof ViewGroup) {
5175                     invalidateDisplayListInt((ViewGroup) view, true);
5176                 }
5177             }
5178             if (!invalidateThis) {
5179                 return;
5180             }
5181             // we need to force it to become invisible
5182             if (viewGroup.getVisibility() == View.INVISIBLE) {
5183                 viewGroup.setVisibility(View.VISIBLE);
5184                 viewGroup.setVisibility(View.INVISIBLE);
5185             } else {
5186                 final int visibility = viewGroup.getVisibility();
5187                 viewGroup.setVisibility(View.INVISIBLE);
5188                 viewGroup.setVisibility(visibility);
5189             }
5190         }
5191 
5192         /**
5193          * Recycle a detached view. The specified view will be added to a pool of views
5194          * for later rebinding and reuse.
5195          *
5196          * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
5197          * View is scrapped, it will be removed from scrap list.</p>
5198          *
5199          * @param view Removed view for recycling
5200          * @see LayoutManager#removeAndRecycleView(View, Recycler)
5201          */
recycleView(View view)5202         public void recycleView(View view) {
5203             // This public recycle method tries to make view recycle-able since layout manager
5204             // intended to recycle this view (e.g. even if it is in scrap or change cache)
5205             ViewHolder holder = getChildViewHolderInt(view);
5206             if (holder.isTmpDetached()) {
5207                 removeDetachedView(view, false);
5208             }
5209             if (holder.isScrap()) {
5210                 holder.unScrap();
5211             } else if (holder.wasReturnedFromScrap()){
5212                 holder.clearReturnedFromScrapFlag();
5213             }
5214             recycleViewHolderInternal(holder);
5215         }
5216 
5217         /**
5218          * Internally, use this method instead of {@link #recycleView(android.view.View)} to
5219          * catch potential bugs.
5220          * @param view
5221          */
recycleViewInternal(View view)5222         void recycleViewInternal(View view) {
5223             recycleViewHolderInternal(getChildViewHolderInt(view));
5224         }
5225 
recycleAndClearCachedViews()5226         void recycleAndClearCachedViews() {
5227             final int count = mCachedViews.size();
5228             for (int i = count - 1; i >= 0; i--) {
5229                 recycleCachedViewAt(i);
5230             }
5231             mCachedViews.clear();
5232         }
5233 
5234         /**
5235          * Recycles a cached view and removes the view from the list. Views are added to cache
5236          * if and only if they are recyclable, so this method does not check it again.
5237          * <p>
5238          * A small exception to this rule is when the view does not have an animator reference
5239          * but transient state is true (due to animations created outside ItemAnimator). In that
5240          * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
5241          * still recyclable since Adapter wants to do so.
5242          *
5243          * @param cachedViewIndex The index of the view in cached views list
5244          */
recycleCachedViewAt(int cachedViewIndex)5245         void recycleCachedViewAt(int cachedViewIndex) {
5246             if (DEBUG) {
5247                 Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
5248             }
5249             ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
5250             if (DEBUG) {
5251                 Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
5252             }
5253             addViewHolderToRecycledViewPool(viewHolder);
5254             mCachedViews.remove(cachedViewIndex);
5255         }
5256 
5257         /**
5258          * internal implementation checks if view is scrapped or attached and throws an exception
5259          * if so.
5260          * Public version un-scraps before calling recycle.
5261          */
recycleViewHolderInternal(ViewHolder holder)5262         void recycleViewHolderInternal(ViewHolder holder) {
5263             if (holder.isScrap() || holder.itemView.getParent() != null) {
5264                 throw new IllegalArgumentException(
5265                         "Scrapped or attached views may not be recycled. isScrap:"
5266                                 + holder.isScrap() + " isAttached:"
5267                                 + (holder.itemView.getParent() != null));
5268             }
5269 
5270             if (holder.isTmpDetached()) {
5271                 throw new IllegalArgumentException("Tmp detached view should be removed "
5272                         + "from RecyclerView before it can be recycled: " + holder);
5273             }
5274 
5275             if (holder.shouldIgnore()) {
5276                 throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
5277                         + " should first call stopIgnoringView(view) before calling recycle.");
5278             }
5279             //noinspection unchecked
5280             final boolean transientStatePreventsRecycling = holder
5281                     .doesTransientStatePreventRecycling();
5282             final boolean forceRecycle = mAdapter != null
5283                     && transientStatePreventsRecycling
5284                     && mAdapter.onFailedToRecycleView(holder);
5285             boolean cached = false;
5286             boolean recycled = false;
5287             if (DEBUG && mCachedViews.contains(holder)) {
5288                 throw new IllegalArgumentException("cached view received recycle internal? " +
5289                         holder);
5290             }
5291             if (forceRecycle || holder.isRecyclable()) {
5292                 if (!holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED
5293                         | ViewHolder.FLAG_UPDATE)) {
5294                     // Retire oldest cached view
5295                     int cachedViewSize = mCachedViews.size();
5296                     if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
5297                         recycleCachedViewAt(0);
5298                         cachedViewSize --;
5299                     }
5300                     if (cachedViewSize < mViewCacheMax) {
5301                         mCachedViews.add(holder);
5302                         cached = true;
5303                     }
5304                 }
5305                 if (!cached) {
5306                     addViewHolderToRecycledViewPool(holder);
5307                     recycled = true;
5308                 }
5309             } else if (DEBUG) {
5310                 Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
5311                         + "re-visit here. We are still removing it from animation lists");
5312             }
5313             // even if the holder is not removed, we still call this method so that it is removed
5314             // from view holder lists.
5315             mViewInfoStore.removeViewHolder(holder);
5316             if (!cached && !recycled && transientStatePreventsRecycling) {
5317                 holder.mOwnerRecyclerView = null;
5318             }
5319         }
5320 
addViewHolderToRecycledViewPool(ViewHolder holder)5321         void addViewHolderToRecycledViewPool(ViewHolder holder) {
5322             ViewCompat.setAccessibilityDelegate(holder.itemView, null);
5323             dispatchViewRecycled(holder);
5324             holder.mOwnerRecyclerView = null;
5325             getRecycledViewPool().putRecycledView(holder);
5326         }
5327 
5328         /**
5329          * Used as a fast path for unscrapping and recycling a view during a bulk operation.
5330          * The caller must call {@link #clearScrap()} when it's done to update the recycler's
5331          * internal bookkeeping.
5332          */
quickRecycleScrapView(View view)5333         void quickRecycleScrapView(View view) {
5334             final ViewHolder holder = getChildViewHolderInt(view);
5335             holder.mScrapContainer = null;
5336             holder.mInChangeScrap = false;
5337             holder.clearReturnedFromScrapFlag();
5338             recycleViewHolderInternal(holder);
5339         }
5340 
5341         /**
5342          * Mark an attached view as scrap.
5343          *
5344          * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
5345          * for rebinding and reuse. Requests for a view for a given position may return a
5346          * reused or rebound scrap view instance.</p>
5347          *
5348          * @param view View to scrap
5349          */
scrapView(View view)5350         void scrapView(View view) {
5351             final ViewHolder holder = getChildViewHolderInt(view);
5352             if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
5353                     || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
5354                 if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
5355                     throw new IllegalArgumentException("Called scrap view with an invalid view."
5356                             + " Invalid views cannot be reused from scrap, they should rebound from"
5357                             + " recycler pool.");
5358                 }
5359                 holder.setScrapContainer(this, false);
5360                 mAttachedScrap.add(holder);
5361             } else {
5362                 if (mChangedScrap == null) {
5363                     mChangedScrap = new ArrayList<ViewHolder>();
5364                 }
5365                 holder.setScrapContainer(this, true);
5366                 mChangedScrap.add(holder);
5367             }
5368         }
5369 
5370         /**
5371          * Remove a previously scrapped view from the pool of eligible scrap.
5372          *
5373          * <p>This view will no longer be eligible for reuse until re-scrapped or
5374          * until it is explicitly removed and recycled.</p>
5375          */
unscrapView(ViewHolder holder)5376         void unscrapView(ViewHolder holder) {
5377             if (holder.mInChangeScrap) {
5378                 mChangedScrap.remove(holder);
5379             } else {
5380                 mAttachedScrap.remove(holder);
5381             }
5382             holder.mScrapContainer = null;
5383             holder.mInChangeScrap = false;
5384             holder.clearReturnedFromScrapFlag();
5385         }
5386 
getScrapCount()5387         int getScrapCount() {
5388             return mAttachedScrap.size();
5389         }
5390 
getScrapViewAt(int index)5391         View getScrapViewAt(int index) {
5392             return mAttachedScrap.get(index).itemView;
5393         }
5394 
clearScrap()5395         void clearScrap() {
5396             mAttachedScrap.clear();
5397             if (mChangedScrap != null) {
5398                 mChangedScrap.clear();
5399             }
5400         }
5401 
getChangedScrapViewForPosition(int position)5402         ViewHolder getChangedScrapViewForPosition(int position) {
5403             // If pre-layout, check the changed scrap for an exact match.
5404             final int changedScrapSize;
5405             if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
5406                 return null;
5407             }
5408             // find by position
5409             for (int i = 0; i < changedScrapSize; i++) {
5410                 final ViewHolder holder = mChangedScrap.get(i);
5411                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
5412                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5413                     return holder;
5414                 }
5415             }
5416             // find by id
5417             if (mAdapter.hasStableIds()) {
5418                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5419                 if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
5420                     final long id = mAdapter.getItemId(offsetPosition);
5421                     for (int i = 0; i < changedScrapSize; i++) {
5422                         final ViewHolder holder = mChangedScrap.get(i);
5423                         if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
5424                             holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5425                             return holder;
5426                         }
5427                     }
5428                 }
5429             }
5430             return null;
5431         }
5432 
5433         /**
5434          * Returns a scrap view for the position. If type is not INVALID_TYPE, it also checks if
5435          * ViewHolder's type matches the provided type.
5436          *
5437          * @param position Item position
5438          * @param type View type
5439          * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
5440          * @return a ViewHolder that can be re-used for this position.
5441          */
getScrapViewForPosition(int position, int type, boolean dryRun)5442         ViewHolder getScrapViewForPosition(int position, int type, boolean dryRun) {
5443             final int scrapCount = mAttachedScrap.size();
5444 
5445             // Try first for an exact, non-invalid match from scrap.
5446             for (int i = 0; i < scrapCount; i++) {
5447                 final ViewHolder holder = mAttachedScrap.get(i);
5448                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
5449                         && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
5450                     if (type != INVALID_TYPE && holder.getItemViewType() != type) {
5451                         Log.e(TAG, "Scrap view for position " + position + " isn't dirty but has" +
5452                                 " wrong view type! (found " + holder.getItemViewType() +
5453                                 " but expected " + type + ")");
5454                         break;
5455                     }
5456                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5457                     return holder;
5458                 }
5459             }
5460 
5461             if (!dryRun) {
5462                 View view = mChildHelper.findHiddenNonRemovedView(position, type);
5463                 if (view != null) {
5464                     // This View is good to be used. We just need to unhide, detach and move to the
5465                     // scrap list.
5466                     final ViewHolder vh = getChildViewHolderInt(view);
5467                     mChildHelper.unhide(view);
5468                     int layoutIndex = mChildHelper.indexOfChild(view);
5469                     if (layoutIndex == RecyclerView.NO_POSITION) {
5470                         throw new IllegalStateException("layout index should not be -1 after "
5471                                 + "unhiding a view:" + vh);
5472                     }
5473                     mChildHelper.detachViewFromParent(layoutIndex);
5474                     scrapView(view);
5475                     vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
5476                             | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5477                     return vh;
5478                 }
5479             }
5480 
5481             // Search in our first-level recycled view cache.
5482             final int cacheSize = mCachedViews.size();
5483             for (int i = 0; i < cacheSize; i++) {
5484                 final ViewHolder holder = mCachedViews.get(i);
5485                 // invalid view holders may be in cache if adapter has stable ids as they can be
5486                 // retrieved via getScrapViewForId
5487                 if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
5488                     if (!dryRun) {
5489                         mCachedViews.remove(i);
5490                     }
5491                     if (DEBUG) {
5492                         Log.d(TAG, "getScrapViewForPosition(" + position + ", " + type +
5493                                 ") found match in cache: " + holder);
5494                     }
5495                     return holder;
5496                 }
5497             }
5498             return null;
5499         }
5500 
getScrapViewForId(long id, int type, boolean dryRun)5501         ViewHolder getScrapViewForId(long id, int type, boolean dryRun) {
5502             // Look in our attached views first
5503             final int count = mAttachedScrap.size();
5504             for (int i = count - 1; i >= 0; i--) {
5505                 final ViewHolder holder = mAttachedScrap.get(i);
5506                 if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
5507                     if (type == holder.getItemViewType()) {
5508                         holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5509                         if (holder.isRemoved()) {
5510                             // this might be valid in two cases:
5511                             // > item is removed but we are in pre-layout pass
5512                             // >> do nothing. return as is. make sure we don't rebind
5513                             // > item is removed then added to another position and we are in
5514                             // post layout.
5515                             // >> remove removed and invalid flags, add update flag to rebind
5516                             // because item was invisible to us and we don't know what happened in
5517                             // between.
5518                             if (!mState.isPreLayout()) {
5519                                 holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE |
5520                                         ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
5521                             }
5522                         }
5523                         return holder;
5524                     } else if (!dryRun) {
5525                         // if we are running animations, it is actually better to keep it in scrap
5526                         // but this would force layout manager to lay it out which would be bad.
5527                         // Recycle this scrap. Type mismatch.
5528                         mAttachedScrap.remove(i);
5529                         removeDetachedView(holder.itemView, false);
5530                         quickRecycleScrapView(holder.itemView);
5531                     }
5532                 }
5533             }
5534 
5535             // Search the first-level cache
5536             final int cacheSize = mCachedViews.size();
5537             for (int i = cacheSize - 1; i >= 0; i--) {
5538                 final ViewHolder holder = mCachedViews.get(i);
5539                 if (holder.getItemId() == id) {
5540                     if (type == holder.getItemViewType()) {
5541                         if (!dryRun) {
5542                             mCachedViews.remove(i);
5543                         }
5544                         return holder;
5545                     } else if (!dryRun) {
5546                         recycleCachedViewAt(i);
5547                     }
5548                 }
5549             }
5550             return null;
5551         }
5552 
dispatchViewRecycled(ViewHolder holder)5553         void dispatchViewRecycled(ViewHolder holder) {
5554             if (mRecyclerListener != null) {
5555                 mRecyclerListener.onViewRecycled(holder);
5556             }
5557             if (mAdapter != null) {
5558                 mAdapter.onViewRecycled(holder);
5559             }
5560             if (mState != null) {
5561                 mViewInfoStore.removeViewHolder(holder);
5562             }
5563             if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
5564         }
5565 
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, boolean compatibleWithPrevious)5566         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
5567                 boolean compatibleWithPrevious) {
5568             clear();
5569             getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
5570         }
5571 
offsetPositionRecordsForMove(int from, int to)5572         void offsetPositionRecordsForMove(int from, int to) {
5573             final int start, end, inBetweenOffset;
5574             if (from < to) {
5575                 start = from;
5576                 end = to;
5577                 inBetweenOffset = -1;
5578             } else {
5579                 start = to;
5580                 end = from;
5581                 inBetweenOffset = 1;
5582             }
5583             final int cachedCount = mCachedViews.size();
5584             for (int i = 0; i < cachedCount; i++) {
5585                 final ViewHolder holder = mCachedViews.get(i);
5586                 if (holder == null || holder.mPosition < start || holder.mPosition > end) {
5587                     continue;
5588                 }
5589                 if (holder.mPosition == from) {
5590                     holder.offsetPosition(to - from, false);
5591                 } else {
5592                     holder.offsetPosition(inBetweenOffset, false);
5593                 }
5594                 if (DEBUG) {
5595                     Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder " +
5596                             holder);
5597                 }
5598             }
5599         }
5600 
offsetPositionRecordsForInsert(int insertedAt, int count)5601         void offsetPositionRecordsForInsert(int insertedAt, int count) {
5602             final int cachedCount = mCachedViews.size();
5603             for (int i = 0; i < cachedCount; i++) {
5604                 final ViewHolder holder = mCachedViews.get(i);
5605                 if (holder != null && holder.mPosition >= insertedAt) {
5606                     if (DEBUG) {
5607                         Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder " +
5608                                 holder + " now at position " + (holder.mPosition + count));
5609                     }
5610                     holder.offsetPosition(count, true);
5611                 }
5612             }
5613         }
5614 
5615         /**
5616          * @param removedFrom Remove start index
5617          * @param count Remove count
5618          * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
5619          *                         false, they'll be applied before the second layout pass
5620          */
offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout)5621         void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
5622             final int removedEnd = removedFrom + count;
5623             final int cachedCount = mCachedViews.size();
5624             for (int i = cachedCount - 1; i >= 0; i--) {
5625                 final ViewHolder holder = mCachedViews.get(i);
5626                 if (holder != null) {
5627                     if (holder.mPosition >= removedEnd) {
5628                         if (DEBUG) {
5629                             Log.d(TAG, "offsetPositionRecordsForRemove cached " + i +
5630                                     " holder " + holder + " now at position " +
5631                                     (holder.mPosition - count));
5632                         }
5633                         holder.offsetPosition(-count, applyToPreLayout);
5634                     } else if (holder.mPosition >= removedFrom) {
5635                         // Item for this view was removed. Dump it from the cache.
5636                         holder.addFlags(ViewHolder.FLAG_REMOVED);
5637                         recycleCachedViewAt(i);
5638                     }
5639                 }
5640             }
5641         }
5642 
setViewCacheExtension(ViewCacheExtension extension)5643         void setViewCacheExtension(ViewCacheExtension extension) {
5644             mViewCacheExtension = extension;
5645         }
5646 
setRecycledViewPool(RecycledViewPool pool)5647         void setRecycledViewPool(RecycledViewPool pool) {
5648             if (mRecyclerPool != null) {
5649                 mRecyclerPool.detach();
5650             }
5651             mRecyclerPool = pool;
5652             if (pool != null) {
5653                 mRecyclerPool.attach(getAdapter());
5654             }
5655         }
5656 
getRecycledViewPool()5657         RecycledViewPool getRecycledViewPool() {
5658             if (mRecyclerPool == null) {
5659                 mRecyclerPool = new RecycledViewPool();
5660             }
5661             return mRecyclerPool;
5662         }
5663 
viewRangeUpdate(int positionStart, int itemCount)5664         void viewRangeUpdate(int positionStart, int itemCount) {
5665             final int positionEnd = positionStart + itemCount;
5666             final int cachedCount = mCachedViews.size();
5667             for (int i = cachedCount - 1; i >= 0; i--) {
5668                 final ViewHolder holder = mCachedViews.get(i);
5669                 if (holder == null) {
5670                     continue;
5671                 }
5672 
5673                 final int pos = holder.getLayoutPosition();
5674                 if (pos >= positionStart && pos < positionEnd) {
5675                     holder.addFlags(ViewHolder.FLAG_UPDATE);
5676                     recycleCachedViewAt(i);
5677                     // cached views should not be flagged as changed because this will cause them
5678                     // to animate when they are returned from cache.
5679                 }
5680             }
5681         }
5682 
setAdapterPositionsAsUnknown()5683         void setAdapterPositionsAsUnknown() {
5684             final int cachedCount = mCachedViews.size();
5685             for (int i = 0; i < cachedCount; i++) {
5686                 final ViewHolder holder = mCachedViews.get(i);
5687                 if (holder != null) {
5688                     holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
5689                 }
5690             }
5691         }
5692 
markKnownViewsInvalid()5693         void markKnownViewsInvalid() {
5694             if (mAdapter != null && mAdapter.hasStableIds()) {
5695                 final int cachedCount = mCachedViews.size();
5696                 for (int i = 0; i < cachedCount; i++) {
5697                     final ViewHolder holder = mCachedViews.get(i);
5698                     if (holder != null) {
5699                         holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
5700                         holder.addChangePayload(null);
5701                     }
5702                 }
5703             } else {
5704                 // we cannot re-use cached views in this case. Recycle them all
5705                 recycleAndClearCachedViews();
5706             }
5707         }
5708 
clearOldPositions()5709         void clearOldPositions() {
5710             final int cachedCount = mCachedViews.size();
5711             for (int i = 0; i < cachedCount; i++) {
5712                 final ViewHolder holder = mCachedViews.get(i);
5713                 holder.clearOldPosition();
5714             }
5715             final int scrapCount = mAttachedScrap.size();
5716             for (int i = 0; i < scrapCount; i++) {
5717                 mAttachedScrap.get(i).clearOldPosition();
5718             }
5719             if (mChangedScrap != null) {
5720                 final int changedScrapCount = mChangedScrap.size();
5721                 for (int i = 0; i < changedScrapCount; i++) {
5722                     mChangedScrap.get(i).clearOldPosition();
5723                 }
5724             }
5725         }
5726 
markItemDecorInsetsDirty()5727         void markItemDecorInsetsDirty() {
5728             final int cachedCount = mCachedViews.size();
5729             for (int i = 0; i < cachedCount; i++) {
5730                 final ViewHolder holder = mCachedViews.get(i);
5731                 LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
5732                 if (layoutParams != null) {
5733                     layoutParams.mInsetsDirty = true;
5734                 }
5735             }
5736         }
5737     }
5738 
5739     /**
5740      * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
5741      * be controlled by the developer.
5742      * <p>
5743      * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
5744      * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
5745      * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
5746      * {@link RecycledViewPool}.
5747      * <p>
5748      * Note that, Recycler never sends Views to this method to be cached. It is developers
5749      * responsibility to decide whether they want to keep their Views in this custom cache or let
5750      * the default recycling policy handle it.
5751      */
5752     public abstract static class ViewCacheExtension {
5753 
5754         /**
5755          * Returns a View that can be binded to the given Adapter position.
5756          * <p>
5757          * This method should <b>not</b> create a new View. Instead, it is expected to return
5758          * an already created View that can be re-used for the given type and position.
5759          * If the View is marked as ignored, it should first call
5760          * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
5761          * <p>
5762          * RecyclerView will re-bind the returned View to the position if necessary.
5763          *
5764          * @param recycler The Recycler that can be used to bind the View
5765          * @param position The adapter position
5766          * @param type     The type of the View, defined by adapter
5767          * @return A View that is bound to the given position or NULL if there is no View to re-use
5768          * @see LayoutManager#ignoreView(View)
5769          */
getViewForPositionAndType(Recycler recycler, int position, int type)5770         abstract public View getViewForPositionAndType(Recycler recycler, int position, int type);
5771     }
5772 
5773     /**
5774      * Base class for an Adapter
5775      *
5776      * <p>Adapters provide a binding from an app-specific data set to views that are displayed
5777      * within a {@link RecyclerView}.</p>
5778      */
5779     public static abstract class Adapter<VH extends ViewHolder> {
5780         private final AdapterDataObservable mObservable = new AdapterDataObservable();
5781         private boolean mHasStableIds = false;
5782 
5783         /**
5784          * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
5785          * an item.
5786          * <p>
5787          * This new ViewHolder should be constructed with a new View that can represent the items
5788          * of the given type. You can either create a new View manually or inflate it from an XML
5789          * layout file.
5790          * <p>
5791          * The new ViewHolder will be used to display items of the adapter using
5792          * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
5793          * different items in the data set, it is a good idea to cache references to sub views of
5794          * the View to avoid unnecessary {@link View#findViewById(int)} calls.
5795          *
5796          * @param parent The ViewGroup into which the new View will be added after it is bound to
5797          *               an adapter position.
5798          * @param viewType The view type of the new View.
5799          *
5800          * @return A new ViewHolder that holds a View of the given view type.
5801          * @see #getItemViewType(int)
5802          * @see #onBindViewHolder(ViewHolder, int)
5803          */
onCreateViewHolder(ViewGroup parent, int viewType)5804         public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
5805 
5806         /**
5807          * Called by RecyclerView to display the data at the specified position. This method should
5808          * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
5809          * position.
5810          * <p>
5811          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
5812          * again if the position of the item changes in the data set unless the item itself is
5813          * invalidated or the new position cannot be determined. For this reason, you should only
5814          * use the <code>position</code> parameter while acquiring the related data item inside
5815          * this method and should not keep a copy of it. If you need the position of an item later
5816          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
5817          * have the updated adapter position.
5818          *
5819          * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
5820          * handle efficient partial bind.
5821          *
5822          * @param holder The ViewHolder which should be updated to represent the contents of the
5823          *        item at the given position in the data set.
5824          * @param position The position of the item within the adapter's data set.
5825          */
onBindViewHolder(VH holder, int position)5826         public abstract void onBindViewHolder(VH holder, int position);
5827 
5828         /**
5829          * Called by RecyclerView to display the data at the specified position. This method
5830          * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
5831          * the given position.
5832          * <p>
5833          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
5834          * again if the position of the item changes in the data set unless the item itself is
5835          * invalidated or the new position cannot be determined. For this reason, you should only
5836          * use the <code>position</code> parameter while acquiring the related data item inside
5837          * this method and should not keep a copy of it. If you need the position of an item later
5838          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
5839          * have the updated adapter position.
5840          * <p>
5841          * Partial bind vs full bind:
5842          * <p>
5843          * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
5844          * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
5845          * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
5846          * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
5847          * Adapter should not assume that the payload passed in notify methods will be received by
5848          * onBindViewHolder().  For example when the view is not attached to the screen, the
5849          * payload in notifyItemChange() will be simply dropped.
5850          *
5851          * @param holder The ViewHolder which should be updated to represent the contents of the
5852          *               item at the given position in the data set.
5853          * @param position The position of the item within the adapter's data set.
5854          * @param payloads A non-null list of merged payloads. Can be empty list if requires full
5855          *                 update.
5856          */
onBindViewHolder(VH holder, int position, List<Object> payloads)5857         public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
5858             onBindViewHolder(holder, position);
5859         }
5860 
5861         /**
5862          * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
5863          * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
5864          *
5865          * @see #onCreateViewHolder(ViewGroup, int)
5866          */
createViewHolder(ViewGroup parent, int viewType)5867         public final VH createViewHolder(ViewGroup parent, int viewType) {
5868             TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
5869             final VH holder = onCreateViewHolder(parent, viewType);
5870             holder.mItemViewType = viewType;
5871             TraceCompat.endSection();
5872             return holder;
5873         }
5874 
5875         /**
5876          * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
5877          * {@link ViewHolder} contents with the item at the given position and also sets up some
5878          * private fields to be used by RecyclerView.
5879          *
5880          * @see #onBindViewHolder(ViewHolder, int)
5881          */
bindViewHolder(VH holder, int position)5882         public final void bindViewHolder(VH holder, int position) {
5883             holder.mPosition = position;
5884             if (hasStableIds()) {
5885                 holder.mItemId = getItemId(position);
5886             }
5887             holder.setFlags(ViewHolder.FLAG_BOUND,
5888                     ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
5889                             | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
5890             TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
5891             onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
5892             holder.clearPayload();
5893             final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
5894             if (layoutParams instanceof RecyclerView.LayoutParams) {
5895                 ((LayoutParams) layoutParams).mInsetsDirty = true;
5896             }
5897             TraceCompat.endSection();
5898         }
5899 
5900         /**
5901          * Return the view type of the item at <code>position</code> for the purposes
5902          * of view recycling.
5903          *
5904          * <p>The default implementation of this method returns 0, making the assumption of
5905          * a single view type for the adapter. Unlike ListView adapters, types need not
5906          * be contiguous. Consider using id resources to uniquely identify item view types.
5907          *
5908          * @param position position to query
5909          * @return integer value identifying the type of the view needed to represent the item at
5910          *                 <code>position</code>. Type codes need not be contiguous.
5911          */
getItemViewType(int position)5912         public int getItemViewType(int position) {
5913             return 0;
5914         }
5915 
5916         /**
5917          * Indicates whether each item in the data set can be represented with a unique identifier
5918          * of type {@link java.lang.Long}.
5919          *
5920          * @param hasStableIds Whether items in data set have unique identifiers or not.
5921          * @see #hasStableIds()
5922          * @see #getItemId(int)
5923          */
setHasStableIds(boolean hasStableIds)5924         public void setHasStableIds(boolean hasStableIds) {
5925             if (hasObservers()) {
5926                 throw new IllegalStateException("Cannot change whether this adapter has " +
5927                         "stable IDs while the adapter has registered observers.");
5928             }
5929             mHasStableIds = hasStableIds;
5930         }
5931 
5932         /**
5933          * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
5934          * would return false this method should return {@link #NO_ID}. The default implementation
5935          * of this method returns {@link #NO_ID}.
5936          *
5937          * @param position Adapter position to query
5938          * @return the stable ID of the item at position
5939          */
getItemId(int position)5940         public long getItemId(int position) {
5941             return NO_ID;
5942         }
5943 
5944         /**
5945          * Returns the total number of items in the data set held by the adapter.
5946          *
5947          * @return The total number of items in this adapter.
5948          */
getItemCount()5949         public abstract int getItemCount();
5950 
5951         /**
5952          * Returns true if this adapter publishes a unique <code>long</code> value that can
5953          * act as a key for the item at a given position in the data set. If that item is relocated
5954          * in the data set, the ID returned for that item should be the same.
5955          *
5956          * @return true if this adapter's items have stable IDs
5957          */
hasStableIds()5958         public final boolean hasStableIds() {
5959             return mHasStableIds;
5960         }
5961 
5962         /**
5963          * Called when a view created by this adapter has been recycled.
5964          *
5965          * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
5966          * needs to be attached to its parent {@link RecyclerView}. This can be because it has
5967          * fallen out of visibility or a set of cached views represented by views still
5968          * attached to the parent RecyclerView. If an item view has large or expensive data
5969          * bound to it such as large bitmaps, this may be a good place to release those
5970          * resources.</p>
5971          * <p>
5972          * RecyclerView calls this method right before clearing ViewHolder's internal data and
5973          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
5974          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
5975          * its adapter position.
5976          *
5977          * @param holder The ViewHolder for the view being recycled
5978          */
onViewRecycled(VH holder)5979         public void onViewRecycled(VH holder) {
5980         }
5981 
5982         /**
5983          * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
5984          * due to its transient state. Upon receiving this callback, Adapter can clear the
5985          * animation(s) that effect the View's transient state and return <code>true</code> so that
5986          * the View can be recycled. Keep in mind that the View in question is already removed from
5987          * the RecyclerView.
5988          * <p>
5989          * In some cases, it is acceptable to recycle a View although it has transient state. Most
5990          * of the time, this is a case where the transient state will be cleared in
5991          * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
5992          * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
5993          * value of this method to decide whether the View should be recycled or not.
5994          * <p>
5995          * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
5996          * should never receive this callback because RecyclerView keeps those Views as children
5997          * until their animations are complete. This callback is useful when children of the item
5998          * views create animations which may not be easy to implement using an {@link ItemAnimator}.
5999          * <p>
6000          * You should <em>never</em> fix this issue by calling
6001          * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
6002          * <code>holder.itemView.setHasTransientState(true);</code>. Each
6003          * <code>View.setHasTransientState(true)</code> call must be matched by a
6004          * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
6005          * may become inconsistent. You should always prefer to end or cancel animations that are
6006          * triggering the transient state instead of handling it manually.
6007          *
6008          * @param holder The ViewHolder containing the View that could not be recycled due to its
6009          *               transient state.
6010          * @return True if the View should be recycled, false otherwise. Note that if this method
6011          * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
6012          * the View and recycle it regardless. If this method returns <code>false</code>,
6013          * RecyclerView will check the View's transient state again before giving a final decision.
6014          * Default implementation returns false.
6015          */
onFailedToRecycleView(VH holder)6016         public boolean onFailedToRecycleView(VH holder) {
6017             return false;
6018         }
6019 
6020         /**
6021          * Called when a view created by this adapter has been attached to a window.
6022          *
6023          * <p>This can be used as a reasonable signal that the view is about to be seen
6024          * by the user. If the adapter previously freed any resources in
6025          * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
6026          * those resources should be restored here.</p>
6027          *
6028          * @param holder Holder of the view being attached
6029          */
onViewAttachedToWindow(VH holder)6030         public void onViewAttachedToWindow(VH holder) {
6031         }
6032 
6033         /**
6034          * Called when a view created by this adapter has been detached from its window.
6035          *
6036          * <p>Becoming detached from the window is not necessarily a permanent condition;
6037          * the consumer of an Adapter's views may choose to cache views offscreen while they
6038          * are not visible, attaching and detaching them as appropriate.</p>
6039          *
6040          * @param holder Holder of the view being detached
6041          */
onViewDetachedFromWindow(VH holder)6042         public void onViewDetachedFromWindow(VH holder) {
6043         }
6044 
6045         /**
6046          * Returns true if one or more observers are attached to this adapter.
6047          *
6048          * @return true if this adapter has observers
6049          */
hasObservers()6050         public final boolean hasObservers() {
6051             return mObservable.hasObservers();
6052         }
6053 
6054         /**
6055          * Register a new observer to listen for data changes.
6056          *
6057          * <p>The adapter may publish a variety of events describing specific changes.
6058          * Not all adapters may support all change types and some may fall back to a generic
6059          * {@link android.support.v7.widget.RecyclerView.AdapterDataObserver#onChanged()
6060          * "something changed"} event if more specific data is not available.</p>
6061          *
6062          * <p>Components registering observers with an adapter are responsible for
6063          * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6064          * unregistering} those observers when finished.</p>
6065          *
6066          * @param observer Observer to register
6067          *
6068          * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6069          */
registerAdapterDataObserver(AdapterDataObserver observer)6070         public void registerAdapterDataObserver(AdapterDataObserver observer) {
6071             mObservable.registerObserver(observer);
6072         }
6073 
6074         /**
6075          * Unregister an observer currently listening for data changes.
6076          *
6077          * <p>The unregistered observer will no longer receive events about changes
6078          * to the adapter.</p>
6079          *
6080          * @param observer Observer to unregister
6081          *
6082          * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
6083          */
unregisterAdapterDataObserver(AdapterDataObserver observer)6084         public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
6085             mObservable.unregisterObserver(observer);
6086         }
6087 
6088         /**
6089          * Called by RecyclerView when it starts observing this Adapter.
6090          * <p>
6091          * Keep in mind that same adapter may be observed by multiple RecyclerViews.
6092          *
6093          * @param recyclerView The RecyclerView instance which started observing this adapter.
6094          * @see #onDetachedFromRecyclerView(RecyclerView)
6095          */
onAttachedToRecyclerView(RecyclerView recyclerView)6096         public void onAttachedToRecyclerView(RecyclerView recyclerView) {
6097         }
6098 
6099         /**
6100          * Called by RecyclerView when it stops observing this Adapter.
6101          *
6102          * @param recyclerView The RecyclerView instance which stopped observing this adapter.
6103          * @see #onAttachedToRecyclerView(RecyclerView)
6104          */
onDetachedFromRecyclerView(RecyclerView recyclerView)6105         public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
6106         }
6107 
6108         /**
6109          * Notify any registered observers that the data set has changed.
6110          *
6111          * <p>There are two different classes of data change events, item changes and structural
6112          * changes. Item changes are when a single item has its data updated but no positional
6113          * changes have occurred. Structural changes are when items are inserted, removed or moved
6114          * within the data set.</p>
6115          *
6116          * <p>This event does not specify what about the data set has changed, forcing
6117          * any observers to assume that all existing items and structure may no longer be valid.
6118          * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
6119          *
6120          * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
6121          * for adapters that report that they have {@link #hasStableIds() stable IDs} when
6122          * this method is used. This can help for the purposes of animation and visual
6123          * object persistence but individual item views will still need to be rebound
6124          * and relaid out.</p>
6125          *
6126          * <p>If you are writing an adapter it will always be more efficient to use the more
6127          * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
6128          * as a last resort.</p>
6129          *
6130          * @see #notifyItemChanged(int)
6131          * @see #notifyItemInserted(int)
6132          * @see #notifyItemRemoved(int)
6133          * @see #notifyItemRangeChanged(int, int)
6134          * @see #notifyItemRangeInserted(int, int)
6135          * @see #notifyItemRangeRemoved(int, int)
6136          */
notifyDataSetChanged()6137         public final void notifyDataSetChanged() {
6138             mObservable.notifyChanged();
6139         }
6140 
6141         /**
6142          * Notify any registered observers that the item at <code>position</code> has changed.
6143          * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
6144          *
6145          * <p>This is an item change event, not a structural change event. It indicates that any
6146          * reflection of the data at <code>position</code> is out of date and should be updated.
6147          * The item at <code>position</code> retains the same identity.</p>
6148          *
6149          * @param position Position of the item that has changed
6150          *
6151          * @see #notifyItemRangeChanged(int, int)
6152          */
notifyItemChanged(int position)6153         public final void notifyItemChanged(int position) {
6154             mObservable.notifyItemRangeChanged(position, 1);
6155         }
6156 
6157         /**
6158          * Notify any registered observers that the item at <code>position</code> has changed with an
6159          * optional payload object.
6160          *
6161          * <p>This is an item change event, not a structural change event. It indicates that any
6162          * reflection of the data at <code>position</code> is out of date and should be updated.
6163          * The item at <code>position</code> retains the same identity.
6164          * </p>
6165          *
6166          * <p>
6167          * Client can optionally pass a payload for partial change. These payloads will be merged
6168          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6169          * item is already represented by a ViewHolder and it will be rebound to the same
6170          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6171          * payloads on that item and prevent future payload until
6172          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6173          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6174          * attached, the payload will be simply dropped.
6175          *
6176          * @param position Position of the item that has changed
6177          * @param payload Optional parameter, use null to identify a "full" update
6178          *
6179          * @see #notifyItemRangeChanged(int, int)
6180          */
notifyItemChanged(int position, Object payload)6181         public final void notifyItemChanged(int position, Object payload) {
6182             mObservable.notifyItemRangeChanged(position, 1, payload);
6183         }
6184 
6185         /**
6186          * Notify any registered observers that the <code>itemCount</code> items starting at
6187          * position <code>positionStart</code> have changed.
6188          * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
6189          *
6190          * <p>This is an item change event, not a structural change event. It indicates that
6191          * any reflection of the data in the given position range is out of date and should
6192          * be updated. The items in the given range retain the same identity.</p>
6193          *
6194          * @param positionStart Position of the first item that has changed
6195          * @param itemCount Number of items that have changed
6196          *
6197          * @see #notifyItemChanged(int)
6198          */
notifyItemRangeChanged(int positionStart, int itemCount)6199         public final void notifyItemRangeChanged(int positionStart, int itemCount) {
6200             mObservable.notifyItemRangeChanged(positionStart, itemCount);
6201         }
6202 
6203         /**
6204          * Notify any registered observers that the <code>itemCount</code> items starting at
6205          * position <code>positionStart</code> have changed. An optional payload can be
6206          * passed to each changed item.
6207          *
6208          * <p>This is an item change event, not a structural change event. It indicates that any
6209          * reflection of the data in the given position range is out of date and should be updated.
6210          * The items in the given range retain the same identity.
6211          * </p>
6212          *
6213          * <p>
6214          * Client can optionally pass a payload for partial change. These payloads will be merged
6215          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6216          * item is already represented by a ViewHolder and it will be rebound to the same
6217          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6218          * payloads on that item and prevent future payload until
6219          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6220          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6221          * attached, the payload will be simply dropped.
6222          *
6223          * @param positionStart Position of the first item that has changed
6224          * @param itemCount Number of items that have changed
6225          * @param payload  Optional parameter, use null to identify a "full" update
6226          *
6227          * @see #notifyItemChanged(int)
6228          */
notifyItemRangeChanged(int positionStart, int itemCount, Object payload)6229         public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
6230             mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
6231         }
6232 
6233         /**
6234          * Notify any registered observers that the item reflected at <code>position</code>
6235          * has been newly inserted. The item previously at <code>position</code> is now at
6236          * position <code>position + 1</code>.
6237          *
6238          * <p>This is a structural change event. Representations of other existing items in the
6239          * data set are still considered up to date and will not be rebound, though their
6240          * positions may be altered.</p>
6241          *
6242          * @param position Position of the newly inserted item in the data set
6243          *
6244          * @see #notifyItemRangeInserted(int, int)
6245          */
notifyItemInserted(int position)6246         public final void notifyItemInserted(int position) {
6247             mObservable.notifyItemRangeInserted(position, 1);
6248         }
6249 
6250         /**
6251          * Notify any registered observers that the item reflected at <code>fromPosition</code>
6252          * has been moved to <code>toPosition</code>.
6253          *
6254          * <p>This is a structural change event. Representations of other existing items in the
6255          * data set are still considered up to date and will not be rebound, though their
6256          * positions may be altered.</p>
6257          *
6258          * @param fromPosition Previous position of the item.
6259          * @param toPosition New position of the item.
6260          */
notifyItemMoved(int fromPosition, int toPosition)6261         public final void notifyItemMoved(int fromPosition, int toPosition) {
6262             mObservable.notifyItemMoved(fromPosition, toPosition);
6263         }
6264 
6265         /**
6266          * Notify any registered observers that the currently reflected <code>itemCount</code>
6267          * items starting at <code>positionStart</code> have been newly inserted. The items
6268          * previously located at <code>positionStart</code> and beyond can now be found starting
6269          * at position <code>positionStart + itemCount</code>.
6270          *
6271          * <p>This is a structural change event. Representations of other existing items in the
6272          * data set are still considered up to date and will not be rebound, though their positions
6273          * may be altered.</p>
6274          *
6275          * @param positionStart Position of the first item that was inserted
6276          * @param itemCount Number of items inserted
6277          *
6278          * @see #notifyItemInserted(int)
6279          */
notifyItemRangeInserted(int positionStart, int itemCount)6280         public final void notifyItemRangeInserted(int positionStart, int itemCount) {
6281             mObservable.notifyItemRangeInserted(positionStart, itemCount);
6282         }
6283 
6284         /**
6285          * Notify any registered observers that the item previously located at <code>position</code>
6286          * has been removed from the data set. The items previously located at and after
6287          * <code>position</code> may now be found at <code>oldPosition - 1</code>.
6288          *
6289          * <p>This is a structural change event. Representations of other existing items in the
6290          * data set are still considered up to date and will not be rebound, though their positions
6291          * may be altered.</p>
6292          *
6293          * @param position Position of the item that has now been removed
6294          *
6295          * @see #notifyItemRangeRemoved(int, int)
6296          */
notifyItemRemoved(int position)6297         public final void notifyItemRemoved(int position) {
6298             mObservable.notifyItemRangeRemoved(position, 1);
6299         }
6300 
6301         /**
6302          * Notify any registered observers that the <code>itemCount</code> items previously
6303          * located at <code>positionStart</code> have been removed from the data set. The items
6304          * previously located at and after <code>positionStart + itemCount</code> may now be found
6305          * at <code>oldPosition - itemCount</code>.
6306          *
6307          * <p>This is a structural change event. Representations of other existing items in the data
6308          * set are still considered up to date and will not be rebound, though their positions
6309          * may be altered.</p>
6310          *
6311          * @param positionStart Previous position of the first item that was removed
6312          * @param itemCount Number of items removed from the data set
6313          */
notifyItemRangeRemoved(int positionStart, int itemCount)6314         public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
6315             mObservable.notifyItemRangeRemoved(positionStart, itemCount);
6316         }
6317     }
6318 
dispatchChildDetached(View child)6319     private void dispatchChildDetached(View child) {
6320         final ViewHolder viewHolder = getChildViewHolderInt(child);
6321         onChildDetachedFromWindow(child);
6322         if (mAdapter != null && viewHolder != null) {
6323             mAdapter.onViewDetachedFromWindow(viewHolder);
6324         }
6325         if (mOnChildAttachStateListeners != null) {
6326             final int cnt = mOnChildAttachStateListeners.size();
6327             for (int i = cnt - 1; i >= 0; i--) {
6328                 mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
6329             }
6330         }
6331     }
6332 
dispatchChildAttached(View child)6333     private void dispatchChildAttached(View child) {
6334         final ViewHolder viewHolder = getChildViewHolderInt(child);
6335         onChildAttachedToWindow(child);
6336         if (mAdapter != null && viewHolder != null) {
6337             mAdapter.onViewAttachedToWindow(viewHolder);
6338         }
6339         if (mOnChildAttachStateListeners != null) {
6340             final int cnt = mOnChildAttachStateListeners.size();
6341             for (int i = cnt - 1; i >= 0; i--) {
6342                 mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
6343             }
6344         }
6345     }
6346 
6347     /**
6348      * A <code>LayoutManager</code> is responsible for measuring and positioning item views
6349      * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
6350      * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
6351      * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
6352      * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
6353      * layout managers are provided for general use.
6354      * <p/>
6355      * If the LayoutManager specifies a default constructor or one with the signature
6356      * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
6357      * instantiate and set the LayoutManager when being inflated. Most used properties can
6358      * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
6359      * a LayoutManager specifies both constructors, the non-default constructor will take
6360      * precedence.
6361      *
6362      */
6363     public static abstract class LayoutManager {
6364         ChildHelper mChildHelper;
6365         RecyclerView mRecyclerView;
6366 
6367         @Nullable
6368         SmoothScroller mSmoothScroller;
6369 
6370         private boolean mRequestedSimpleAnimations = false;
6371 
6372         boolean mIsAttachedToWindow = false;
6373 
6374         private boolean mAutoMeasure = false;
6375 
6376         /**
6377          * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
6378          * if the space that will be given to it is already larger than what it has measured before.
6379          */
6380         private boolean mMeasurementCacheEnabled = true;
6381 
6382 
6383         /**
6384          * These measure specs might be the measure specs that were passed into RecyclerView's
6385          * onMeasure method OR fake measure specs created by the RecyclerView.
6386          * For example, when a layout is run, RecyclerView always sets these specs to be
6387          * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
6388          * <p>
6389          * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
6390          * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
6391          * corrupt values. Older platforms have no responsibility to provide a size if they set
6392          * mode to unspecified.
6393          */
6394         private int mWidthMode, mHeightMode;
6395         private int mWidth, mHeight;
6396 
setRecyclerView(RecyclerView recyclerView)6397         void setRecyclerView(RecyclerView recyclerView) {
6398             if (recyclerView == null) {
6399                 mRecyclerView = null;
6400                 mChildHelper = null;
6401                 mWidth = 0;
6402                 mHeight = 0;
6403             } else {
6404                 mRecyclerView = recyclerView;
6405                 mChildHelper = recyclerView.mChildHelper;
6406                 mWidth = recyclerView.getWidth();
6407                 mHeight = recyclerView.getHeight();
6408             }
6409             mWidthMode = MeasureSpec.EXACTLY;
6410             mHeightMode = MeasureSpec.EXACTLY;
6411         }
6412 
setMeasureSpecs(int wSpec, int hSpec)6413         void setMeasureSpecs(int wSpec, int hSpec) {
6414             mWidth = MeasureSpec.getSize(wSpec);
6415             mWidthMode = MeasureSpec.getMode(wSpec);
6416             if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
6417                 mWidth = 0;
6418             }
6419 
6420             mHeight = MeasureSpec.getSize(hSpec);
6421             mHeightMode = MeasureSpec.getMode(hSpec);
6422             if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
6423                 mHeight = 0;
6424             }
6425         }
6426 
6427         /**
6428          * Called after a layout is calculated during a measure pass when using auto-measure.
6429          * <p>
6430          * It simply traverses all children to calculate a bounding box then calls
6431          * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
6432          * if they need to handle the bounding box differently.
6433          * <p>
6434          * For example, GridLayoutManager override that method to ensure that even if a column is
6435          * empty, the GridLayoutManager still measures wide enough to include it.
6436          *
6437          * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
6438          * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
6439          */
setMeasuredDimensionFromChildren(int widthSpec, int heightSpec)6440         void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
6441             final int count = getChildCount();
6442             if (count == 0) {
6443                 mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
6444                 return;
6445             }
6446             int minX = Integer.MAX_VALUE;
6447             int minY = Integer.MAX_VALUE;
6448             int maxX = Integer.MIN_VALUE;
6449             int maxY = Integer.MIN_VALUE;
6450 
6451             for (int i = 0; i < count; i++) {
6452                 View child = getChildAt(i);
6453                 LayoutParams lp = (LayoutParams) child.getLayoutParams();
6454                 final Rect bounds = mRecyclerView.mTempRect;
6455                 getDecoratedBoundsWithMargins(child, bounds);
6456                 if (bounds.left < minX) {
6457                     minX = bounds.left;
6458                 }
6459                 if (bounds.right > maxX) {
6460                     maxX = bounds.right;
6461                 }
6462                 if (bounds.top < minY) {
6463                     minY = bounds.top;
6464                 }
6465                 if (bounds.bottom > maxY) {
6466                     maxY = bounds.bottom;
6467                 }
6468             }
6469             mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
6470             setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
6471         }
6472 
6473         /**
6474          * Sets the measured dimensions from the given bounding box of the children and the
6475          * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
6476          * called after the RecyclerView calls
6477          * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a measurement pass.
6478          * <p>
6479          * This method should call {@link #setMeasuredDimension(int, int)}.
6480          * <p>
6481          * The default implementation adds the RecyclerView's padding to the given bounding box
6482          * then caps the value to be within the given measurement specs.
6483          * <p>
6484          * This method is only called if the LayoutManager opted into the auto measurement API.
6485          *
6486          * @param childrenBounds The bounding box of all children
6487          * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
6488          * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
6489          *
6490          * @see #setAutoMeasureEnabled(boolean)
6491          */
setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec)6492         public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
6493             int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
6494             int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
6495             int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
6496             int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
6497             setMeasuredDimension(width, height);
6498         }
6499 
6500         /**
6501          * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
6502          */
requestLayout()6503         public void requestLayout() {
6504             if(mRecyclerView != null) {
6505                 mRecyclerView.requestLayout();
6506             }
6507         }
6508 
6509         /**
6510          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
6511          * {@link IllegalStateException} if it <b>is not</b>.
6512          *
6513          * @param message The message for the exception. Can be null.
6514          * @see #assertNotInLayoutOrScroll(String)
6515          */
assertInLayoutOrScroll(String message)6516         public void assertInLayoutOrScroll(String message) {
6517             if (mRecyclerView != null) {
6518                 mRecyclerView.assertInLayoutOrScroll(message);
6519             }
6520         }
6521 
6522         /**
6523          * Chooses a size from the given specs and parameters that is closest to the desired size
6524          * and also complies with the spec.
6525          *
6526          * @param spec The measureSpec
6527          * @param desired The preferred measurement
6528          * @param min The minimum value
6529          *
6530          * @return A size that fits to the given specs
6531          */
chooseSize(int spec, int desired, int min)6532         public static int chooseSize(int spec, int desired, int min) {
6533             final int mode = View.MeasureSpec.getMode(spec);
6534             final int size = View.MeasureSpec.getSize(spec);
6535             switch (mode) {
6536                 case View.MeasureSpec.EXACTLY:
6537                     return size;
6538                 case View.MeasureSpec.AT_MOST:
6539                     return Math.min(size, Math.max(desired, min));
6540                 case View.MeasureSpec.UNSPECIFIED:
6541                 default:
6542                     return Math.max(desired, min);
6543             }
6544         }
6545 
6546         /**
6547          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
6548          * {@link IllegalStateException} if it <b>is</b>.
6549          *
6550          * @param message The message for the exception. Can be null.
6551          * @see #assertInLayoutOrScroll(String)
6552          */
assertNotInLayoutOrScroll(String message)6553         public void assertNotInLayoutOrScroll(String message) {
6554             if (mRecyclerView != null) {
6555                 mRecyclerView.assertNotInLayoutOrScroll(message);
6556             }
6557         }
6558 
6559         /**
6560          * Defines whether the layout should be measured by the RecyclerView or the LayoutManager
6561          * wants to handle the layout measurements itself.
6562          * <p>
6563          * This method is usually called by the LayoutManager with value {@code true} if it wants
6564          * to support WRAP_CONTENT. If you are using a public LayoutManager but want to customize
6565          * the measurement logic, you can call this method with {@code false} and override
6566          * {@link LayoutManager#onMeasure(int, int)} to implement your custom measurement logic.
6567          * <p>
6568          * AutoMeasure is a convenience mechanism for LayoutManagers to easily wrap their content or
6569          * handle various specs provided by the RecyclerView's parent.
6570          * It works by calling {@link LayoutManager#onLayoutChildren(Recycler, State)} during an
6571          * {@link RecyclerView#onMeasure(int, int)} call, then calculating desired dimensions based
6572          * on children's positions. It does this while supporting all existing animation
6573          * capabilities of the RecyclerView.
6574          * <p>
6575          * AutoMeasure works as follows:
6576          * <ol>
6577          * <li>LayoutManager should call {@code setAutoMeasureEnabled(true)} to enable it. All of
6578          * the framework LayoutManagers use {@code auto-measure}.</li>
6579          * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided specs are
6580          * exact, RecyclerView will only call LayoutManager's {@code onMeasure} and return without
6581          * doing any layout calculation.</li>
6582          * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
6583          * layout process in {@code onMeasure} call. It will process all pending Adapter updates and
6584          * decide whether to run a predictive layout or not. If it decides to do so, it will first
6585          * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
6586          * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
6587          * return the width and height of the RecyclerView as of the last layout calculation.
6588          * <p>
6589          * After handling the predictive case, RecyclerView will call
6590          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
6591          * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
6592          * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
6593          * {@link #getWidth()} and {@link #getWidthMode()}.</li>
6594          * <li>After the layout calculation, RecyclerView sets the measured width & height by
6595          * calculating the bounding box for the children (+ RecyclerView's padding). The
6596          * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
6597          * different values. For instance, GridLayoutManager overrides this value to handle the case
6598          * where if it is vertical and has 3 columns but only 2 items, it should still measure its
6599          * width to fit 3 items, not 2.</li>
6600          * <li>Any following on measure call to the RecyclerView will run
6601          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
6602          * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
6603          * take care of which views are actually added / removed / moved / changed for animations so
6604          * that the LayoutManager should not worry about them and handle each
6605          * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.
6606          * </li>
6607          * <li>When measure is complete and RecyclerView's
6608          * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
6609          * whether it already did layout calculations during the measure pass and if so, it re-uses
6610          * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
6611          * if the last measure spec was different from the final dimensions or adapter contents
6612          * have changed between the measure call and the layout call.</li>
6613          * <li>Finally, animations are calculated and run as usual.</li>
6614          * </ol>
6615          *
6616          * @param enabled <code>True</code> if the Layout should be measured by the
6617          *                             RecyclerView, <code>false</code> if the LayoutManager wants
6618          *                             to measure itself.
6619          *
6620          * @see #setMeasuredDimension(Rect, int, int)
6621          * @see #isAutoMeasureEnabled()
6622          */
setAutoMeasureEnabled(boolean enabled)6623         public void setAutoMeasureEnabled(boolean enabled) {
6624             mAutoMeasure = enabled;
6625         }
6626 
6627         /**
6628          * Returns whether the LayoutManager uses the automatic measurement API or not.
6629          *
6630          * @return <code>True</code> if the LayoutManager is measured by the RecyclerView or
6631          * <code>false</code> if it measures itself.
6632          *
6633          * @see #setAutoMeasureEnabled(boolean)
6634          */
isAutoMeasureEnabled()6635         public boolean isAutoMeasureEnabled() {
6636             return mAutoMeasure;
6637         }
6638 
6639         /**
6640          * Returns whether this LayoutManager supports automatic item animations.
6641          * A LayoutManager wishing to support item animations should obey certain
6642          * rules as outlined in {@link #onLayoutChildren(Recycler, State)}.
6643          * The default return value is <code>false</code>, so subclasses of LayoutManager
6644          * will not get predictive item animations by default.
6645          *
6646          * <p>Whether item animations are enabled in a RecyclerView is determined both
6647          * by the return value from this method and the
6648          * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
6649          * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
6650          * method returns false, then simple item animations will be enabled, in which
6651          * views that are moving onto or off of the screen are simply faded in/out. If
6652          * the RecyclerView has a non-null ItemAnimator and this method returns true,
6653          * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to
6654          * setup up the information needed to more intelligently predict where appearing
6655          * and disappearing views should be animated from/to.</p>
6656          *
6657          * @return true if predictive item animations should be enabled, false otherwise
6658          */
supportsPredictiveItemAnimations()6659         public boolean supportsPredictiveItemAnimations() {
6660             return false;
6661         }
6662 
dispatchAttachedToWindow(RecyclerView view)6663         void dispatchAttachedToWindow(RecyclerView view) {
6664             mIsAttachedToWindow = true;
6665             onAttachedToWindow(view);
6666         }
6667 
dispatchDetachedFromWindow(RecyclerView view, Recycler recycler)6668         void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
6669             mIsAttachedToWindow = false;
6670             onDetachedFromWindow(view, recycler);
6671         }
6672 
6673         /**
6674          * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
6675          * to a window.
6676          *
6677          * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
6678          * is attached to window.
6679          */
isAttachedToWindow()6680         public boolean isAttachedToWindow() {
6681             return mIsAttachedToWindow;
6682         }
6683 
6684         /**
6685          * Causes the Runnable to execute on the next animation time step.
6686          * The runnable will be run on the user interface thread.
6687          * <p>
6688          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
6689          *
6690          * @param action The Runnable that will be executed.
6691          *
6692          * @see #removeCallbacks
6693          */
postOnAnimation(Runnable action)6694         public void postOnAnimation(Runnable action) {
6695             if (mRecyclerView != null) {
6696                 ViewCompat.postOnAnimation(mRecyclerView, action);
6697             }
6698         }
6699 
6700         /**
6701          * Removes the specified Runnable from the message queue.
6702          * <p>
6703          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
6704          *
6705          * @param action The Runnable to remove from the message handling queue
6706          *
6707          * @return true if RecyclerView could ask the Handler to remove the Runnable,
6708          *         false otherwise. When the returned value is true, the Runnable
6709          *         may or may not have been actually removed from the message queue
6710          *         (for instance, if the Runnable was not in the queue already.)
6711          *
6712          * @see #postOnAnimation
6713          */
removeCallbacks(Runnable action)6714         public boolean removeCallbacks(Runnable action) {
6715             if (mRecyclerView != null) {
6716                 return mRecyclerView.removeCallbacks(action);
6717             }
6718             return false;
6719         }
6720         /**
6721          * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
6722          * is attached to a window.
6723          * <p>
6724          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
6725          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
6726          * not requested on the RecyclerView while it was detached.
6727          * <p>
6728          * Subclass implementations should always call through to the superclass implementation.
6729          *
6730          * @param view The RecyclerView this LayoutManager is bound to
6731          *
6732          * @see #onDetachedFromWindow(RecyclerView, Recycler)
6733          */
6734         @CallSuper
onAttachedToWindow(RecyclerView view)6735         public void onAttachedToWindow(RecyclerView view) {
6736         }
6737 
6738         /**
6739          * @deprecated
6740          * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
6741          */
6742         @Deprecated
onDetachedFromWindow(RecyclerView view)6743         public void onDetachedFromWindow(RecyclerView view) {
6744 
6745         }
6746 
6747         /**
6748          * Called when this LayoutManager is detached from its parent RecyclerView or when
6749          * its parent RecyclerView is detached from its window.
6750          * <p>
6751          * LayoutManager should clear all of its View references as another LayoutManager might be
6752          * assigned to the RecyclerView.
6753          * <p>
6754          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
6755          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
6756          * not requested on the RecyclerView while it was detached.
6757          * <p>
6758          * If your LayoutManager has View references that it cleans in on-detach, it should also
6759          * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
6760          * RecyclerView is re-attached.
6761          * <p>
6762          * Subclass implementations should always call through to the superclass implementation.
6763          *
6764          * @param view The RecyclerView this LayoutManager is bound to
6765          * @param recycler The recycler to use if you prefer to recycle your children instead of
6766          *                 keeping them around.
6767          *
6768          * @see #onAttachedToWindow(RecyclerView)
6769          */
6770         @CallSuper
onDetachedFromWindow(RecyclerView view, Recycler recycler)6771         public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
6772             onDetachedFromWindow(view);
6773         }
6774 
6775         /**
6776          * Check if the RecyclerView is configured to clip child views to its padding.
6777          *
6778          * @return true if this RecyclerView clips children to its padding, false otherwise
6779          */
getClipToPadding()6780         public boolean getClipToPadding() {
6781             return mRecyclerView != null && mRecyclerView.mClipToPadding;
6782         }
6783 
6784         /**
6785          * Lay out all relevant child views from the given adapter.
6786          *
6787          * The LayoutManager is in charge of the behavior of item animations. By default,
6788          * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
6789          * item animations are enabled. This means that add/remove operations on the
6790          * adapter will result in animations to add new or appearing items, removed or
6791          * disappearing items, and moved items. If a LayoutManager returns false from
6792          * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
6793          * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
6794          * RecyclerView will have enough information to run those animations in a simple
6795          * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
6796          * simply fade views in and out, whether they are actually added/removed or whether
6797          * they are moved on or off the screen due to other add/remove operations.
6798          *
6799          * <p>A LayoutManager wanting a better item animation experience, where items can be
6800          * animated onto and off of the screen according to where the items exist when they
6801          * are not on screen, then the LayoutManager should return true from
6802          * {@link #supportsPredictiveItemAnimations()} and add additional logic to
6803          * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
6804          * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
6805          * once as a "pre" layout step to determine where items would have been prior to
6806          * a real layout, and again to do the "real" layout. In the pre-layout phase,
6807          * items will remember their pre-layout positions to allow them to be laid out
6808          * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
6809          * be returned from the scrap to help determine correct placement of other items.
6810          * These removed items should not be added to the child list, but should be used
6811          * to help calculate correct positioning of other views, including views that
6812          * were not previously onscreen (referred to as APPEARING views), but whose
6813          * pre-layout offscreen position can be determined given the extra
6814          * information about the pre-layout removed views.</p>
6815          *
6816          * <p>The second layout pass is the real layout in which only non-removed views
6817          * will be used. The only additional requirement during this pass is, if
6818          * {@link #supportsPredictiveItemAnimations()} returns true, to note which
6819          * views exist in the child list prior to layout and which are not there after
6820          * layout (referred to as DISAPPEARING views), and to position/layout those views
6821          * appropriately, without regard to the actual bounds of the RecyclerView. This allows
6822          * the animation system to know the location to which to animate these disappearing
6823          * views.</p>
6824          *
6825          * <p>The default LayoutManager implementations for RecyclerView handle all of these
6826          * requirements for animations already. Clients of RecyclerView can either use one
6827          * of these layout managers directly or look at their implementations of
6828          * onLayoutChildren() to see how they account for the APPEARING and
6829          * DISAPPEARING views.</p>
6830          *
6831          * @param recycler         Recycler to use for fetching potentially cached views for a
6832          *                         position
6833          * @param state            Transient state of RecyclerView
6834          */
onLayoutChildren(Recycler recycler, State state)6835         public void onLayoutChildren(Recycler recycler, State state) {
6836             Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
6837         }
6838 
6839         /**
6840          * Called after a full layout calculation is finished. The layout calculation may include
6841          * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
6842          * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
6843          * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
6844          * <p>
6845          * This is a good place for the LayoutManager to do some cleanup like pending scroll
6846          * position, saved state etc.
6847          *
6848          * @param state Transient state of RecyclerView
6849          */
onLayoutCompleted(State state)6850         public void onLayoutCompleted(State state) {
6851         }
6852 
6853         /**
6854          * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
6855          *
6856          * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
6857          * to store extra information specific to the layout. Client code should subclass
6858          * {@link RecyclerView.LayoutParams} for this purpose.</p>
6859          *
6860          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
6861          * you must also override
6862          * {@link #checkLayoutParams(LayoutParams)},
6863          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
6864          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
6865          *
6866          * @return A new LayoutParams for a child view
6867          */
generateDefaultLayoutParams()6868         public abstract LayoutParams generateDefaultLayoutParams();
6869 
6870         /**
6871          * Determines the validity of the supplied LayoutParams object.
6872          *
6873          * <p>This should check to make sure that the object is of the correct type
6874          * and all values are within acceptable ranges. The default implementation
6875          * returns <code>true</code> for non-null params.</p>
6876          *
6877          * @param lp LayoutParams object to check
6878          * @return true if this LayoutParams object is valid, false otherwise
6879          */
checkLayoutParams(LayoutParams lp)6880         public boolean checkLayoutParams(LayoutParams lp) {
6881             return lp != null;
6882         }
6883 
6884         /**
6885          * Create a LayoutParams object suitable for this LayoutManager, copying relevant
6886          * values from the supplied LayoutParams object if possible.
6887          *
6888          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
6889          * you must also override
6890          * {@link #checkLayoutParams(LayoutParams)},
6891          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
6892          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
6893          *
6894          * @param lp Source LayoutParams object to copy values from
6895          * @return a new LayoutParams object
6896          */
generateLayoutParams(ViewGroup.LayoutParams lp)6897         public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
6898             if (lp instanceof LayoutParams) {
6899                 return new LayoutParams((LayoutParams) lp);
6900             } else if (lp instanceof MarginLayoutParams) {
6901                 return new LayoutParams((MarginLayoutParams) lp);
6902             } else {
6903                 return new LayoutParams(lp);
6904             }
6905         }
6906 
6907         /**
6908          * Create a LayoutParams object suitable for this LayoutManager from
6909          * an inflated layout resource.
6910          *
6911          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
6912          * you must also override
6913          * {@link #checkLayoutParams(LayoutParams)},
6914          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
6915          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
6916          *
6917          * @param c Context for obtaining styled attributes
6918          * @param attrs AttributeSet describing the supplied arguments
6919          * @return a new LayoutParams object
6920          */
generateLayoutParams(Context c, AttributeSet attrs)6921         public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
6922             return new LayoutParams(c, attrs);
6923         }
6924 
6925         /**
6926          * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
6927          * The default implementation does nothing and returns 0.
6928          *
6929          * @param dx            distance to scroll by in pixels. X increases as scroll position
6930          *                      approaches the right.
6931          * @param recycler      Recycler to use for fetching potentially cached views for a
6932          *                      position
6933          * @param state         Transient state of RecyclerView
6934          * @return The actual distance scrolled. The return value will be negative if dx was
6935          * negative and scrolling proceeeded in that direction.
6936          * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
6937          */
scrollHorizontallyBy(int dx, Recycler recycler, State state)6938         public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
6939             return 0;
6940         }
6941 
6942         /**
6943          * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
6944          * The default implementation does nothing and returns 0.
6945          *
6946          * @param dy            distance to scroll in pixels. Y increases as scroll position
6947          *                      approaches the bottom.
6948          * @param recycler      Recycler to use for fetching potentially cached views for a
6949          *                      position
6950          * @param state         Transient state of RecyclerView
6951          * @return The actual distance scrolled. The return value will be negative if dy was
6952          * negative and scrolling proceeeded in that direction.
6953          * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
6954          */
scrollVerticallyBy(int dy, Recycler recycler, State state)6955         public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
6956             return 0;
6957         }
6958 
6959         /**
6960          * Query if horizontal scrolling is currently supported. The default implementation
6961          * returns false.
6962          *
6963          * @return True if this LayoutManager can scroll the current contents horizontally
6964          */
canScrollHorizontally()6965         public boolean canScrollHorizontally() {
6966             return false;
6967         }
6968 
6969         /**
6970          * Query if vertical scrolling is currently supported. The default implementation
6971          * returns false.
6972          *
6973          * @return True if this LayoutManager can scroll the current contents vertically
6974          */
canScrollVertically()6975         public boolean canScrollVertically() {
6976             return false;
6977         }
6978 
6979         /**
6980          * Scroll to the specified adapter position.
6981          *
6982          * Actual position of the item on the screen depends on the LayoutManager implementation.
6983          * @param position Scroll to this adapter position.
6984          */
scrollToPosition(int position)6985         public void scrollToPosition(int position) {
6986             if (DEBUG) {
6987                 Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
6988             }
6989         }
6990 
6991         /**
6992          * <p>Smooth scroll to the specified adapter position.</p>
6993          * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
6994          * instance and call {@link #startSmoothScroll(SmoothScroller)}.
6995          * </p>
6996          * @param recyclerView The RecyclerView to which this layout manager is attached
6997          * @param state    Current State of RecyclerView
6998          * @param position Scroll to this adapter position.
6999          */
smoothScrollToPosition(RecyclerView recyclerView, State state, int position)7000         public void smoothScrollToPosition(RecyclerView recyclerView, State state,
7001                 int position) {
7002             Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
7003         }
7004 
7005         /**
7006          * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
7007          * <p>Calling this method will cancel any previous smooth scroll request.</p>
7008          * @param smoothScroller Instance which defines how smooth scroll should be animated
7009          */
startSmoothScroll(SmoothScroller smoothScroller)7010         public void startSmoothScroll(SmoothScroller smoothScroller) {
7011             if (mSmoothScroller != null && smoothScroller != mSmoothScroller
7012                     && mSmoothScroller.isRunning()) {
7013                 mSmoothScroller.stop();
7014             }
7015             mSmoothScroller = smoothScroller;
7016             mSmoothScroller.start(mRecyclerView, this);
7017         }
7018 
7019         /**
7020          * @return true if RecycylerView is currently in the state of smooth scrolling.
7021          */
isSmoothScrolling()7022         public boolean isSmoothScrolling() {
7023             return mSmoothScroller != null && mSmoothScroller.isRunning();
7024         }
7025 
7026 
7027         /**
7028          * Returns the resolved layout direction for this RecyclerView.
7029          *
7030          * @return {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout
7031          * direction is RTL or returns
7032          * {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction
7033          * is not RTL.
7034          */
getLayoutDirection()7035         public int getLayoutDirection() {
7036             return ViewCompat.getLayoutDirection(mRecyclerView);
7037         }
7038 
7039         /**
7040          * Ends all animations on the view created by the {@link ItemAnimator}.
7041          *
7042          * @param view The View for which the animations should be ended.
7043          * @see RecyclerView.ItemAnimator#endAnimations()
7044          */
endAnimation(View view)7045         public void endAnimation(View view) {
7046             if (mRecyclerView.mItemAnimator != null) {
7047                 mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
7048             }
7049         }
7050 
7051         /**
7052          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
7053          * to the layout that is known to be going away, either because it has been
7054          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
7055          * visible portion of the container but is being laid out in order to inform RecyclerView
7056          * in how to animate the item out of view.
7057          * <p>
7058          * Views added via this method are going to be invisible to LayoutManager after the
7059          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
7060          * or won't be included in {@link #getChildCount()} method.
7061          *
7062          * @param child View to add and then remove with animation.
7063          */
addDisappearingView(View child)7064         public void addDisappearingView(View child) {
7065             addDisappearingView(child, -1);
7066         }
7067 
7068         /**
7069          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
7070          * to the layout that is known to be going away, either because it has been
7071          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
7072          * visible portion of the container but is being laid out in order to inform RecyclerView
7073          * in how to animate the item out of view.
7074          * <p>
7075          * Views added via this method are going to be invisible to LayoutManager after the
7076          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
7077          * or won't be included in {@link #getChildCount()} method.
7078          *
7079          * @param child View to add and then remove with animation.
7080          * @param index Index of the view.
7081          */
addDisappearingView(View child, int index)7082         public void addDisappearingView(View child, int index) {
7083             addViewInt(child, index, true);
7084         }
7085 
7086         /**
7087          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
7088          * use this method to add views obtained from a {@link Recycler} using
7089          * {@link Recycler#getViewForPosition(int)}.
7090          *
7091          * @param child View to add
7092          */
addView(View child)7093         public void addView(View child) {
7094             addView(child, -1);
7095         }
7096 
7097         /**
7098          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
7099          * use this method to add views obtained from a {@link Recycler} using
7100          * {@link Recycler#getViewForPosition(int)}.
7101          *
7102          * @param child View to add
7103          * @param index Index to add child at
7104          */
addView(View child, int index)7105         public void addView(View child, int index) {
7106             addViewInt(child, index, false);
7107         }
7108 
addViewInt(View child, int index, boolean disappearing)7109         private void addViewInt(View child, int index, boolean disappearing) {
7110             final ViewHolder holder = getChildViewHolderInt(child);
7111             if (disappearing || holder.isRemoved()) {
7112                 // these views will be hidden at the end of the layout pass.
7113                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
7114             } else {
7115                 // This may look like unnecessary but may happen if layout manager supports
7116                 // predictive layouts and adapter removed then re-added the same item.
7117                 // In this case, added version will be visible in the post layout (because add is
7118                 // deferred) but RV will still bind it to the same View.
7119                 // So if a View re-appears in post layout pass, remove it from disappearing list.
7120                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
7121             }
7122             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
7123             if (holder.wasReturnedFromScrap() || holder.isScrap()) {
7124                 if (holder.isScrap()) {
7125                     holder.unScrap();
7126                 } else {
7127                     holder.clearReturnedFromScrapFlag();
7128                 }
7129                 mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
7130                 if (DISPATCH_TEMP_DETACH) {
7131                     ViewCompat.dispatchFinishTemporaryDetach(child);
7132                 }
7133             } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
7134                 // ensure in correct position
7135                 int currentIndex = mChildHelper.indexOfChild(child);
7136                 if (index == -1) {
7137                     index = mChildHelper.getChildCount();
7138                 }
7139                 if (currentIndex == -1) {
7140                     throw new IllegalStateException("Added View has RecyclerView as parent but"
7141                             + " view is not a real child. Unfiltered index:"
7142                             + mRecyclerView.indexOfChild(child));
7143                 }
7144                 if (currentIndex != index) {
7145                     mRecyclerView.mLayout.moveView(currentIndex, index);
7146                 }
7147             } else {
7148                 mChildHelper.addView(child, index, false);
7149                 lp.mInsetsDirty = true;
7150                 if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
7151                     mSmoothScroller.onChildAttachedToWindow(child);
7152                 }
7153             }
7154             if (lp.mPendingInvalidate) {
7155                 if (DEBUG) {
7156                     Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
7157                 }
7158                 holder.itemView.invalidate();
7159                 lp.mPendingInvalidate = false;
7160             }
7161         }
7162 
7163         /**
7164          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
7165          * use this method to completely remove a child view that is no longer needed.
7166          * LayoutManagers should strongly consider recycling removed views using
7167          * {@link Recycler#recycleView(android.view.View)}.
7168          *
7169          * @param child View to remove
7170          */
removeView(View child)7171         public void removeView(View child) {
7172             mChildHelper.removeView(child);
7173         }
7174 
7175         /**
7176          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
7177          * use this method to completely remove a child view that is no longer needed.
7178          * LayoutManagers should strongly consider recycling removed views using
7179          * {@link Recycler#recycleView(android.view.View)}.
7180          *
7181          * @param index Index of the child view to remove
7182          */
removeViewAt(int index)7183         public void removeViewAt(int index) {
7184             final View child = getChildAt(index);
7185             if (child != null) {
7186                 mChildHelper.removeViewAt(index);
7187             }
7188         }
7189 
7190         /**
7191          * Remove all views from the currently attached RecyclerView. This will not recycle
7192          * any of the affected views; the LayoutManager is responsible for doing so if desired.
7193          */
removeAllViews()7194         public void removeAllViews() {
7195             // Only remove non-animating views
7196             final int childCount = getChildCount();
7197             for (int i = childCount - 1; i >= 0; i--) {
7198                 mChildHelper.removeViewAt(i);
7199             }
7200         }
7201 
7202         /**
7203          * Returns offset of the RecyclerView's text baseline from the its top boundary.
7204          *
7205          * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
7206          * there is no baseline.
7207          */
getBaseline()7208         public int getBaseline() {
7209             return -1;
7210         }
7211 
7212         /**
7213          * Returns the adapter position of the item represented by the given View. This does not
7214          * contain any adapter changes that might have happened after the last layout.
7215          *
7216          * @param view The view to query
7217          * @return The adapter position of the item which is rendered by this View.
7218          */
getPosition(View view)7219         public int getPosition(View view) {
7220             return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
7221         }
7222 
7223         /**
7224          * Returns the View type defined by the adapter.
7225          *
7226          * @param view The view to query
7227          * @return The type of the view assigned by the adapter.
7228          */
getItemViewType(View view)7229         public int getItemViewType(View view) {
7230             return getChildViewHolderInt(view).getItemViewType();
7231         }
7232 
7233         /**
7234          * Traverses the ancestors of the given view and returns the item view that contains it
7235          * and also a direct child of the LayoutManager.
7236          * <p>
7237          * Note that this method may return null if the view is a child of the RecyclerView but
7238          * not a child of the LayoutManager (e.g. running a disappear animation).
7239          *
7240          * @param view The view that is a descendant of the LayoutManager.
7241          *
7242          * @return The direct child of the LayoutManager which contains the given view or null if
7243          * the provided view is not a descendant of this LayoutManager.
7244          *
7245          * @see RecyclerView#getChildViewHolder(View)
7246          * @see RecyclerView#findContainingViewHolder(View)
7247          */
7248         @Nullable
findContainingItemView(View view)7249         public View findContainingItemView(View view) {
7250             if (mRecyclerView == null) {
7251                 return null;
7252             }
7253             View found = mRecyclerView.findContainingItemView(view);
7254             if (found == null) {
7255                 return null;
7256             }
7257             if (mChildHelper.isHidden(found)) {
7258                 return null;
7259             }
7260             return found;
7261         }
7262 
7263         /**
7264          * Finds the view which represents the given adapter position.
7265          * <p>
7266          * This method traverses each child since it has no information about child order.
7267          * Override this method to improve performance if your LayoutManager keeps data about
7268          * child views.
7269          * <p>
7270          * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
7271          *
7272          * @param position Position of the item in adapter
7273          * @return The child view that represents the given position or null if the position is not
7274          * laid out
7275          */
findViewByPosition(int position)7276         public View findViewByPosition(int position) {
7277             final int childCount = getChildCount();
7278             for (int i = 0; i < childCount; i++) {
7279                 View child = getChildAt(i);
7280                 ViewHolder vh = getChildViewHolderInt(child);
7281                 if (vh == null) {
7282                     continue;
7283                 }
7284                 if (vh.getLayoutPosition() == position && !vh.shouldIgnore() &&
7285                         (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
7286                     return child;
7287                 }
7288             }
7289             return null;
7290         }
7291 
7292         /**
7293          * Temporarily detach a child view.
7294          *
7295          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
7296          * views currently attached to the RecyclerView. Generally LayoutManager implementations
7297          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
7298          * so that the detached view may be rebound and reused.</p>
7299          *
7300          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
7301          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
7302          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
7303          * before the LayoutManager entry point method called by RecyclerView returns.</p>
7304          *
7305          * @param child Child to detach
7306          */
detachView(View child)7307         public void detachView(View child) {
7308             final int ind = mChildHelper.indexOfChild(child);
7309             if (ind >= 0) {
7310                 detachViewInternal(ind, child);
7311             }
7312         }
7313 
7314         /**
7315          * Temporarily detach a child view.
7316          *
7317          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
7318          * views currently attached to the RecyclerView. Generally LayoutManager implementations
7319          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
7320          * so that the detached view may be rebound and reused.</p>
7321          *
7322          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
7323          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
7324          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
7325          * before the LayoutManager entry point method called by RecyclerView returns.</p>
7326          *
7327          * @param index Index of the child to detach
7328          */
detachViewAt(int index)7329         public void detachViewAt(int index) {
7330             detachViewInternal(index, getChildAt(index));
7331         }
7332 
detachViewInternal(int index, View view)7333         private void detachViewInternal(int index, View view) {
7334             if (DISPATCH_TEMP_DETACH) {
7335                 ViewCompat.dispatchStartTemporaryDetach(view);
7336             }
7337             mChildHelper.detachViewFromParent(index);
7338         }
7339 
7340         /**
7341          * Reattach a previously {@link #detachView(android.view.View) detached} view.
7342          * This method should not be used to reattach views that were previously
7343          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
7344          *
7345          * @param child Child to reattach
7346          * @param index Intended child index for child
7347          * @param lp LayoutParams for child
7348          */
attachView(View child, int index, LayoutParams lp)7349         public void attachView(View child, int index, LayoutParams lp) {
7350             ViewHolder vh = getChildViewHolderInt(child);
7351             if (vh.isRemoved()) {
7352                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
7353             } else {
7354                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
7355             }
7356             mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
7357             if (DISPATCH_TEMP_DETACH)  {
7358                 ViewCompat.dispatchFinishTemporaryDetach(child);
7359             }
7360         }
7361 
7362         /**
7363          * Reattach a previously {@link #detachView(android.view.View) detached} view.
7364          * This method should not be used to reattach views that were previously
7365          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
7366          *
7367          * @param child Child to reattach
7368          * @param index Intended child index for child
7369          */
attachView(View child, int index)7370         public void attachView(View child, int index) {
7371             attachView(child, index, (LayoutParams) child.getLayoutParams());
7372         }
7373 
7374         /**
7375          * Reattach a previously {@link #detachView(android.view.View) detached} view.
7376          * This method should not be used to reattach views that were previously
7377          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
7378          *
7379          * @param child Child to reattach
7380          */
attachView(View child)7381         public void attachView(View child) {
7382             attachView(child, -1);
7383         }
7384 
7385         /**
7386          * Finish removing a view that was previously temporarily
7387          * {@link #detachView(android.view.View) detached}.
7388          *
7389          * @param child Detached child to remove
7390          */
removeDetachedView(View child)7391         public void removeDetachedView(View child) {
7392             mRecyclerView.removeDetachedView(child, false);
7393         }
7394 
7395         /**
7396          * Moves a View from one position to another.
7397          *
7398          * @param fromIndex The View's initial index
7399          * @param toIndex The View's target index
7400          */
moveView(int fromIndex, int toIndex)7401         public void moveView(int fromIndex, int toIndex) {
7402             View view = getChildAt(fromIndex);
7403             if (view == null) {
7404                 throw new IllegalArgumentException("Cannot move a child from non-existing index:"
7405                         + fromIndex);
7406             }
7407             detachViewAt(fromIndex);
7408             attachView(view, toIndex);
7409         }
7410 
7411         /**
7412          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
7413          *
7414          * <p>Scrapping a view allows it to be rebound and reused to show updated or
7415          * different data.</p>
7416          *
7417          * @param child Child to detach and scrap
7418          * @param recycler Recycler to deposit the new scrap view into
7419          */
detachAndScrapView(View child, Recycler recycler)7420         public void detachAndScrapView(View child, Recycler recycler) {
7421             int index = mChildHelper.indexOfChild(child);
7422             scrapOrRecycleView(recycler, index, child);
7423         }
7424 
7425         /**
7426          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
7427          *
7428          * <p>Scrapping a view allows it to be rebound and reused to show updated or
7429          * different data.</p>
7430          *
7431          * @param index Index of child to detach and scrap
7432          * @param recycler Recycler to deposit the new scrap view into
7433          */
detachAndScrapViewAt(int index, Recycler recycler)7434         public void detachAndScrapViewAt(int index, Recycler recycler) {
7435             final View child = getChildAt(index);
7436             scrapOrRecycleView(recycler, index, child);
7437         }
7438 
7439         /**
7440          * Remove a child view and recycle it using the given Recycler.
7441          *
7442          * @param child Child to remove and recycle
7443          * @param recycler Recycler to use to recycle child
7444          */
removeAndRecycleView(View child, Recycler recycler)7445         public void removeAndRecycleView(View child, Recycler recycler) {
7446             removeView(child);
7447             recycler.recycleView(child);
7448         }
7449 
7450         /**
7451          * Remove a child view and recycle it using the given Recycler.
7452          *
7453          * @param index Index of child to remove and recycle
7454          * @param recycler Recycler to use to recycle child
7455          */
removeAndRecycleViewAt(int index, Recycler recycler)7456         public void removeAndRecycleViewAt(int index, Recycler recycler) {
7457             final View view = getChildAt(index);
7458             removeViewAt(index);
7459             recycler.recycleView(view);
7460         }
7461 
7462         /**
7463          * Return the current number of child views attached to the parent RecyclerView.
7464          * This does not include child views that were temporarily detached and/or scrapped.
7465          *
7466          * @return Number of attached children
7467          */
getChildCount()7468         public int getChildCount() {
7469             return mChildHelper != null ? mChildHelper.getChildCount() : 0;
7470         }
7471 
7472         /**
7473          * Return the child view at the given index
7474          * @param index Index of child to return
7475          * @return Child view at index
7476          */
getChildAt(int index)7477         public View getChildAt(int index) {
7478             return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
7479         }
7480 
7481         /**
7482          * Return the width measurement spec mode of the RecyclerView.
7483          * <p>
7484          * This value is set only if the LayoutManager opts into the auto measure api via
7485          * {@link #setAutoMeasureEnabled(boolean)}.
7486          * <p>
7487          * When RecyclerView is running a layout, this value is always set to
7488          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
7489          *
7490          * @return Width measure spec mode.
7491          *
7492          * @see View.MeasureSpec#getMode(int)
7493          * @see View#onMeasure(int, int)
7494          */
getWidthMode()7495         public int getWidthMode() {
7496             return mWidthMode;
7497         }
7498 
7499         /**
7500          * Return the height measurement spec mode of the RecyclerView.
7501          * <p>
7502          * This value is set only if the LayoutManager opts into the auto measure api via
7503          * {@link #setAutoMeasureEnabled(boolean)}.
7504          * <p>
7505          * When RecyclerView is running a layout, this value is always set to
7506          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
7507          *
7508          * @return Height measure spec mode.
7509          *
7510          * @see View.MeasureSpec#getMode(int)
7511          * @see View#onMeasure(int, int)
7512          */
getHeightMode()7513         public int getHeightMode() {
7514             return mHeightMode;
7515         }
7516 
7517         /**
7518          * Return the width of the parent RecyclerView
7519          *
7520          * @return Width in pixels
7521          */
getWidth()7522         public int getWidth() {
7523             return mWidth;
7524         }
7525 
7526         /**
7527          * Return the height of the parent RecyclerView
7528          *
7529          * @return Height in pixels
7530          */
getHeight()7531         public int getHeight() {
7532             return mHeight;
7533         }
7534 
7535         /**
7536          * Return the left padding of the parent RecyclerView
7537          *
7538          * @return Padding in pixels
7539          */
getPaddingLeft()7540         public int getPaddingLeft() {
7541             return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
7542         }
7543 
7544         /**
7545          * Return the top padding of the parent RecyclerView
7546          *
7547          * @return Padding in pixels
7548          */
getPaddingTop()7549         public int getPaddingTop() {
7550             return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
7551         }
7552 
7553         /**
7554          * Return the right padding of the parent RecyclerView
7555          *
7556          * @return Padding in pixels
7557          */
getPaddingRight()7558         public int getPaddingRight() {
7559             return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
7560         }
7561 
7562         /**
7563          * Return the bottom padding of the parent RecyclerView
7564          *
7565          * @return Padding in pixels
7566          */
getPaddingBottom()7567         public int getPaddingBottom() {
7568             return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
7569         }
7570 
7571         /**
7572          * Return the start padding of the parent RecyclerView
7573          *
7574          * @return Padding in pixels
7575          */
getPaddingStart()7576         public int getPaddingStart() {
7577             return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;
7578         }
7579 
7580         /**
7581          * Return the end padding of the parent RecyclerView
7582          *
7583          * @return Padding in pixels
7584          */
getPaddingEnd()7585         public int getPaddingEnd() {
7586             return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;
7587         }
7588 
7589         /**
7590          * Returns true if the RecyclerView this LayoutManager is bound to has focus.
7591          *
7592          * @return True if the RecyclerView has focus, false otherwise.
7593          * @see View#isFocused()
7594          */
isFocused()7595         public boolean isFocused() {
7596             return mRecyclerView != null && mRecyclerView.isFocused();
7597         }
7598 
7599         /**
7600          * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
7601          *
7602          * @return true if the RecyclerView has or contains focus
7603          * @see View#hasFocus()
7604          */
hasFocus()7605         public boolean hasFocus() {
7606             return mRecyclerView != null && mRecyclerView.hasFocus();
7607         }
7608 
7609         /**
7610          * Returns the item View which has or contains focus.
7611          *
7612          * @return A direct child of RecyclerView which has focus or contains the focused child.
7613          */
getFocusedChild()7614         public View getFocusedChild() {
7615             if (mRecyclerView == null) {
7616                 return null;
7617             }
7618             final View focused = mRecyclerView.getFocusedChild();
7619             if (focused == null || mChildHelper.isHidden(focused)) {
7620                 return null;
7621             }
7622             return focused;
7623         }
7624 
7625         /**
7626          * Returns the number of items in the adapter bound to the parent RecyclerView.
7627          * <p>
7628          * Note that this number is not necessarily equal to {@link State#getItemCount()}. In
7629          * methods where State is available, you should use {@link State#getItemCount()} instead.
7630          * For more details, check the documentation for {@link State#getItemCount()}.
7631          *
7632          * @return The number of items in the bound adapter
7633          * @see State#getItemCount()
7634          */
getItemCount()7635         public int getItemCount() {
7636             final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
7637             return a != null ? a.getItemCount() : 0;
7638         }
7639 
7640         /**
7641          * Offset all child views attached to the parent RecyclerView by dx pixels along
7642          * the horizontal axis.
7643          *
7644          * @param dx Pixels to offset by
7645          */
offsetChildrenHorizontal(int dx)7646         public void offsetChildrenHorizontal(int dx) {
7647             if (mRecyclerView != null) {
7648                 mRecyclerView.offsetChildrenHorizontal(dx);
7649             }
7650         }
7651 
7652         /**
7653          * Offset all child views attached to the parent RecyclerView by dy pixels along
7654          * the vertical axis.
7655          *
7656          * @param dy Pixels to offset by
7657          */
offsetChildrenVertical(int dy)7658         public void offsetChildrenVertical(int dy) {
7659             if (mRecyclerView != null) {
7660                 mRecyclerView.offsetChildrenVertical(dy);
7661             }
7662         }
7663 
7664         /**
7665          * Flags a view so that it will not be scrapped or recycled.
7666          * <p>
7667          * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
7668          * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
7669          * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
7670          * ignore the child.
7671          * <p>
7672          * Before this child can be recycled again, you have to call
7673          * {@link #stopIgnoringView(View)}.
7674          * <p>
7675          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
7676          *
7677          * @param view View to ignore.
7678          * @see #stopIgnoringView(View)
7679          */
ignoreView(View view)7680         public void ignoreView(View view) {
7681             if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
7682                 // checking this because calling this method on a recycled or detached view may
7683                 // cause loss of state.
7684                 throw new IllegalArgumentException("View should be fully attached to be ignored");
7685             }
7686             final ViewHolder vh = getChildViewHolderInt(view);
7687             vh.addFlags(ViewHolder.FLAG_IGNORE);
7688             mRecyclerView.mViewInfoStore.removeViewHolder(vh);
7689         }
7690 
7691         /**
7692          * View can be scrapped and recycled again.
7693          * <p>
7694          * Note that calling this method removes all information in the view holder.
7695          * <p>
7696          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
7697          *
7698          * @param view View to ignore.
7699          */
stopIgnoringView(View view)7700         public void stopIgnoringView(View view) {
7701             final ViewHolder vh = getChildViewHolderInt(view);
7702             vh.stopIgnoring();
7703             vh.resetInternal();
7704             vh.addFlags(ViewHolder.FLAG_INVALID);
7705         }
7706 
7707         /**
7708          * Temporarily detach and scrap all currently attached child views. Views will be scrapped
7709          * into the given Recycler. The Recycler may prefer to reuse scrap views before
7710          * other views that were previously recycled.
7711          *
7712          * @param recycler Recycler to scrap views into
7713          */
detachAndScrapAttachedViews(Recycler recycler)7714         public void detachAndScrapAttachedViews(Recycler recycler) {
7715             final int childCount = getChildCount();
7716             for (int i = childCount - 1; i >= 0; i--) {
7717                 final View v = getChildAt(i);
7718                 scrapOrRecycleView(recycler, i, v);
7719             }
7720         }
7721 
scrapOrRecycleView(Recycler recycler, int index, View view)7722         private void scrapOrRecycleView(Recycler recycler, int index, View view) {
7723             final ViewHolder viewHolder = getChildViewHolderInt(view);
7724             if (viewHolder.shouldIgnore()) {
7725                 if (DEBUG) {
7726                     Log.d(TAG, "ignoring view " + viewHolder);
7727                 }
7728                 return;
7729             }
7730             if (viewHolder.isInvalid() && !viewHolder.isRemoved() &&
7731                     !mRecyclerView.mAdapter.hasStableIds()) {
7732                 removeViewAt(index);
7733                 recycler.recycleViewHolderInternal(viewHolder);
7734             } else {
7735                 detachViewAt(index);
7736                 recycler.scrapView(view);
7737                 mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
7738             }
7739         }
7740 
7741         /**
7742          * Recycles the scrapped views.
7743          * <p>
7744          * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
7745          * the expected behavior if scrapped views are used for animations. Otherwise, we need to
7746          * call remove and invalidate RecyclerView to ensure UI update.
7747          *
7748          * @param recycler Recycler
7749          */
removeAndRecycleScrapInt(Recycler recycler)7750         void removeAndRecycleScrapInt(Recycler recycler) {
7751             final int scrapCount = recycler.getScrapCount();
7752             // Loop backward, recycler might be changed by removeDetachedView()
7753             for (int i = scrapCount - 1; i >= 0; i--) {
7754                 final View scrap = recycler.getScrapViewAt(i);
7755                 final ViewHolder vh = getChildViewHolderInt(scrap);
7756                 if (vh.shouldIgnore()) {
7757                     continue;
7758                 }
7759                 // If the scrap view is animating, we need to cancel them first. If we cancel it
7760                 // here, ItemAnimator callback may recycle it which will cause double recycling.
7761                 // To avoid this, we mark it as not recycleable before calling the item animator.
7762                 // Since removeDetachedView calls a user API, a common mistake (ending animations on
7763                 // the view) may recycle it too, so we guard it before we call user APIs.
7764                 vh.setIsRecyclable(false);
7765                 if (vh.isTmpDetached()) {
7766                     mRecyclerView.removeDetachedView(scrap, false);
7767                 }
7768                 if (mRecyclerView.mItemAnimator != null) {
7769                     mRecyclerView.mItemAnimator.endAnimation(vh);
7770                 }
7771                 vh.setIsRecyclable(true);
7772                 recycler.quickRecycleScrapView(scrap);
7773             }
7774             recycler.clearScrap();
7775             if (scrapCount > 0) {
7776                 mRecyclerView.invalidate();
7777             }
7778         }
7779 
7780 
7781         /**
7782          * Measure a child view using standard measurement policy, taking the padding
7783          * of the parent RecyclerView and any added item decorations into account.
7784          *
7785          * <p>If the RecyclerView can be scrolled in either dimension the caller may
7786          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
7787          *
7788          * @param child Child view to measure
7789          * @param widthUsed Width in pixels currently consumed by other views, if relevant
7790          * @param heightUsed Height in pixels currently consumed by other views, if relevant
7791          */
measureChild(View child, int widthUsed, int heightUsed)7792         public void measureChild(View child, int widthUsed, int heightUsed) {
7793             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
7794 
7795             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
7796             widthUsed += insets.left + insets.right;
7797             heightUsed += insets.top + insets.bottom;
7798             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
7799                     getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
7800                     canScrollHorizontally());
7801             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
7802                     getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
7803                     canScrollVertically());
7804             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
7805                 child.measure(widthSpec, heightSpec);
7806             }
7807         }
7808 
7809         /**
7810          * RecyclerView internally does its own View measurement caching which should help with
7811          * WRAP_CONTENT.
7812          * <p>
7813          * Use this method if the View is already measured once in this layout pass.
7814          */
shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp)7815         boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
7816             return !mMeasurementCacheEnabled
7817                     || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
7818                     || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
7819         }
7820 
7821         // we may consider making this public
7822         /**
7823          * RecyclerView internally does its own View measurement caching which should help with
7824          * WRAP_CONTENT.
7825          * <p>
7826          * Use this method if the View is not yet measured and you need to decide whether to
7827          * measure this View or not.
7828          */
shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp)7829         boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
7830             return child.isLayoutRequested()
7831                     || !mMeasurementCacheEnabled
7832                     || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
7833                     || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
7834         }
7835 
7836         /**
7837          * In addition to the View Framework's measurement cache, RecyclerView uses its own
7838          * additional measurement cache for its children to avoid re-measuring them when not
7839          * necessary. It is on by default but it can be turned off via
7840          * {@link #setMeasurementCacheEnabled(boolean)}.
7841          *
7842          * @return True if measurement cache is enabled, false otherwise.
7843          *
7844          * @see #setMeasurementCacheEnabled(boolean)
7845          */
isMeasurementCacheEnabled()7846         public boolean isMeasurementCacheEnabled() {
7847             return mMeasurementCacheEnabled;
7848         }
7849 
7850         /**
7851          * Sets whether RecyclerView should use its own measurement cache for the children. This is
7852          * a more aggressive cache than the framework uses.
7853          *
7854          * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
7855          *
7856          * @see #isMeasurementCacheEnabled()
7857          */
setMeasurementCacheEnabled(boolean measurementCacheEnabled)7858         public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
7859             mMeasurementCacheEnabled = measurementCacheEnabled;
7860         }
7861 
isMeasurementUpToDate(int childSize, int spec, int dimension)7862         private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
7863             final int specMode = MeasureSpec.getMode(spec);
7864             final int specSize = MeasureSpec.getSize(spec);
7865             if (dimension > 0 && childSize != dimension) {
7866                 return false;
7867             }
7868             switch (specMode) {
7869                 case MeasureSpec.UNSPECIFIED:
7870                     return true;
7871                 case MeasureSpec.AT_MOST:
7872                     return specSize >= childSize;
7873                 case MeasureSpec.EXACTLY:
7874                     return  specSize == childSize;
7875             }
7876             return false;
7877         }
7878 
7879         /**
7880          * Measure a child view using standard measurement policy, taking the padding
7881          * of the parent RecyclerView, any added item decorations and the child margins
7882          * into account.
7883          *
7884          * <p>If the RecyclerView can be scrolled in either dimension the caller may
7885          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
7886          *
7887          * @param child Child view to measure
7888          * @param widthUsed Width in pixels currently consumed by other views, if relevant
7889          * @param heightUsed Height in pixels currently consumed by other views, if relevant
7890          */
measureChildWithMargins(View child, int widthUsed, int heightUsed)7891         public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
7892             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
7893 
7894             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
7895             widthUsed += insets.left + insets.right;
7896             heightUsed += insets.top + insets.bottom;
7897 
7898             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
7899                     getPaddingLeft() + getPaddingRight() +
7900                             lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
7901                     canScrollHorizontally());
7902             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
7903                     getPaddingTop() + getPaddingBottom() +
7904                             lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
7905                     canScrollVertically());
7906             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
7907                 child.measure(widthSpec, heightSpec);
7908             }
7909         }
7910 
7911         /**
7912          * Calculate a MeasureSpec value for measuring a child view in one dimension.
7913          *
7914          * @param parentSize Size of the parent view where the child will be placed
7915          * @param padding Total space currently consumed by other elements of the parent
7916          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
7917          *                       Generally obtained from the child view's LayoutParams
7918          * @param canScroll true if the parent RecyclerView can scroll in this dimension
7919          *
7920          * @return a MeasureSpec value for the child view
7921          * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
7922          */
7923         @Deprecated
getChildMeasureSpec(int parentSize, int padding, int childDimension, boolean canScroll)7924         public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
7925                 boolean canScroll) {
7926             int size = Math.max(0, parentSize - padding);
7927             int resultSize = 0;
7928             int resultMode = 0;
7929             if (canScroll) {
7930                 if (childDimension >= 0) {
7931                     resultSize = childDimension;
7932                     resultMode = MeasureSpec.EXACTLY;
7933                 } else {
7934                     // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
7935                     // instead using UNSPECIFIED.
7936                     resultSize = 0;
7937                     resultMode = MeasureSpec.UNSPECIFIED;
7938                 }
7939             } else {
7940                 if (childDimension >= 0) {
7941                     resultSize = childDimension;
7942                     resultMode = MeasureSpec.EXACTLY;
7943                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
7944                     resultSize = size;
7945                     // TODO this should be my spec.
7946                     resultMode = MeasureSpec.EXACTLY;
7947                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
7948                     resultSize = size;
7949                     resultMode = MeasureSpec.AT_MOST;
7950                 }
7951             }
7952             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
7953         }
7954 
7955         /**
7956          * Calculate a MeasureSpec value for measuring a child view in one dimension.
7957          *
7958          * @param parentSize Size of the parent view where the child will be placed
7959          * @param parentMode The measurement spec mode of the parent
7960          * @param padding Total space currently consumed by other elements of parent
7961          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
7962          *                       Generally obtained from the child view's LayoutParams
7963          * @param canScroll true if the parent RecyclerView can scroll in this dimension
7964          *
7965          * @return a MeasureSpec value for the child view
7966          */
getChildMeasureSpec(int parentSize, int parentMode, int padding, int childDimension, boolean canScroll)7967         public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
7968                 int childDimension, boolean canScroll) {
7969             int size = Math.max(0, parentSize - padding);
7970             int resultSize = 0;
7971             int resultMode = 0;
7972             if (childDimension >= 0) {
7973                 resultSize = childDimension;
7974                 resultMode = MeasureSpec.EXACTLY;
7975             } else if (canScroll) {
7976                  if (childDimension == LayoutParams.MATCH_PARENT){
7977                     switch (parentMode) {
7978                         case MeasureSpec.AT_MOST:
7979                         case MeasureSpec.EXACTLY:
7980                             resultSize = size;
7981                             resultMode = parentMode;
7982                             break;
7983                         case MeasureSpec.UNSPECIFIED:
7984                             resultSize = 0;
7985                             resultMode = MeasureSpec.UNSPECIFIED;
7986                             break;
7987                     }
7988                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
7989                     resultSize = 0;
7990                     resultMode = MeasureSpec.UNSPECIFIED;
7991                 }
7992             } else {
7993                 if (childDimension == LayoutParams.MATCH_PARENT) {
7994                     resultSize = size;
7995                     resultMode = parentMode;
7996                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
7997                     resultSize = size;
7998                     if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
7999                         resultMode = MeasureSpec.AT_MOST;
8000                     } else {
8001                         resultMode = MeasureSpec.UNSPECIFIED;
8002                     }
8003 
8004                 }
8005             }
8006             //noinspection WrongConstant
8007             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
8008         }
8009 
8010         /**
8011          * Returns the measured width of the given child, plus the additional size of
8012          * any insets applied by {@link ItemDecoration ItemDecorations}.
8013          *
8014          * @param child Child view to query
8015          * @return child's measured width plus <code>ItemDecoration</code> insets
8016          *
8017          * @see View#getMeasuredWidth()
8018          */
getDecoratedMeasuredWidth(View child)8019         public int getDecoratedMeasuredWidth(View child) {
8020             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8021             return child.getMeasuredWidth() + insets.left + insets.right;
8022         }
8023 
8024         /**
8025          * Returns the measured height of the given child, plus the additional size of
8026          * any insets applied by {@link ItemDecoration ItemDecorations}.
8027          *
8028          * @param child Child view to query
8029          * @return child's measured height plus <code>ItemDecoration</code> insets
8030          *
8031          * @see View#getMeasuredHeight()
8032          */
getDecoratedMeasuredHeight(View child)8033         public int getDecoratedMeasuredHeight(View child) {
8034             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8035             return child.getMeasuredHeight() + insets.top + insets.bottom;
8036         }
8037 
8038         /**
8039          * Lay out the given child view within the RecyclerView using coordinates that
8040          * include any current {@link ItemDecoration ItemDecorations}.
8041          *
8042          * <p>LayoutManagers should prefer working in sizes and coordinates that include
8043          * item decoration insets whenever possible. This allows the LayoutManager to effectively
8044          * ignore decoration insets within measurement and layout code. See the following
8045          * methods:</p>
8046          * <ul>
8047          *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
8048          *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
8049          *     <li>{@link #measureChild(View, int, int)}</li>
8050          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
8051          *     <li>{@link #getDecoratedLeft(View)}</li>
8052          *     <li>{@link #getDecoratedTop(View)}</li>
8053          *     <li>{@link #getDecoratedRight(View)}</li>
8054          *     <li>{@link #getDecoratedBottom(View)}</li>
8055          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
8056          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
8057          * </ul>
8058          *
8059          * @param child Child to lay out
8060          * @param left Left edge, with item decoration insets included
8061          * @param top Top edge, with item decoration insets included
8062          * @param right Right edge, with item decoration insets included
8063          * @param bottom Bottom edge, with item decoration insets included
8064          *
8065          * @see View#layout(int, int, int, int)
8066          * @see #layoutDecoratedWithMargins(View, int, int, int, int)
8067          */
layoutDecorated(View child, int left, int top, int right, int bottom)8068         public void layoutDecorated(View child, int left, int top, int right, int bottom) {
8069             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8070             child.layout(left + insets.left, top + insets.top, right - insets.right,
8071                     bottom - insets.bottom);
8072         }
8073 
8074         /**
8075          * Lay out the given child view within the RecyclerView using coordinates that
8076          * include any current {@link ItemDecoration ItemDecorations} and margins.
8077          *
8078          * <p>LayoutManagers should prefer working in sizes and coordinates that include
8079          * item decoration insets whenever possible. This allows the LayoutManager to effectively
8080          * ignore decoration insets within measurement and layout code. See the following
8081          * methods:</p>
8082          * <ul>
8083          *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
8084          *     <li>{@link #measureChild(View, int, int)}</li>
8085          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
8086          *     <li>{@link #getDecoratedLeft(View)}</li>
8087          *     <li>{@link #getDecoratedTop(View)}</li>
8088          *     <li>{@link #getDecoratedRight(View)}</li>
8089          *     <li>{@link #getDecoratedBottom(View)}</li>
8090          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
8091          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
8092          * </ul>
8093          *
8094          * @param child Child to lay out
8095          * @param left Left edge, with item decoration insets and left margin included
8096          * @param top Top edge, with item decoration insets and top margin included
8097          * @param right Right edge, with item decoration insets and right margin included
8098          * @param bottom Bottom edge, with item decoration insets and bottom margin included
8099          *
8100          * @see View#layout(int, int, int, int)
8101          * @see #layoutDecorated(View, int, int, int, int)
8102          */
layoutDecoratedWithMargins(View child, int left, int top, int right, int bottom)8103         public void layoutDecoratedWithMargins(View child, int left, int top, int right,
8104                 int bottom) {
8105             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8106             final Rect insets = lp.mDecorInsets;
8107             child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
8108                     right - insets.right - lp.rightMargin,
8109                     bottom - insets.bottom - lp.bottomMargin);
8110         }
8111 
8112         /**
8113          * Calculates the bounding box of the View while taking into account its matrix changes
8114          * (translation, scale etc) with respect to the RecyclerView.
8115          * <p>
8116          * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
8117          * the View's matrix so that the decor offsets also go through the same transformation.
8118          *
8119          * @param child The ItemView whose bounding box should be calculated.
8120          * @param includeDecorInsets True if the decor insets should be included in the bounding box
8121          * @param out The rectangle into which the output will be written.
8122          */
getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out)8123         public void getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out) {
8124             if (includeDecorInsets) {
8125                 Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8126                 out.set(-insets.left, -insets.top,
8127                         child.getWidth() + insets.right, child.getHeight() + insets.bottom);
8128             } else {
8129                 out.set(0, 0, child.getWidth(), child.getHeight());
8130             }
8131 
8132             if (mRecyclerView != null) {
8133                 final Matrix childMatrix = ViewCompat.getMatrix(child);
8134                 if (childMatrix != null && !childMatrix.isIdentity()) {
8135                     final RectF tempRectF = mRecyclerView.mTempRectF;
8136                     tempRectF.set(out);
8137                     childMatrix.mapRect(tempRectF);
8138                     out.set(
8139                             (int) Math.floor(tempRectF.left),
8140                             (int) Math.floor(tempRectF.top),
8141                             (int) Math.ceil(tempRectF.right),
8142                             (int) Math.ceil(tempRectF.bottom)
8143                     );
8144                 }
8145             }
8146             out.offset(child.getLeft(), child.getTop());
8147         }
8148 
8149         /**
8150          * Returns the bounds of the view including its decoration and margins.
8151          *
8152          * @param view The view element to check
8153          * @param outBounds A rect that will receive the bounds of the element including its
8154          *                  decoration and margins.
8155          */
getDecoratedBoundsWithMargins(View view, Rect outBounds)8156         public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
8157             RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
8158         }
8159 
8160         /**
8161          * Returns the left edge of the given child view within its parent, offset by any applied
8162          * {@link ItemDecoration ItemDecorations}.
8163          *
8164          * @param child Child to query
8165          * @return Child left edge with offsets applied
8166          * @see #getLeftDecorationWidth(View)
8167          */
getDecoratedLeft(View child)8168         public int getDecoratedLeft(View child) {
8169             return child.getLeft() - getLeftDecorationWidth(child);
8170         }
8171 
8172         /**
8173          * Returns the top edge of the given child view within its parent, offset by any applied
8174          * {@link ItemDecoration ItemDecorations}.
8175          *
8176          * @param child Child to query
8177          * @return Child top edge with offsets applied
8178          * @see #getTopDecorationHeight(View)
8179          */
getDecoratedTop(View child)8180         public int getDecoratedTop(View child) {
8181             return child.getTop() - getTopDecorationHeight(child);
8182         }
8183 
8184         /**
8185          * Returns the right edge of the given child view within its parent, offset by any applied
8186          * {@link ItemDecoration ItemDecorations}.
8187          *
8188          * @param child Child to query
8189          * @return Child right edge with offsets applied
8190          * @see #getRightDecorationWidth(View)
8191          */
getDecoratedRight(View child)8192         public int getDecoratedRight(View child) {
8193             return child.getRight() + getRightDecorationWidth(child);
8194         }
8195 
8196         /**
8197          * Returns the bottom edge of the given child view within its parent, offset by any applied
8198          * {@link ItemDecoration ItemDecorations}.
8199          *
8200          * @param child Child to query
8201          * @return Child bottom edge with offsets applied
8202          * @see #getBottomDecorationHeight(View)
8203          */
getDecoratedBottom(View child)8204         public int getDecoratedBottom(View child) {
8205             return child.getBottom() + getBottomDecorationHeight(child);
8206         }
8207 
8208         /**
8209          * Calculates the item decor insets applied to the given child and updates the provided
8210          * Rect instance with the inset values.
8211          * <ul>
8212          *     <li>The Rect's left is set to the total width of left decorations.</li>
8213          *     <li>The Rect's top is set to the total height of top decorations.</li>
8214          *     <li>The Rect's right is set to the total width of right decorations.</li>
8215          *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
8216          * </ul>
8217          * <p>
8218          * Note that item decorations are automatically calculated when one of the LayoutManager's
8219          * measure child methods is called. If you need to measure the child with custom specs via
8220          * {@link View#measure(int, int)}, you can use this method to get decorations.
8221          *
8222          * @param child The child view whose decorations should be calculated
8223          * @param outRect The Rect to hold result values
8224          */
calculateItemDecorationsForChild(View child, Rect outRect)8225         public void calculateItemDecorationsForChild(View child, Rect outRect) {
8226             if (mRecyclerView == null) {
8227                 outRect.set(0, 0, 0, 0);
8228                 return;
8229             }
8230             Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8231             outRect.set(insets);
8232         }
8233 
8234         /**
8235          * Returns the total height of item decorations applied to child's top.
8236          * <p>
8237          * Note that this value is not updated until the View is measured or
8238          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8239          *
8240          * @param child Child to query
8241          * @return The total height of item decorations applied to the child's top.
8242          * @see #getDecoratedTop(View)
8243          * @see #calculateItemDecorationsForChild(View, Rect)
8244          */
getTopDecorationHeight(View child)8245         public int getTopDecorationHeight(View child) {
8246             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
8247         }
8248 
8249         /**
8250          * Returns the total height of item decorations applied to child's bottom.
8251          * <p>
8252          * Note that this value is not updated until the View is measured or
8253          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8254          *
8255          * @param child Child to query
8256          * @return The total height of item decorations applied to the child's bottom.
8257          * @see #getDecoratedBottom(View)
8258          * @see #calculateItemDecorationsForChild(View, Rect)
8259          */
getBottomDecorationHeight(View child)8260         public int getBottomDecorationHeight(View child) {
8261             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
8262         }
8263 
8264         /**
8265          * Returns the total width of item decorations applied to child's left.
8266          * <p>
8267          * Note that this value is not updated until the View is measured or
8268          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8269          *
8270          * @param child Child to query
8271          * @return The total width of item decorations applied to the child's left.
8272          * @see #getDecoratedLeft(View)
8273          * @see #calculateItemDecorationsForChild(View, Rect)
8274          */
getLeftDecorationWidth(View child)8275         public int getLeftDecorationWidth(View child) {
8276             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
8277         }
8278 
8279         /**
8280          * Returns the total width of item decorations applied to child's right.
8281          * <p>
8282          * Note that this value is not updated until the View is measured or
8283          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8284          *
8285          * @param child Child to query
8286          * @return The total width of item decorations applied to the child's right.
8287          * @see #getDecoratedRight(View)
8288          * @see #calculateItemDecorationsForChild(View, Rect)
8289          */
getRightDecorationWidth(View child)8290         public int getRightDecorationWidth(View child) {
8291             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
8292         }
8293 
8294         /**
8295          * Called when searching for a focusable view in the given direction has failed
8296          * for the current content of the RecyclerView.
8297          *
8298          * <p>This is the LayoutManager's opportunity to populate views in the given direction
8299          * to fulfill the request if it can. The LayoutManager should attach and return
8300          * the view to be focused. The default implementation returns null.</p>
8301          *
8302          * @param focused   The currently focused view
8303          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
8304          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
8305          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
8306          *                  or 0 for not applicable
8307          * @param recycler  The recycler to use for obtaining views for currently offscreen items
8308          * @param state     Transient state of RecyclerView
8309          * @return The chosen view to be focused
8310          */
8311         @Nullable
onFocusSearchFailed(View focused, int direction, Recycler recycler, State state)8312         public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
8313                 State state) {
8314             return null;
8315         }
8316 
8317         /**
8318          * This method gives a LayoutManager an opportunity to intercept the initial focus search
8319          * before the default behavior of {@link FocusFinder} is used. If this method returns
8320          * null FocusFinder will attempt to find a focusable child view. If it fails
8321          * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
8322          * will be called to give the LayoutManager an opportunity to add new views for items
8323          * that did not have attached views representing them. The LayoutManager should not add
8324          * or remove views from this method.
8325          *
8326          * @param focused The currently focused view
8327          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
8328          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
8329          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
8330          * @return A descendant view to focus or null to fall back to default behavior.
8331          *         The default implementation returns null.
8332          */
onInterceptFocusSearch(View focused, int direction)8333         public View onInterceptFocusSearch(View focused, int direction) {
8334             return null;
8335         }
8336 
8337         /**
8338          * Called when a child of the RecyclerView wants a particular rectangle to be positioned
8339          * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
8340          * android.graphics.Rect, boolean)} for more details.
8341          *
8342          * <p>The base implementation will attempt to perform a standard programmatic scroll
8343          * to bring the given rect into view, within the padded area of the RecyclerView.</p>
8344          *
8345          * @param child The direct child making the request.
8346          * @param rect  The rectangle in the child's coordinates the child
8347          *              wishes to be on the screen.
8348          * @param immediate True to forbid animated or delayed scrolling,
8349          *                  false otherwise
8350          * @return Whether the group scrolled to handle the operation
8351          */
requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect, boolean immediate)8352         public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
8353                 boolean immediate) {
8354             final int parentLeft = getPaddingLeft();
8355             final int parentTop = getPaddingTop();
8356             final int parentRight = getWidth() - getPaddingRight();
8357             final int parentBottom = getHeight() - getPaddingBottom();
8358             final int childLeft = child.getLeft() + rect.left - child.getScrollX();
8359             final int childTop = child.getTop() + rect.top - child.getScrollY();
8360             final int childRight = childLeft + rect.width();
8361             final int childBottom = childTop + rect.height();
8362 
8363             final int offScreenLeft = Math.min(0, childLeft - parentLeft);
8364             final int offScreenTop = Math.min(0, childTop - parentTop);
8365             final int offScreenRight = Math.max(0, childRight - parentRight);
8366             final int offScreenBottom = Math.max(0, childBottom - parentBottom);
8367 
8368             // Favor the "start" layout direction over the end when bringing one side or the other
8369             // of a large rect into view. If we decide to bring in end because start is already
8370             // visible, limit the scroll such that start won't go out of bounds.
8371             final int dx;
8372             if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) {
8373                 dx = offScreenRight != 0 ? offScreenRight
8374                         : Math.max(offScreenLeft, childRight - parentRight);
8375             } else {
8376                 dx = offScreenLeft != 0 ? offScreenLeft
8377                         : Math.min(childLeft - parentLeft, offScreenRight);
8378             }
8379 
8380             // Favor bringing the top into view over the bottom. If top is already visible and
8381             // we should scroll to make bottom visible, make sure top does not go out of bounds.
8382             final int dy = offScreenTop != 0 ? offScreenTop
8383                     : Math.min(childTop - parentTop, offScreenBottom);
8384 
8385             if (dx != 0 || dy != 0) {
8386                 if (immediate) {
8387                     parent.scrollBy(dx, dy);
8388                 } else {
8389                     parent.smoothScrollBy(dx, dy);
8390                 }
8391                 return true;
8392             }
8393             return false;
8394         }
8395 
8396         /**
8397          * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
8398          */
8399         @Deprecated
onRequestChildFocus(RecyclerView parent, View child, View focused)8400         public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
8401             // eat the request if we are in the middle of a scroll or layout
8402             return isSmoothScrolling() || parent.isComputingLayout();
8403         }
8404 
8405         /**
8406          * Called when a descendant view of the RecyclerView requests focus.
8407          *
8408          * <p>A LayoutManager wishing to keep focused views aligned in a specific
8409          * portion of the view may implement that behavior in an override of this method.</p>
8410          *
8411          * <p>If the LayoutManager executes different behavior that should override the default
8412          * behavior of scrolling the focused child on screen instead of running alongside it,
8413          * this method should return true.</p>
8414          *
8415          * @param parent  The RecyclerView hosting this LayoutManager
8416          * @param state   Current state of RecyclerView
8417          * @param child   Direct child of the RecyclerView containing the newly focused view
8418          * @param focused The newly focused view. This may be the same view as child or it may be
8419          *                null
8420          * @return true if the default scroll behavior should be suppressed
8421          */
onRequestChildFocus(RecyclerView parent, State state, View child, View focused)8422         public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
8423                 View focused) {
8424             return onRequestChildFocus(parent, child, focused);
8425         }
8426 
8427         /**
8428          * Called if the RecyclerView this LayoutManager is bound to has a different adapter set.
8429          * The LayoutManager may use this opportunity to clear caches and configure state such
8430          * that it can relayout appropriately with the new data and potentially new view types.
8431          *
8432          * <p>The default implementation removes all currently attached views.</p>
8433          *
8434          * @param oldAdapter The previous adapter instance. Will be null if there was previously no
8435          *                   adapter.
8436          * @param newAdapter The new adapter instance. Might be null if
8437          *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
8438          */
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter)8439         public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
8440         }
8441 
8442         /**
8443          * Called to populate focusable views within the RecyclerView.
8444          *
8445          * <p>The LayoutManager implementation should return <code>true</code> if the default
8446          * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
8447          * suppressed.</p>
8448          *
8449          * <p>The default implementation returns <code>false</code> to trigger RecyclerView
8450          * to fall back to the default ViewGroup behavior.</p>
8451          *
8452          * @param recyclerView The RecyclerView hosting this LayoutManager
8453          * @param views List of output views. This method should add valid focusable views
8454          *              to this list.
8455          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
8456          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
8457          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
8458          * @param focusableMode The type of focusables to be added.
8459          *
8460          * @return true to suppress the default behavior, false to add default focusables after
8461          *         this method returns.
8462          *
8463          * @see #FOCUSABLES_ALL
8464          * @see #FOCUSABLES_TOUCH_MODE
8465          */
onAddFocusables(RecyclerView recyclerView, ArrayList<View> views, int direction, int focusableMode)8466         public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,
8467                 int direction, int focusableMode) {
8468             return false;
8469         }
8470 
8471         /**
8472          * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving
8473          * detailed information on what has actually changed.
8474          *
8475          * @param recyclerView
8476          */
onItemsChanged(RecyclerView recyclerView)8477         public void onItemsChanged(RecyclerView recyclerView) {
8478         }
8479 
8480         /**
8481          * Called when items have been added to the adapter. The LayoutManager may choose to
8482          * requestLayout if the inserted items would require refreshing the currently visible set
8483          * of child views. (e.g. currently empty space would be filled by appended items, etc.)
8484          *
8485          * @param recyclerView
8486          * @param positionStart
8487          * @param itemCount
8488          */
onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount)8489         public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
8490         }
8491 
8492         /**
8493          * Called when items have been removed from the adapter.
8494          *
8495          * @param recyclerView
8496          * @param positionStart
8497          * @param itemCount
8498          */
onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount)8499         public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
8500         }
8501 
8502         /**
8503          * Called when items have been changed in the adapter.
8504          * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
8505          * instead, then this callback will not be invoked.
8506          *
8507          * @param recyclerView
8508          * @param positionStart
8509          * @param itemCount
8510          */
onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount)8511         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
8512         }
8513 
8514         /**
8515          * Called when items have been changed in the adapter and with optional payload.
8516          * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
8517          *
8518          * @param recyclerView
8519          * @param positionStart
8520          * @param itemCount
8521          * @param payload
8522          */
onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount, Object payload)8523         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
8524                 Object payload) {
8525             onItemsUpdated(recyclerView, positionStart, itemCount);
8526         }
8527 
8528         /**
8529          * Called when an item is moved withing the adapter.
8530          * <p>
8531          * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
8532          * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
8533          * is called.
8534          *
8535          * @param recyclerView
8536          * @param from
8537          * @param to
8538          * @param itemCount
8539          */
onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount)8540         public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
8541 
8542         }
8543 
8544 
8545         /**
8546          * <p>Override this method if you want to support scroll bars.</p>
8547          *
8548          * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
8549          *
8550          * <p>Default implementation returns 0.</p>
8551          *
8552          * @param state Current state of RecyclerView
8553          * @return The horizontal extent of the scrollbar's thumb
8554          * @see RecyclerView#computeHorizontalScrollExtent()
8555          */
computeHorizontalScrollExtent(State state)8556         public int computeHorizontalScrollExtent(State state) {
8557             return 0;
8558         }
8559 
8560         /**
8561          * <p>Override this method if you want to support scroll bars.</p>
8562          *
8563          * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
8564          *
8565          * <p>Default implementation returns 0.</p>
8566          *
8567          * @param state Current State of RecyclerView where you can find total item count
8568          * @return The horizontal offset of the scrollbar's thumb
8569          * @see RecyclerView#computeHorizontalScrollOffset()
8570          */
computeHorizontalScrollOffset(State state)8571         public int computeHorizontalScrollOffset(State state) {
8572             return 0;
8573         }
8574 
8575         /**
8576          * <p>Override this method if you want to support scroll bars.</p>
8577          *
8578          * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
8579          *
8580          * <p>Default implementation returns 0.</p>
8581          *
8582          * @param state Current State of RecyclerView where you can find total item count
8583          * @return The total horizontal range represented by the vertical scrollbar
8584          * @see RecyclerView#computeHorizontalScrollRange()
8585          */
computeHorizontalScrollRange(State state)8586         public int computeHorizontalScrollRange(State state) {
8587             return 0;
8588         }
8589 
8590         /**
8591          * <p>Override this method if you want to support scroll bars.</p>
8592          *
8593          * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
8594          *
8595          * <p>Default implementation returns 0.</p>
8596          *
8597          * @param state Current state of RecyclerView
8598          * @return The vertical extent of the scrollbar's thumb
8599          * @see RecyclerView#computeVerticalScrollExtent()
8600          */
computeVerticalScrollExtent(State state)8601         public int computeVerticalScrollExtent(State state) {
8602             return 0;
8603         }
8604 
8605         /**
8606          * <p>Override this method if you want to support scroll bars.</p>
8607          *
8608          * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
8609          *
8610          * <p>Default implementation returns 0.</p>
8611          *
8612          * @param state Current State of RecyclerView where you can find total item count
8613          * @return The vertical offset of the scrollbar's thumb
8614          * @see RecyclerView#computeVerticalScrollOffset()
8615          */
computeVerticalScrollOffset(State state)8616         public int computeVerticalScrollOffset(State state) {
8617             return 0;
8618         }
8619 
8620         /**
8621          * <p>Override this method if you want to support scroll bars.</p>
8622          *
8623          * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
8624          *
8625          * <p>Default implementation returns 0.</p>
8626          *
8627          * @param state Current State of RecyclerView where you can find total item count
8628          * @return The total vertical range represented by the vertical scrollbar
8629          * @see RecyclerView#computeVerticalScrollRange()
8630          */
computeVerticalScrollRange(State state)8631         public int computeVerticalScrollRange(State state) {
8632             return 0;
8633         }
8634 
8635         /**
8636          * Measure the attached RecyclerView. Implementations must call
8637          * {@link #setMeasuredDimension(int, int)} before returning.
8638          *
8639          * <p>The default implementation will handle EXACTLY measurements and respect
8640          * the minimum width and height properties of the host RecyclerView if measured
8641          * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
8642          * will consume all available space.</p>
8643          *
8644          * @param recycler Recycler
8645          * @param state Transient state of RecyclerView
8646          * @param widthSpec Width {@link android.view.View.MeasureSpec}
8647          * @param heightSpec Height {@link android.view.View.MeasureSpec}
8648          */
onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec)8649         public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
8650             mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
8651         }
8652 
8653         /**
8654          * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
8655          * host RecyclerView.
8656          *
8657          * @param widthSize Measured width
8658          * @param heightSize Measured height
8659          */
setMeasuredDimension(int widthSize, int heightSize)8660         public void setMeasuredDimension(int widthSize, int heightSize) {
8661             mRecyclerView.setMeasuredDimension(widthSize, heightSize);
8662         }
8663 
8664         /**
8665          * @return The host RecyclerView's {@link View#getMinimumWidth()}
8666          */
getMinimumWidth()8667         public int getMinimumWidth() {
8668             return ViewCompat.getMinimumWidth(mRecyclerView);
8669         }
8670 
8671         /**
8672          * @return The host RecyclerView's {@link View#getMinimumHeight()}
8673          */
getMinimumHeight()8674         public int getMinimumHeight() {
8675             return ViewCompat.getMinimumHeight(mRecyclerView);
8676         }
8677         /**
8678          * <p>Called when the LayoutManager should save its state. This is a good time to save your
8679          * scroll position, configuration and anything else that may be required to restore the same
8680          * layout state if the LayoutManager is recreated.</p>
8681          * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
8682          * restore. This will let you share information between your LayoutManagers but it is also
8683          * your responsibility to make sure they use the same parcelable class.</p>
8684          *
8685          * @return Necessary information for LayoutManager to be able to restore its state
8686          */
onSaveInstanceState()8687         public Parcelable onSaveInstanceState() {
8688             return null;
8689         }
8690 
8691 
onRestoreInstanceState(Parcelable state)8692         public void onRestoreInstanceState(Parcelable state) {
8693 
8694         }
8695 
stopSmoothScroller()8696         void stopSmoothScroller() {
8697             if (mSmoothScroller != null) {
8698                 mSmoothScroller.stop();
8699             }
8700         }
8701 
onSmoothScrollerStopped(SmoothScroller smoothScroller)8702         private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
8703             if (mSmoothScroller == smoothScroller) {
8704                 mSmoothScroller = null;
8705             }
8706         }
8707 
8708         /**
8709          * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
8710          *
8711          * @param state The new scroll state for RecyclerView
8712          */
onScrollStateChanged(int state)8713         public void onScrollStateChanged(int state) {
8714         }
8715 
8716         /**
8717          * Removes all views and recycles them using the given recycler.
8718          * <p>
8719          * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
8720          * <p>
8721          * If a View is marked as "ignored", it is not removed nor recycled.
8722          *
8723          * @param recycler Recycler to use to recycle children
8724          * @see #removeAndRecycleView(View, Recycler)
8725          * @see #removeAndRecycleViewAt(int, Recycler)
8726          * @see #ignoreView(View)
8727          */
removeAndRecycleAllViews(Recycler recycler)8728         public void removeAndRecycleAllViews(Recycler recycler) {
8729             for (int i = getChildCount() - 1; i >= 0; i--) {
8730                 final View view = getChildAt(i);
8731                 if (!getChildViewHolderInt(view).shouldIgnore()) {
8732                     removeAndRecycleViewAt(i, recycler);
8733                 }
8734             }
8735         }
8736 
8737         // called by accessibility delegate
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info)8738         void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
8739             onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
8740         }
8741 
8742         /**
8743          * Called by the AccessibilityDelegate when the information about the current layout should
8744          * be populated.
8745          * <p>
8746          * Default implementation adds a {@link
8747          * android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}.
8748          * <p>
8749          * You should override
8750          * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
8751          * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
8752          * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
8753          * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
8754          * more accurate accessibility information.
8755          *
8756          * @param recycler The Recycler that can be used to convert view positions into adapter
8757          *                 positions
8758          * @param state    The current state of RecyclerView
8759          * @param info     The info that should be filled by the LayoutManager
8760          * @see View#onInitializeAccessibilityNodeInfo(
8761          *android.view.accessibility.AccessibilityNodeInfo)
8762          * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
8763          * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
8764          * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
8765          * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
8766          */
onInitializeAccessibilityNodeInfo(Recycler recycler, State state, AccessibilityNodeInfoCompat info)8767         public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
8768                 AccessibilityNodeInfoCompat info) {
8769             if (ViewCompat.canScrollVertically(mRecyclerView, -1) ||
8770                     ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
8771                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
8772                 info.setScrollable(true);
8773             }
8774             if (ViewCompat.canScrollVertically(mRecyclerView, 1) ||
8775                     ViewCompat.canScrollHorizontally(mRecyclerView, 1)) {
8776                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
8777                 info.setScrollable(true);
8778             }
8779             final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo
8780                     = AccessibilityNodeInfoCompat.CollectionInfoCompat
8781                     .obtain(getRowCountForAccessibility(recycler, state),
8782                             getColumnCountForAccessibility(recycler, state),
8783                             isLayoutHierarchical(recycler, state),
8784                             getSelectionModeForAccessibility(recycler, state));
8785             info.setCollectionInfo(collectionInfo);
8786         }
8787 
8788         // called by accessibility delegate
onInitializeAccessibilityEvent(AccessibilityEvent event)8789         public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
8790             onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
8791         }
8792 
8793         /**
8794          * Called by the accessibility delegate to initialize an accessibility event.
8795          * <p>
8796          * Default implementation adds item count and scroll information to the event.
8797          *
8798          * @param recycler The Recycler that can be used to convert view positions into adapter
8799          *                 positions
8800          * @param state    The current state of RecyclerView
8801          * @param event    The event instance to initialize
8802          * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
8803          */
onInitializeAccessibilityEvent(Recycler recycler, State state, AccessibilityEvent event)8804         public void onInitializeAccessibilityEvent(Recycler recycler, State state,
8805                 AccessibilityEvent event) {
8806             final AccessibilityRecordCompat record = AccessibilityEventCompat
8807                     .asRecord(event);
8808             if (mRecyclerView == null || record == null) {
8809                 return;
8810             }
8811             record.setScrollable(ViewCompat.canScrollVertically(mRecyclerView, 1)
8812                     || ViewCompat.canScrollVertically(mRecyclerView, -1)
8813                     || ViewCompat.canScrollHorizontally(mRecyclerView, -1)
8814                     || ViewCompat.canScrollHorizontally(mRecyclerView, 1));
8815 
8816             if (mRecyclerView.mAdapter != null) {
8817                 record.setItemCount(mRecyclerView.mAdapter.getItemCount());
8818             }
8819         }
8820 
8821         // called by accessibility delegate
onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info)8822         void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) {
8823             final ViewHolder vh = getChildViewHolderInt(host);
8824             // avoid trying to create accessibility node info for removed children
8825             if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
8826                 onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
8827                         mRecyclerView.mState, host, info);
8828             }
8829         }
8830 
8831         /**
8832          * Called by the AccessibilityDelegate when the accessibility information for a specific
8833          * item should be populated.
8834          * <p>
8835          * Default implementation adds basic positioning information about the item.
8836          *
8837          * @param recycler The Recycler that can be used to convert view positions into adapter
8838          *                 positions
8839          * @param state    The current state of RecyclerView
8840          * @param host     The child for which accessibility node info should be populated
8841          * @param info     The info to fill out about the item
8842          * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
8843          * android.view.accessibility.AccessibilityNodeInfo)
8844          */
onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state, View host, AccessibilityNodeInfoCompat info)8845         public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
8846                 View host, AccessibilityNodeInfoCompat info) {
8847             int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
8848             int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
8849             final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo
8850                     = AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
8851                     columnIndexGuess, 1, false, false);
8852             info.setCollectionItemInfo(itemInfo);
8853         }
8854 
8855         /**
8856          * A LayoutManager can call this method to force RecyclerView to run simple animations in
8857          * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
8858          * change).
8859          * <p>
8860          * Note that, calling this method will not guarantee that RecyclerView will run animations
8861          * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
8862          * not run any animations but will still clear this flag after the layout is complete.
8863          *
8864          */
requestSimpleAnimationsInNextLayout()8865         public void requestSimpleAnimationsInNextLayout() {
8866             mRequestedSimpleAnimations = true;
8867         }
8868 
8869         /**
8870          * Returns the selection mode for accessibility. Should be
8871          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE},
8872          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or
8873          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}.
8874          * <p>
8875          * Default implementation returns
8876          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
8877          *
8878          * @param recycler The Recycler that can be used to convert view positions into adapter
8879          *                 positions
8880          * @param state    The current state of RecyclerView
8881          * @return Selection mode for accessibility. Default implementation returns
8882          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
8883          */
getSelectionModeForAccessibility(Recycler recycler, State state)8884         public int getSelectionModeForAccessibility(Recycler recycler, State state) {
8885             return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
8886         }
8887 
8888         /**
8889          * Returns the number of rows for accessibility.
8890          * <p>
8891          * Default implementation returns the number of items in the adapter if LayoutManager
8892          * supports vertical scrolling or 1 if LayoutManager does not support vertical
8893          * scrolling.
8894          *
8895          * @param recycler The Recycler that can be used to convert view positions into adapter
8896          *                 positions
8897          * @param state    The current state of RecyclerView
8898          * @return The number of rows in LayoutManager for accessibility.
8899          */
getRowCountForAccessibility(Recycler recycler, State state)8900         public int getRowCountForAccessibility(Recycler recycler, State state) {
8901             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
8902                 return 1;
8903             }
8904             return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
8905         }
8906 
8907         /**
8908          * Returns the number of columns for accessibility.
8909          * <p>
8910          * Default implementation returns the number of items in the adapter if LayoutManager
8911          * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
8912          * scrolling.
8913          *
8914          * @param recycler The Recycler that can be used to convert view positions into adapter
8915          *                 positions
8916          * @param state    The current state of RecyclerView
8917          * @return The number of rows in LayoutManager for accessibility.
8918          */
getColumnCountForAccessibility(Recycler recycler, State state)8919         public int getColumnCountForAccessibility(Recycler recycler, State state) {
8920             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
8921                 return 1;
8922             }
8923             return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
8924         }
8925 
8926         /**
8927          * Returns whether layout is hierarchical or not to be used for accessibility.
8928          * <p>
8929          * Default implementation returns false.
8930          *
8931          * @param recycler The Recycler that can be used to convert view positions into adapter
8932          *                 positions
8933          * @param state    The current state of RecyclerView
8934          * @return True if layout is hierarchical.
8935          */
isLayoutHierarchical(Recycler recycler, State state)8936         public boolean isLayoutHierarchical(Recycler recycler, State state) {
8937             return false;
8938         }
8939 
8940         // called by accessibility delegate
performAccessibilityAction(int action, Bundle args)8941         boolean performAccessibilityAction(int action, Bundle args) {
8942             return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
8943                     action, args);
8944         }
8945 
8946         /**
8947          * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
8948          *
8949          * @param recycler  The Recycler that can be used to convert view positions into adapter
8950          *                  positions
8951          * @param state     The current state of RecyclerView
8952          * @param action    The action to perform
8953          * @param args      Optional action arguments
8954          * @see View#performAccessibilityAction(int, android.os.Bundle)
8955          */
performAccessibilityAction(Recycler recycler, State state, int action, Bundle args)8956         public boolean performAccessibilityAction(Recycler recycler, State state, int action,
8957                 Bundle args) {
8958             if (mRecyclerView == null) {
8959                 return false;
8960             }
8961             int vScroll = 0, hScroll = 0;
8962             switch (action) {
8963                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
8964                     if (ViewCompat.canScrollVertically(mRecyclerView, -1)) {
8965                         vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
8966                     }
8967                     if (ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
8968                         hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
8969                     }
8970                     break;
8971                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
8972                     if (ViewCompat.canScrollVertically(mRecyclerView, 1)) {
8973                         vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
8974                     }
8975                     if (ViewCompat.canScrollHorizontally(mRecyclerView, 1)) {
8976                         hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
8977                     }
8978                     break;
8979             }
8980             if (vScroll == 0 && hScroll == 0) {
8981                 return false;
8982             }
8983             mRecyclerView.scrollBy(hScroll, vScroll);
8984             return true;
8985         }
8986 
8987         // called by accessibility delegate
performAccessibilityActionForItem(View view, int action, Bundle args)8988         boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
8989             return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
8990                     view, action, args);
8991         }
8992 
8993         /**
8994          * Called by AccessibilityDelegate when an accessibility action is requested on one of the
8995          * children of LayoutManager.
8996          * <p>
8997          * Default implementation does not do anything.
8998          *
8999          * @param recycler The Recycler that can be used to convert view positions into adapter
9000          *                 positions
9001          * @param state    The current state of RecyclerView
9002          * @param view     The child view on which the action is performed
9003          * @param action   The action to perform
9004          * @param args     Optional action arguments
9005          * @return true if action is handled
9006          * @see View#performAccessibilityAction(int, android.os.Bundle)
9007          */
performAccessibilityActionForItem(Recycler recycler, State state, View view, int action, Bundle args)9008         public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
9009                 int action, Bundle args) {
9010             return false;
9011         }
9012 
9013         /**
9014          * Parse the xml attributes to get the most common properties used by layout managers.
9015          *
9016          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
9017          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
9018          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
9019          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
9020          *
9021          * @return an object containing the properties as specified in the attrs.
9022          */
getProperties(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)9023         public static Properties getProperties(Context context, AttributeSet attrs,
9024                 int defStyleAttr, int defStyleRes) {
9025             Properties properties = new Properties();
9026             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
9027                     defStyleAttr, defStyleRes);
9028             properties.orientation = a.getInt(R.styleable.RecyclerView_android_orientation, VERTICAL);
9029             properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
9030             properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
9031             properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
9032             a.recycle();
9033             return properties;
9034         }
9035 
setExactMeasureSpecsFrom(RecyclerView recyclerView)9036         void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
9037             setMeasureSpecs(
9038                     MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
9039                     MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
9040             );
9041         }
9042 
9043         /**
9044          * Internal API to allow LayoutManagers to be measured twice.
9045          * <p>
9046          * This is not public because LayoutManagers should be able to handle their layouts in one
9047          * pass but it is very convenient to make existing LayoutManagers support wrapping content
9048          * when both orientations are undefined.
9049          * <p>
9050          * This API will be removed after default LayoutManagers properly implement wrap content in
9051          * non-scroll orientation.
9052          */
shouldMeasureTwice()9053         boolean shouldMeasureTwice() {
9054             return false;
9055         }
9056 
hasFlexibleChildInBothOrientations()9057         boolean hasFlexibleChildInBothOrientations() {
9058             final int childCount = getChildCount();
9059             for (int i = 0; i < childCount; i++) {
9060                 final View child = getChildAt(i);
9061                 final ViewGroup.LayoutParams lp = child.getLayoutParams();
9062                 if (lp.width < 0 && lp.height < 0) {
9063                     return true;
9064                 }
9065             }
9066             return false;
9067         }
9068 
9069         /**
9070          * Some general properties that a LayoutManager may want to use.
9071          */
9072         public static class Properties {
9073             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation */
9074             public int orientation;
9075             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount */
9076             public int spanCount;
9077             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout */
9078             public boolean reverseLayout;
9079             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd */
9080             public boolean stackFromEnd;
9081         }
9082     }
9083 
9084     /**
9085      * An ItemDecoration allows the application to add a special drawing and layout offset
9086      * to specific item views from the adapter's data set. This can be useful for drawing dividers
9087      * between items, highlights, visual grouping boundaries and more.
9088      *
9089      * <p>All ItemDecorations are drawn in the order they were added, before the item
9090      * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
9091      * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
9092      * RecyclerView.State)}.</p>
9093      */
9094     public static abstract class ItemDecoration {
9095         /**
9096          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
9097          * Any content drawn by this method will be drawn before the item views are drawn,
9098          * and will thus appear underneath the views.
9099          *
9100          * @param c Canvas to draw into
9101          * @param parent RecyclerView this ItemDecoration is drawing into
9102          * @param state The current state of RecyclerView
9103          */
onDraw(Canvas c, RecyclerView parent, State state)9104         public void onDraw(Canvas c, RecyclerView parent, State state) {
9105             onDraw(c, parent);
9106         }
9107 
9108         /**
9109          * @deprecated
9110          * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
9111          */
9112         @Deprecated
onDraw(Canvas c, RecyclerView parent)9113         public void onDraw(Canvas c, RecyclerView parent) {
9114         }
9115 
9116         /**
9117          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
9118          * Any content drawn by this method will be drawn after the item views are drawn
9119          * and will thus appear over the views.
9120          *
9121          * @param c Canvas to draw into
9122          * @param parent RecyclerView this ItemDecoration is drawing into
9123          * @param state The current state of RecyclerView.
9124          */
onDrawOver(Canvas c, RecyclerView parent, State state)9125         public void onDrawOver(Canvas c, RecyclerView parent, State state) {
9126             onDrawOver(c, parent);
9127         }
9128 
9129         /**
9130          * @deprecated
9131          * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
9132          */
9133         @Deprecated
onDrawOver(Canvas c, RecyclerView parent)9134         public void onDrawOver(Canvas c, RecyclerView parent) {
9135         }
9136 
9137 
9138         /**
9139          * @deprecated
9140          * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
9141          */
9142         @Deprecated
getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)9143         public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
9144             outRect.set(0, 0, 0, 0);
9145         }
9146 
9147         /**
9148          * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
9149          * the number of pixels that the item view should be inset by, similar to padding or margin.
9150          * The default implementation sets the bounds of outRect to 0 and returns.
9151          *
9152          * <p>
9153          * If this ItemDecoration does not affect the positioning of item views, it should set
9154          * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
9155          * before returning.
9156          *
9157          * <p>
9158          * If you need to access Adapter for additional data, you can call
9159          * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
9160          * View.
9161          *
9162          * @param outRect Rect to receive the output.
9163          * @param view    The child view to decorate
9164          * @param parent  RecyclerView this ItemDecoration is decorating
9165          * @param state   The current state of RecyclerView.
9166          */
getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)9167         public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
9168             getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
9169                     parent);
9170         }
9171     }
9172 
9173     /**
9174      * An OnItemTouchListener allows the application to intercept touch events in progress at the
9175      * view hierarchy level of the RecyclerView before those touch events are considered for
9176      * RecyclerView's own scrolling behavior.
9177      *
9178      * <p>This can be useful for applications that wish to implement various forms of gestural
9179      * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
9180      * a touch interaction already in progress even if the RecyclerView is already handling that
9181      * gesture stream itself for the purposes of scrolling.</p>
9182      *
9183      * @see SimpleOnItemTouchListener
9184      */
9185     public static interface OnItemTouchListener {
9186         /**
9187          * Silently observe and/or take over touch events sent to the RecyclerView
9188          * before they are handled by either the RecyclerView itself or its child views.
9189          *
9190          * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
9191          * in the order in which each listener was added, before any other touch processing
9192          * by the RecyclerView itself or child views occurs.</p>
9193          *
9194          * @param e MotionEvent describing the touch event. All coordinates are in
9195          *          the RecyclerView's coordinate system.
9196          * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
9197          *         to continue with the current behavior and continue observing future events in
9198          *         the gesture.
9199          */
onInterceptTouchEvent(RecyclerView rv, MotionEvent e)9200         public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
9201 
9202         /**
9203          * Process a touch event as part of a gesture that was claimed by returning true from
9204          * a previous call to {@link #onInterceptTouchEvent}.
9205          *
9206          * @param e MotionEvent describing the touch event. All coordinates are in
9207          *          the RecyclerView's coordinate system.
9208          */
onTouchEvent(RecyclerView rv, MotionEvent e)9209         public void onTouchEvent(RecyclerView rv, MotionEvent e);
9210 
9211         /**
9212          * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
9213          * intercept touch events with
9214          * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
9215          *
9216          * @param disallowIntercept True if the child does not want the parent to
9217          *            intercept touch events.
9218          * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
9219          */
onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)9220         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
9221     }
9222 
9223     /**
9224      * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies and
9225      * default return values.
9226      * <p>
9227      * You may prefer to extend this class if you don't need to override all methods. Another
9228      * benefit of using this class is future compatibility. As the interface may change, we'll
9229      * always provide a default implementation on this class so that your code won't break when
9230      * you update to a new version of the support library.
9231      */
9232     public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
9233         @Override
onInterceptTouchEvent(RecyclerView rv, MotionEvent e)9234         public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
9235             return false;
9236         }
9237 
9238         @Override
onTouchEvent(RecyclerView rv, MotionEvent e)9239         public void onTouchEvent(RecyclerView rv, MotionEvent e) {
9240         }
9241 
9242         @Override
onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)9243         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
9244         }
9245     }
9246 
9247 
9248     /**
9249      * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
9250      * has occurred on that RecyclerView.
9251      * <p>
9252      * @see RecyclerView#addOnScrollListener(OnScrollListener)
9253      * @see RecyclerView#clearOnChildAttachStateChangeListeners()
9254      *
9255      */
9256     public abstract static class OnScrollListener {
9257         /**
9258          * Callback method to be invoked when RecyclerView's scroll state changes.
9259          *
9260          * @param recyclerView The RecyclerView whose scroll state has changed.
9261          * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
9262          *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
9263          */
onScrollStateChanged(RecyclerView recyclerView, int newState)9264         public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
9265 
9266         /**
9267          * Callback method to be invoked when the RecyclerView has been scrolled. This will be
9268          * called after the scroll has completed.
9269          * <p>
9270          * This callback will also be called if visible item range changes after a layout
9271          * calculation. In that case, dx and dy will be 0.
9272          *
9273          * @param recyclerView The RecyclerView which scrolled.
9274          * @param dx The amount of horizontal scroll.
9275          * @param dy The amount of vertical scroll.
9276          */
onScrolled(RecyclerView recyclerView, int dx, int dy)9277         public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
9278     }
9279 
9280     /**
9281      * A RecyclerListener can be set on a RecyclerView to receive messages whenever
9282      * a view is recycled.
9283      *
9284      * @see RecyclerView#setRecyclerListener(RecyclerListener)
9285      */
9286     public interface RecyclerListener {
9287 
9288         /**
9289          * This method is called whenever the view in the ViewHolder is recycled.
9290          *
9291          * RecyclerView calls this method right before clearing ViewHolder's internal data and
9292          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
9293          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
9294          * its adapter position.
9295          *
9296          * @param holder The ViewHolder containing the view that was recycled
9297          */
onViewRecycled(ViewHolder holder)9298         public void onViewRecycled(ViewHolder holder);
9299     }
9300 
9301     /**
9302      * A Listener interface that can be attached to a RecylcerView to get notified
9303      * whenever a ViewHolder is attached to or detached from RecyclerView.
9304      */
9305     public interface OnChildAttachStateChangeListener {
9306 
9307         /**
9308          * Called when a view is attached to the RecyclerView.
9309          *
9310          * @param view The View which is attached to the RecyclerView
9311          */
onChildViewAttachedToWindow(View view)9312         public void onChildViewAttachedToWindow(View view);
9313 
9314         /**
9315          * Called when a view is detached from RecyclerView.
9316          *
9317          * @param view The View which is being detached from the RecyclerView
9318          */
onChildViewDetachedFromWindow(View view)9319         public void onChildViewDetachedFromWindow(View view);
9320     }
9321 
9322     /**
9323      * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
9324      *
9325      * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
9326      * potentially expensive {@link View#findViewById(int)} results.</p>
9327      *
9328      * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
9329      * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
9330      * their own custom ViewHolder implementations to store data that makes binding view contents
9331      * easier. Implementations should assume that individual item views will hold strong references
9332      * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
9333      * strong references to extra off-screen item views for caching purposes</p>
9334      */
9335     public static abstract class ViewHolder {
9336         public final View itemView;
9337         int mPosition = NO_POSITION;
9338         int mOldPosition = NO_POSITION;
9339         long mItemId = NO_ID;
9340         int mItemViewType = INVALID_TYPE;
9341         int mPreLayoutPosition = NO_POSITION;
9342 
9343         // The item that this holder is shadowing during an item change event/animation
9344         ViewHolder mShadowedHolder = null;
9345         // The item that is shadowing this holder during an item change event/animation
9346         ViewHolder mShadowingHolder = null;
9347 
9348         /**
9349          * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
9350          * are all valid.
9351          */
9352         static final int FLAG_BOUND = 1 << 0;
9353 
9354         /**
9355          * The data this ViewHolder's view reflects is stale and needs to be rebound
9356          * by the adapter. mPosition and mItemId are consistent.
9357          */
9358         static final int FLAG_UPDATE = 1 << 1;
9359 
9360         /**
9361          * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
9362          * are not to be trusted and may no longer match the item view type.
9363          * This ViewHolder must be fully rebound to different data.
9364          */
9365         static final int FLAG_INVALID = 1 << 2;
9366 
9367         /**
9368          * This ViewHolder points at data that represents an item previously removed from the
9369          * data set. Its view may still be used for things like outgoing animations.
9370          */
9371         static final int FLAG_REMOVED = 1 << 3;
9372 
9373         /**
9374          * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
9375          * and is intended to keep views around during animations.
9376          */
9377         static final int FLAG_NOT_RECYCLABLE = 1 << 4;
9378 
9379         /**
9380          * This ViewHolder is returned from scrap which means we are expecting an addView call
9381          * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
9382          * the end of the layout pass and then recycled by RecyclerView if it is not added back to
9383          * the RecyclerView.
9384          */
9385         static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
9386 
9387         /**
9388          * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
9389          * it unless LayoutManager is replaced.
9390          * It is still fully visible to the LayoutManager.
9391          */
9392         static final int FLAG_IGNORE = 1 << 7;
9393 
9394         /**
9395          * When the View is detached form the parent, we set this flag so that we can take correct
9396          * action when we need to remove it or add it back.
9397          */
9398         static final int FLAG_TMP_DETACHED = 1 << 8;
9399 
9400         /**
9401          * Set when we can no longer determine the adapter position of this ViewHolder until it is
9402          * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
9403          * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
9404          * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
9405          * re-calculated.
9406          */
9407         static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
9408 
9409         /**
9410          * Set when a addChangePayload(null) is called
9411          */
9412         static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
9413 
9414         /**
9415          * Used by ItemAnimator when a ViewHolder's position changes
9416          */
9417         static final int FLAG_MOVED = 1 << 11;
9418 
9419         /**
9420          * Used by ItemAnimator when a ViewHolder appears in pre-layout
9421          */
9422         static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
9423 
9424         /**
9425          * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
9426          * hidden list (as if it was scrap) without being recycled in between.
9427          *
9428          * When a ViewHolder is hidden, there are 2 paths it can be re-used:
9429          *   a) Animation ends, view is recycled and used from the recycle pool.
9430          *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
9431          *
9432          * This flag is used to represent "case b" where the ViewHolder is reused without being
9433          * recycled (thus "bounced" from the hidden list). This state requires special handling
9434          * because the ViewHolder must be added to pre layout maps for animations as if it was
9435          * already there.
9436          */
9437         static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
9438 
9439         private int mFlags;
9440 
9441         private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
9442 
9443         List<Object> mPayloads = null;
9444         List<Object> mUnmodifiedPayloads = null;
9445 
9446         private int mIsRecyclableCount = 0;
9447 
9448         // If non-null, view is currently considered scrap and may be reused for other data by the
9449         // scrap container.
9450         private Recycler mScrapContainer = null;
9451         // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
9452         private boolean mInChangeScrap = false;
9453 
9454         // Saves isImportantForAccessibility value for the view item while it's in hidden state and
9455         // marked as unimportant for accessibility.
9456         private int mWasImportantForAccessibilityBeforeHidden =
9457                 ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
9458 
9459         /**
9460          * Is set when VH is bound from the adapter and cleaned right before it is sent to
9461          * {@link RecycledViewPool}.
9462          */
9463         RecyclerView mOwnerRecyclerView;
9464 
ViewHolder(View itemView)9465         public ViewHolder(View itemView) {
9466             if (itemView == null) {
9467                 throw new IllegalArgumentException("itemView may not be null");
9468             }
9469             this.itemView = itemView;
9470         }
9471 
flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout)9472         void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
9473             addFlags(ViewHolder.FLAG_REMOVED);
9474             offsetPosition(offset, applyToPreLayout);
9475             mPosition = mNewPosition;
9476         }
9477 
offsetPosition(int offset, boolean applyToPreLayout)9478         void offsetPosition(int offset, boolean applyToPreLayout) {
9479             if (mOldPosition == NO_POSITION) {
9480                 mOldPosition = mPosition;
9481             }
9482             if (mPreLayoutPosition == NO_POSITION) {
9483                 mPreLayoutPosition = mPosition;
9484             }
9485             if (applyToPreLayout) {
9486                 mPreLayoutPosition += offset;
9487             }
9488             mPosition += offset;
9489             if (itemView.getLayoutParams() != null) {
9490                 ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
9491             }
9492         }
9493 
clearOldPosition()9494         void clearOldPosition() {
9495             mOldPosition = NO_POSITION;
9496             mPreLayoutPosition = NO_POSITION;
9497         }
9498 
saveOldPosition()9499         void saveOldPosition() {
9500             if (mOldPosition == NO_POSITION) {
9501                 mOldPosition = mPosition;
9502             }
9503         }
9504 
shouldIgnore()9505         boolean shouldIgnore() {
9506             return (mFlags & FLAG_IGNORE) != 0;
9507         }
9508 
9509         /**
9510          * @deprecated This method is deprecated because its meaning is ambiguous due to the async
9511          * handling of adapter updates. Please use {@link #getLayoutPosition()} or
9512          * {@link #getAdapterPosition()} depending on your use case.
9513          *
9514          * @see #getLayoutPosition()
9515          * @see #getAdapterPosition()
9516          */
9517         @Deprecated
getPosition()9518         public final int getPosition() {
9519             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
9520         }
9521 
9522         /**
9523          * Returns the position of the ViewHolder in terms of the latest layout pass.
9524          * <p>
9525          * This position is mostly used by RecyclerView components to be consistent while
9526          * RecyclerView lazily processes adapter updates.
9527          * <p>
9528          * For performance and animation reasons, RecyclerView batches all adapter updates until the
9529          * next layout pass. This may cause mismatches between the Adapter position of the item and
9530          * the position it had in the latest layout calculations.
9531          * <p>
9532          * LayoutManagers should always call this method while doing calculations based on item
9533          * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
9534          * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
9535          * of the item.
9536          * <p>
9537          * If LayoutManager needs to call an external method that requires the adapter position of
9538          * the item, it can use {@link #getAdapterPosition()} or
9539          * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
9540          *
9541          * @return Returns the adapter position of the ViewHolder in the latest layout pass.
9542          * @see #getAdapterPosition()
9543          */
getLayoutPosition()9544         public final int getLayoutPosition() {
9545             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
9546         }
9547 
9548         /**
9549          * Returns the Adapter position of the item represented by this ViewHolder.
9550          * <p>
9551          * Note that this might be different than the {@link #getLayoutPosition()} if there are
9552          * pending adapter updates but a new layout pass has not happened yet.
9553          * <p>
9554          * RecyclerView does not handle any adapter updates until the next layout traversal. This
9555          * may create temporary inconsistencies between what user sees on the screen and what
9556          * adapter contents have. This inconsistency is not important since it will be less than
9557          * 16ms but it might be a problem if you want to use ViewHolder position to access the
9558          * adapter. Sometimes, you may need to get the exact adapter position to do
9559          * some actions in response to user events. In that case, you should use this method which
9560          * will calculate the Adapter position of the ViewHolder.
9561          * <p>
9562          * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
9563          * next layout pass, the return value of this method will be {@link #NO_POSITION}.
9564          *
9565          * @return The adapter position of the item if it still exists in the adapter.
9566          * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
9567          * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
9568          * layout pass or the ViewHolder has already been recycled.
9569          */
getAdapterPosition()9570         public final int getAdapterPosition() {
9571             if (mOwnerRecyclerView == null) {
9572                 return NO_POSITION;
9573             }
9574             return mOwnerRecyclerView.getAdapterPositionFor(this);
9575         }
9576 
9577         /**
9578          * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
9579          * to perform animations.
9580          * <p>
9581          * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
9582          * adapter index in the previous layout.
9583          *
9584          * @return The previous adapter index of the Item represented by this ViewHolder or
9585          * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
9586          * complete).
9587          */
getOldPosition()9588         public final int getOldPosition() {
9589             return mOldPosition;
9590         }
9591 
9592         /**
9593          * Returns The itemId represented by this ViewHolder.
9594          *
9595          * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
9596          * otherwise
9597          */
getItemId()9598         public final long getItemId() {
9599             return mItemId;
9600         }
9601 
9602         /**
9603          * @return The view type of this ViewHolder.
9604          */
getItemViewType()9605         public final int getItemViewType() {
9606             return mItemViewType;
9607         }
9608 
isScrap()9609         boolean isScrap() {
9610             return mScrapContainer != null;
9611         }
9612 
unScrap()9613         void unScrap() {
9614             mScrapContainer.unscrapView(this);
9615         }
9616 
wasReturnedFromScrap()9617         boolean wasReturnedFromScrap() {
9618             return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
9619         }
9620 
clearReturnedFromScrapFlag()9621         void clearReturnedFromScrapFlag() {
9622             mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
9623         }
9624 
clearTmpDetachFlag()9625         void clearTmpDetachFlag() {
9626             mFlags = mFlags & ~FLAG_TMP_DETACHED;
9627         }
9628 
stopIgnoring()9629         void stopIgnoring() {
9630             mFlags = mFlags & ~FLAG_IGNORE;
9631         }
9632 
setScrapContainer(Recycler recycler, boolean isChangeScrap)9633         void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
9634             mScrapContainer = recycler;
9635             mInChangeScrap = isChangeScrap;
9636         }
9637 
isInvalid()9638         boolean isInvalid() {
9639             return (mFlags & FLAG_INVALID) != 0;
9640         }
9641 
needsUpdate()9642         boolean needsUpdate() {
9643             return (mFlags & FLAG_UPDATE) != 0;
9644         }
9645 
isBound()9646         boolean isBound() {
9647             return (mFlags & FLAG_BOUND) != 0;
9648         }
9649 
isRemoved()9650         boolean isRemoved() {
9651             return (mFlags & FLAG_REMOVED) != 0;
9652         }
9653 
hasAnyOfTheFlags(int flags)9654         boolean hasAnyOfTheFlags(int flags) {
9655             return (mFlags & flags) != 0;
9656         }
9657 
isTmpDetached()9658         boolean isTmpDetached() {
9659             return (mFlags & FLAG_TMP_DETACHED) != 0;
9660         }
9661 
isAdapterPositionUnknown()9662         boolean isAdapterPositionUnknown() {
9663             return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
9664         }
9665 
setFlags(int flags, int mask)9666         void setFlags(int flags, int mask) {
9667             mFlags = (mFlags & ~mask) | (flags & mask);
9668         }
9669 
addFlags(int flags)9670         void addFlags(int flags) {
9671             mFlags |= flags;
9672         }
9673 
addChangePayload(Object payload)9674         void addChangePayload(Object payload) {
9675             if (payload == null) {
9676                 addFlags(FLAG_ADAPTER_FULLUPDATE);
9677             } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
9678                 createPayloadsIfNeeded();
9679                 mPayloads.add(payload);
9680             }
9681         }
9682 
createPayloadsIfNeeded()9683         private void createPayloadsIfNeeded() {
9684             if (mPayloads == null) {
9685                 mPayloads = new ArrayList<Object>();
9686                 mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
9687             }
9688         }
9689 
clearPayload()9690         void clearPayload() {
9691             if (mPayloads != null) {
9692                 mPayloads.clear();
9693             }
9694             mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
9695         }
9696 
getUnmodifiedPayloads()9697         List<Object> getUnmodifiedPayloads() {
9698             if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
9699                 if (mPayloads == null || mPayloads.size() == 0) {
9700                     // Initial state,  no update being called.
9701                     return FULLUPDATE_PAYLOADS;
9702                 }
9703                 // there are none-null payloads
9704                 return mUnmodifiedPayloads;
9705             } else {
9706                 // a full update has been called.
9707                 return FULLUPDATE_PAYLOADS;
9708             }
9709         }
9710 
resetInternal()9711         void resetInternal() {
9712             mFlags = 0;
9713             mPosition = NO_POSITION;
9714             mOldPosition = NO_POSITION;
9715             mItemId = NO_ID;
9716             mPreLayoutPosition = NO_POSITION;
9717             mIsRecyclableCount = 0;
9718             mShadowedHolder = null;
9719             mShadowingHolder = null;
9720             clearPayload();
9721             mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
9722         }
9723 
9724         /**
9725          * Called when the child view enters the hidden state
9726          */
onEnteredHiddenState()9727         private void onEnteredHiddenState() {
9728             // While the view item is in hidden state, make it invisible for the accessibility.
9729             mWasImportantForAccessibilityBeforeHidden =
9730                     ViewCompat.getImportantForAccessibility(itemView);
9731             ViewCompat.setImportantForAccessibility(itemView,
9732                     ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
9733         }
9734 
9735         /**
9736          * Called when the child view leaves the hidden state
9737          */
onLeftHiddenState()9738         private void onLeftHiddenState() {
9739             ViewCompat.setImportantForAccessibility(
9740                     itemView, mWasImportantForAccessibilityBeforeHidden);
9741             mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
9742         }
9743 
9744         @Override
toString()9745         public String toString() {
9746             final StringBuilder sb = new StringBuilder("ViewHolder{" +
9747                     Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId +
9748                     ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
9749             if (isScrap()) {
9750                 sb.append(" scrap ")
9751                         .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
9752             }
9753             if (isInvalid()) sb.append(" invalid");
9754             if (!isBound()) sb.append(" unbound");
9755             if (needsUpdate()) sb.append(" update");
9756             if (isRemoved()) sb.append(" removed");
9757             if (shouldIgnore()) sb.append(" ignored");
9758             if (isTmpDetached()) sb.append(" tmpDetached");
9759             if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
9760             if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
9761 
9762             if (itemView.getParent() == null) sb.append(" no parent");
9763             sb.append("}");
9764             return sb.toString();
9765         }
9766 
9767         /**
9768          * Informs the recycler whether this item can be recycled. Views which are not
9769          * recyclable will not be reused for other items until setIsRecyclable() is
9770          * later set to true. Calls to setIsRecyclable() should always be paired (one
9771          * call to setIsRecyclabe(false) should always be matched with a later call to
9772          * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
9773          * reference-counted.
9774          *
9775          * @param recyclable Whether this item is available to be recycled. Default value
9776          * is true.
9777          */
setIsRecyclable(boolean recyclable)9778         public final void setIsRecyclable(boolean recyclable) {
9779             mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
9780             if (mIsRecyclableCount < 0) {
9781                 mIsRecyclableCount = 0;
9782                 if (DEBUG) {
9783                     throw new RuntimeException("isRecyclable decremented below 0: " +
9784                             "unmatched pair of setIsRecyable() calls for " + this);
9785                 }
9786                 Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: " +
9787                         "unmatched pair of setIsRecyable() calls for " + this);
9788             } else if (!recyclable && mIsRecyclableCount == 1) {
9789                 mFlags |= FLAG_NOT_RECYCLABLE;
9790             } else if (recyclable && mIsRecyclableCount == 0) {
9791                 mFlags &= ~FLAG_NOT_RECYCLABLE;
9792             }
9793             if (DEBUG) {
9794                 Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
9795             }
9796         }
9797 
9798         /**
9799          * @see {@link #setIsRecyclable(boolean)}
9800          *
9801          * @return true if this item is available to be recycled, false otherwise.
9802          */
isRecyclable()9803         public final boolean isRecyclable() {
9804             return (mFlags & FLAG_NOT_RECYCLABLE) == 0 &&
9805                     !ViewCompat.hasTransientState(itemView);
9806         }
9807 
9808         /**
9809          * Returns whether we have animations referring to this view holder or not.
9810          * This is similar to isRecyclable flag but does not check transient state.
9811          */
shouldBeKeptAsChild()9812         private boolean shouldBeKeptAsChild() {
9813             return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
9814         }
9815 
9816         /**
9817          * @return True if ViewHolder is not referenced by RecyclerView animations but has
9818          * transient state which will prevent it from being recycled.
9819          */
doesTransientStatePreventRecycling()9820         private boolean doesTransientStatePreventRecycling() {
9821             return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView);
9822         }
9823 
isUpdated()9824         boolean isUpdated() {
9825             return (mFlags & FLAG_UPDATE) != 0;
9826         }
9827     }
9828 
getAdapterPositionFor(ViewHolder viewHolder)9829     private int getAdapterPositionFor(ViewHolder viewHolder) {
9830         if (viewHolder.hasAnyOfTheFlags( ViewHolder.FLAG_INVALID |
9831                 ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
9832                 || !viewHolder.isBound()) {
9833             return RecyclerView.NO_POSITION;
9834         }
9835         return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
9836     }
9837 
9838     // NestedScrollingChild
9839 
9840     @Override
setNestedScrollingEnabled(boolean enabled)9841     public void setNestedScrollingEnabled(boolean enabled) {
9842         getScrollingChildHelper().setNestedScrollingEnabled(enabled);
9843     }
9844 
9845     @Override
isNestedScrollingEnabled()9846     public boolean isNestedScrollingEnabled() {
9847         return getScrollingChildHelper().isNestedScrollingEnabled();
9848     }
9849 
9850     @Override
startNestedScroll(int axes)9851     public boolean startNestedScroll(int axes) {
9852         return getScrollingChildHelper().startNestedScroll(axes);
9853     }
9854 
9855     @Override
stopNestedScroll()9856     public void stopNestedScroll() {
9857         getScrollingChildHelper().stopNestedScroll();
9858     }
9859 
9860     @Override
hasNestedScrollingParent()9861     public boolean hasNestedScrollingParent() {
9862         return getScrollingChildHelper().hasNestedScrollingParent();
9863     }
9864 
9865     @Override
dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow)9866     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
9867             int dyUnconsumed, int[] offsetInWindow) {
9868         return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
9869                 dxUnconsumed, dyUnconsumed, offsetInWindow);
9870     }
9871 
9872     @Override
dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow)9873     public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
9874         return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
9875     }
9876 
9877     @Override
dispatchNestedFling(float velocityX, float velocityY, boolean consumed)9878     public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
9879         return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
9880     }
9881 
9882     @Override
dispatchNestedPreFling(float velocityX, float velocityY)9883     public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
9884         return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
9885     }
9886 
9887     /**
9888      * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
9889      * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
9890      * to create their own subclass of this <code>LayoutParams</code> class
9891      * to store any additional required per-child view metadata about the layout.
9892      */
9893     public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
9894         ViewHolder mViewHolder;
9895         final Rect mDecorInsets = new Rect();
9896         boolean mInsetsDirty = true;
9897         // Flag is set to true if the view is bound while it is detached from RV.
9898         // In this case, we need to manually call invalidate after view is added to guarantee that
9899         // invalidation is populated through the View hierarchy
9900         boolean mPendingInvalidate = false;
9901 
LayoutParams(Context c, AttributeSet attrs)9902         public LayoutParams(Context c, AttributeSet attrs) {
9903             super(c, attrs);
9904         }
9905 
LayoutParams(int width, int height)9906         public LayoutParams(int width, int height) {
9907             super(width, height);
9908         }
9909 
LayoutParams(MarginLayoutParams source)9910         public LayoutParams(MarginLayoutParams source) {
9911             super(source);
9912         }
9913 
LayoutParams(ViewGroup.LayoutParams source)9914         public LayoutParams(ViewGroup.LayoutParams source) {
9915             super(source);
9916         }
9917 
LayoutParams(LayoutParams source)9918         public LayoutParams(LayoutParams source) {
9919             super((ViewGroup.LayoutParams) source);
9920         }
9921 
9922         /**
9923          * Returns true if the view this LayoutParams is attached to needs to have its content
9924          * updated from the corresponding adapter.
9925          *
9926          * @return true if the view should have its content updated
9927          */
viewNeedsUpdate()9928         public boolean viewNeedsUpdate() {
9929             return mViewHolder.needsUpdate();
9930         }
9931 
9932         /**
9933          * Returns true if the view this LayoutParams is attached to is now representing
9934          * potentially invalid data. A LayoutManager should scrap/recycle it.
9935          *
9936          * @return true if the view is invalid
9937          */
isViewInvalid()9938         public boolean isViewInvalid() {
9939             return mViewHolder.isInvalid();
9940         }
9941 
9942         /**
9943          * Returns true if the adapter data item corresponding to the view this LayoutParams
9944          * is attached to has been removed from the data set. A LayoutManager may choose to
9945          * treat it differently in order to animate its outgoing or disappearing state.
9946          *
9947          * @return true if the item the view corresponds to was removed from the data set
9948          */
isItemRemoved()9949         public boolean isItemRemoved() {
9950             return mViewHolder.isRemoved();
9951         }
9952 
9953         /**
9954          * Returns true if the adapter data item corresponding to the view this LayoutParams
9955          * is attached to has been changed in the data set. A LayoutManager may choose to
9956          * treat it differently in order to animate its changing state.
9957          *
9958          * @return true if the item the view corresponds to was changed in the data set
9959          */
isItemChanged()9960         public boolean isItemChanged() {
9961             return mViewHolder.isUpdated();
9962         }
9963 
9964         /**
9965          * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
9966          */
9967         @Deprecated
getViewPosition()9968         public int getViewPosition() {
9969             return mViewHolder.getPosition();
9970         }
9971 
9972         /**
9973          * Returns the adapter position that the view this LayoutParams is attached to corresponds
9974          * to as of latest layout calculation.
9975          *
9976          * @return the adapter position this view as of latest layout pass
9977          */
getViewLayoutPosition()9978         public int getViewLayoutPosition() {
9979             return mViewHolder.getLayoutPosition();
9980         }
9981 
9982         /**
9983          * Returns the up-to-date adapter position that the view this LayoutParams is attached to
9984          * corresponds to.
9985          *
9986          * @return the up-to-date adapter position this view. It may return
9987          * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
9988          * its up-to-date position cannot be calculated.
9989          */
getViewAdapterPosition()9990         public int getViewAdapterPosition() {
9991             return mViewHolder.getAdapterPosition();
9992         }
9993     }
9994 
9995     /**
9996      * Observer base class for watching changes to an {@link Adapter}.
9997      * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
9998      */
9999     public static abstract class AdapterDataObserver {
onChanged()10000         public void onChanged() {
10001             // Do nothing
10002         }
10003 
onItemRangeChanged(int positionStart, int itemCount)10004         public void onItemRangeChanged(int positionStart, int itemCount) {
10005             // do nothing
10006         }
10007 
onItemRangeChanged(int positionStart, int itemCount, Object payload)10008         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
10009             // fallback to onItemRangeChanged(positionStart, itemCount) if app
10010             // does not override this method.
10011             onItemRangeChanged(positionStart, itemCount);
10012         }
10013 
onItemRangeInserted(int positionStart, int itemCount)10014         public void onItemRangeInserted(int positionStart, int itemCount) {
10015             // do nothing
10016         }
10017 
onItemRangeRemoved(int positionStart, int itemCount)10018         public void onItemRangeRemoved(int positionStart, int itemCount) {
10019             // do nothing
10020         }
10021 
onItemRangeMoved(int fromPosition, int toPosition, int itemCount)10022         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
10023             // do nothing
10024         }
10025     }
10026 
10027     /**
10028      * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
10029      * provides methods to trigger a programmatic scroll.</p>
10030      *
10031      * @see LinearSmoothScroller
10032      */
10033     public static abstract class SmoothScroller {
10034 
10035         private int mTargetPosition = RecyclerView.NO_POSITION;
10036 
10037         private RecyclerView mRecyclerView;
10038 
10039         private LayoutManager mLayoutManager;
10040 
10041         private boolean mPendingInitialRun;
10042 
10043         private boolean mRunning;
10044 
10045         private View mTargetView;
10046 
10047         private final Action mRecyclingAction;
10048 
SmoothScroller()10049         public SmoothScroller() {
10050             mRecyclingAction = new Action(0, 0);
10051         }
10052 
10053         /**
10054          * Starts a smooth scroll for the given target position.
10055          * <p>In each animation step, {@link RecyclerView} will check
10056          * for the target view and call either
10057          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
10058          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
10059          * SmoothScroller is stopped.</p>
10060          *
10061          * <p>Note that if RecyclerView finds the target view, it will automatically stop the
10062          * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
10063          * stop calling SmoothScroller in each animation step.</p>
10064          */
start(RecyclerView recyclerView, LayoutManager layoutManager)10065         void start(RecyclerView recyclerView, LayoutManager layoutManager) {
10066             mRecyclerView = recyclerView;
10067             mLayoutManager = layoutManager;
10068             if (mTargetPosition == RecyclerView.NO_POSITION) {
10069                 throw new IllegalArgumentException("Invalid target position");
10070             }
10071             mRecyclerView.mState.mTargetPosition = mTargetPosition;
10072             mRunning = true;
10073             mPendingInitialRun = true;
10074             mTargetView = findViewByPosition(getTargetPosition());
10075             onStart();
10076             mRecyclerView.mViewFlinger.postOnAnimation();
10077         }
10078 
setTargetPosition(int targetPosition)10079         public void setTargetPosition(int targetPosition) {
10080             mTargetPosition = targetPosition;
10081         }
10082 
10083         /**
10084          * @return The LayoutManager to which this SmoothScroller is attached. Will return
10085          * <code>null</code> after the SmoothScroller is stopped.
10086          */
10087         @Nullable
getLayoutManager()10088         public LayoutManager getLayoutManager() {
10089             return mLayoutManager;
10090         }
10091 
10092         /**
10093          * Stops running the SmoothScroller in each animation callback. Note that this does not
10094          * cancel any existing {@link Action} updated by
10095          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
10096          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
10097          */
stop()10098         final protected void stop() {
10099             if (!mRunning) {
10100                 return;
10101             }
10102             onStop();
10103             mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
10104             mTargetView = null;
10105             mTargetPosition = RecyclerView.NO_POSITION;
10106             mPendingInitialRun = false;
10107             mRunning = false;
10108             // trigger a cleanup
10109             mLayoutManager.onSmoothScrollerStopped(this);
10110             // clear references to avoid any potential leak by a custom smooth scroller
10111             mLayoutManager = null;
10112             mRecyclerView = null;
10113         }
10114 
10115         /**
10116          * Returns true if SmoothScroller has been started but has not received the first
10117          * animation
10118          * callback yet.
10119          *
10120          * @return True if this SmoothScroller is waiting to start
10121          */
isPendingInitialRun()10122         public boolean isPendingInitialRun() {
10123             return mPendingInitialRun;
10124         }
10125 
10126 
10127         /**
10128          * @return True if SmoothScroller is currently active
10129          */
isRunning()10130         public boolean isRunning() {
10131             return mRunning;
10132         }
10133 
10134         /**
10135          * Returns the adapter position of the target item
10136          *
10137          * @return Adapter position of the target item or
10138          * {@link RecyclerView#NO_POSITION} if no target view is set.
10139          */
getTargetPosition()10140         public int getTargetPosition() {
10141             return mTargetPosition;
10142         }
10143 
onAnimation(int dx, int dy)10144         private void onAnimation(int dx, int dy) {
10145             final RecyclerView recyclerView = mRecyclerView;
10146             if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
10147                 stop();
10148             }
10149             mPendingInitialRun = false;
10150             if (mTargetView != null) {
10151                 // verify target position
10152                 if (getChildPosition(mTargetView) == mTargetPosition) {
10153                     onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
10154                     mRecyclingAction.runIfNecessary(recyclerView);
10155                     stop();
10156                 } else {
10157                     Log.e(TAG, "Passed over target position while smooth scrolling.");
10158                     mTargetView = null;
10159                 }
10160             }
10161             if (mRunning) {
10162                 onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
10163                 boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
10164                 mRecyclingAction.runIfNecessary(recyclerView);
10165                 if (hadJumpTarget) {
10166                     // It is not stopped so needs to be restarted
10167                     if (mRunning) {
10168                         mPendingInitialRun = true;
10169                         recyclerView.mViewFlinger.postOnAnimation();
10170                     } else {
10171                         stop(); // done
10172                     }
10173                 }
10174             }
10175         }
10176 
10177         /**
10178          * @see RecyclerView#getChildLayoutPosition(android.view.View)
10179          */
getChildPosition(View view)10180         public int getChildPosition(View view) {
10181             return mRecyclerView.getChildLayoutPosition(view);
10182         }
10183 
10184         /**
10185          * @see RecyclerView.LayoutManager#getChildCount()
10186          */
getChildCount()10187         public int getChildCount() {
10188             return mRecyclerView.mLayout.getChildCount();
10189         }
10190 
10191         /**
10192          * @see RecyclerView.LayoutManager#findViewByPosition(int)
10193          */
findViewByPosition(int position)10194         public View findViewByPosition(int position) {
10195             return mRecyclerView.mLayout.findViewByPosition(position);
10196         }
10197 
10198         /**
10199          * @see RecyclerView#scrollToPosition(int)
10200          * @deprecated Use {@link Action#jumpTo(int)}.
10201          */
10202         @Deprecated
instantScrollToPosition(int position)10203         public void instantScrollToPosition(int position) {
10204             mRecyclerView.scrollToPosition(position);
10205         }
10206 
onChildAttachedToWindow(View child)10207         protected void onChildAttachedToWindow(View child) {
10208             if (getChildPosition(child) == getTargetPosition()) {
10209                 mTargetView = child;
10210                 if (DEBUG) {
10211                     Log.d(TAG, "smooth scroll target view has been attached");
10212                 }
10213             }
10214         }
10215 
10216         /**
10217          * Normalizes the vector.
10218          * @param scrollVector The vector that points to the target scroll position
10219          */
normalize(PointF scrollVector)10220         protected void normalize(PointF scrollVector) {
10221             final double magnitude = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y
10222                     * scrollVector.y);
10223             scrollVector.x /= magnitude;
10224             scrollVector.y /= magnitude;
10225         }
10226 
10227         /**
10228          * Called when smooth scroll is started. This might be a good time to do setup.
10229          */
onStart()10230         abstract protected void onStart();
10231 
10232         /**
10233          * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
10234          * @see #stop()
10235          */
onStop()10236         abstract protected void onStop();
10237 
10238         /**
10239          * <p>RecyclerView will call this method each time it scrolls until it can find the target
10240          * position in the layout.</p>
10241          * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
10242          * provided {@link Action} to define the next scroll.</p>
10243          *
10244          * @param dx        Last scroll amount horizontally
10245          * @param dy        Last scroll amount vertically
10246          * @param state     Transient state of RecyclerView
10247          * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
10248          *                  update this object.
10249          */
onSeekTargetStep(int dx, int dy, State state, Action action)10250         abstract protected void onSeekTargetStep(int dx, int dy, State state, Action action);
10251 
10252         /**
10253          * Called when the target position is laid out. This is the last callback SmoothScroller
10254          * will receive and it should update the provided {@link Action} to define the scroll
10255          * details towards the target view.
10256          * @param targetView    The view element which render the target position.
10257          * @param state         Transient state of RecyclerView
10258          * @param action        Action instance that you should update to define final scroll action
10259          *                      towards the targetView
10260          */
onTargetFound(View targetView, State state, Action action)10261         abstract protected void onTargetFound(View targetView, State state, Action action);
10262 
10263         /**
10264          * Holds information about a smooth scroll request by a {@link SmoothScroller}.
10265          */
10266         public static class Action {
10267 
10268             public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
10269 
10270             private int mDx;
10271 
10272             private int mDy;
10273 
10274             private int mDuration;
10275 
10276             private int mJumpToPosition = NO_POSITION;
10277 
10278             private Interpolator mInterpolator;
10279 
10280             private boolean changed = false;
10281 
10282             // we track this variable to inform custom implementer if they are updating the action
10283             // in every animation callback
10284             private int consecutiveUpdates = 0;
10285 
10286             /**
10287              * @param dx Pixels to scroll horizontally
10288              * @param dy Pixels to scroll vertically
10289              */
Action(int dx, int dy)10290             public Action(int dx, int dy) {
10291                 this(dx, dy, UNDEFINED_DURATION, null);
10292             }
10293 
10294             /**
10295              * @param dx       Pixels to scroll horizontally
10296              * @param dy       Pixels to scroll vertically
10297              * @param duration Duration of the animation in milliseconds
10298              */
Action(int dx, int dy, int duration)10299             public Action(int dx, int dy, int duration) {
10300                 this(dx, dy, duration, null);
10301             }
10302 
10303             /**
10304              * @param dx           Pixels to scroll horizontally
10305              * @param dy           Pixels to scroll vertically
10306              * @param duration     Duration of the animation in milliseconds
10307              * @param interpolator Interpolator to be used when calculating scroll position in each
10308              *                     animation step
10309              */
Action(int dx, int dy, int duration, Interpolator interpolator)10310             public Action(int dx, int dy, int duration, Interpolator interpolator) {
10311                 mDx = dx;
10312                 mDy = dy;
10313                 mDuration = duration;
10314                 mInterpolator = interpolator;
10315             }
10316 
10317             /**
10318              * Instead of specifying pixels to scroll, use the target position to jump using
10319              * {@link RecyclerView#scrollToPosition(int)}.
10320              * <p>
10321              * You may prefer using this method if scroll target is really far away and you prefer
10322              * to jump to a location and smooth scroll afterwards.
10323              * <p>
10324              * Note that calling this method takes priority over other update methods such as
10325              * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
10326              * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
10327              * {@link #jumpTo(int)}, the other changes will not be considered for this animation
10328              * frame.
10329              *
10330              * @param targetPosition The target item position to scroll to using instant scrolling.
10331              */
jumpTo(int targetPosition)10332             public void jumpTo(int targetPosition) {
10333                 mJumpToPosition = targetPosition;
10334             }
10335 
hasJumpTarget()10336             boolean hasJumpTarget() {
10337                 return mJumpToPosition >= 0;
10338             }
10339 
runIfNecessary(RecyclerView recyclerView)10340             private void runIfNecessary(RecyclerView recyclerView) {
10341                 if (mJumpToPosition >= 0) {
10342                     final int position = mJumpToPosition;
10343                     mJumpToPosition = NO_POSITION;
10344                     recyclerView.jumpToPositionForSmoothScroller(position);
10345                     changed = false;
10346                     return;
10347                 }
10348                 if (changed) {
10349                     validate();
10350                     if (mInterpolator == null) {
10351                         if (mDuration == UNDEFINED_DURATION) {
10352                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
10353                         } else {
10354                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
10355                         }
10356                     } else {
10357                         recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration, mInterpolator);
10358                     }
10359                     consecutiveUpdates ++;
10360                     if (consecutiveUpdates > 10) {
10361                         // A new action is being set in every animation step. This looks like a bad
10362                         // implementation. Inform developer.
10363                         Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
10364                                 + " you are not changing it unless necessary");
10365                     }
10366                     changed = false;
10367                 } else {
10368                     consecutiveUpdates = 0;
10369                 }
10370             }
10371 
validate()10372             private void validate() {
10373                 if (mInterpolator != null && mDuration < 1) {
10374                     throw new IllegalStateException("If you provide an interpolator, you must"
10375                             + " set a positive duration");
10376                 } else if (mDuration < 1) {
10377                     throw new IllegalStateException("Scroll duration must be a positive number");
10378                 }
10379             }
10380 
getDx()10381             public int getDx() {
10382                 return mDx;
10383             }
10384 
setDx(int dx)10385             public void setDx(int dx) {
10386                 changed = true;
10387                 mDx = dx;
10388             }
10389 
getDy()10390             public int getDy() {
10391                 return mDy;
10392             }
10393 
setDy(int dy)10394             public void setDy(int dy) {
10395                 changed = true;
10396                 mDy = dy;
10397             }
10398 
getDuration()10399             public int getDuration() {
10400                 return mDuration;
10401             }
10402 
setDuration(int duration)10403             public void setDuration(int duration) {
10404                 changed = true;
10405                 mDuration = duration;
10406             }
10407 
getInterpolator()10408             public Interpolator getInterpolator() {
10409                 return mInterpolator;
10410             }
10411 
10412             /**
10413              * Sets the interpolator to calculate scroll steps
10414              * @param interpolator The interpolator to use. If you specify an interpolator, you must
10415              *                     also set the duration.
10416              * @see #setDuration(int)
10417              */
setInterpolator(Interpolator interpolator)10418             public void setInterpolator(Interpolator interpolator) {
10419                 changed = true;
10420                 mInterpolator = interpolator;
10421             }
10422 
10423             /**
10424              * Updates the action with given parameters.
10425              * @param dx Pixels to scroll horizontally
10426              * @param dy Pixels to scroll vertically
10427              * @param duration Duration of the animation in milliseconds
10428              * @param interpolator Interpolator to be used when calculating scroll position in each
10429              *                     animation step
10430              */
update(int dx, int dy, int duration, Interpolator interpolator)10431             public void update(int dx, int dy, int duration, Interpolator interpolator) {
10432                 mDx = dx;
10433                 mDy = dy;
10434                 mDuration = duration;
10435                 mInterpolator = interpolator;
10436                 changed = true;
10437             }
10438         }
10439 
10440         /**
10441          * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
10442          * to provide a hint to a {@link SmoothScroller} about the location of the target position.
10443          */
10444         public interface ScrollVectorProvider {
10445             /**
10446              * Should calculate the vector that points to the direction where the target position
10447              * can be found.
10448              * <p>
10449              * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
10450              * the target position.
10451              * <p>
10452              * The magnitude of the vector is not important. It is always normalized before being
10453              * used by the {@link LinearSmoothScroller}.
10454              * <p>
10455              * LayoutManager should not check whether the position exists in the adapter or not.
10456              *
10457              * @param targetPosition the target position to which the returned vector should point
10458              *
10459              * @return the scroll vector for a given position.
10460              */
computeScrollVectorForPosition(int targetPosition)10461             PointF computeScrollVectorForPosition(int targetPosition);
10462         }
10463     }
10464 
10465     static class AdapterDataObservable extends Observable<AdapterDataObserver> {
hasObservers()10466         public boolean hasObservers() {
10467             return !mObservers.isEmpty();
10468         }
10469 
notifyChanged()10470         public void notifyChanged() {
10471             // since onChanged() is implemented by the app, it could do anything, including
10472             // removing itself from {@link mObservers} - and that could cause problems if
10473             // an iterator is used on the ArrayList {@link mObservers}.
10474             // to avoid such problems, just march thru the list in the reverse order.
10475             for (int i = mObservers.size() - 1; i >= 0; i--) {
10476                 mObservers.get(i).onChanged();
10477             }
10478         }
10479 
notifyItemRangeChanged(int positionStart, int itemCount)10480         public void notifyItemRangeChanged(int positionStart, int itemCount) {
10481             notifyItemRangeChanged(positionStart, itemCount, null);
10482         }
10483 
notifyItemRangeChanged(int positionStart, int itemCount, Object payload)10484         public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
10485             // since onItemRangeChanged() is implemented by the app, it could do anything, including
10486             // removing itself from {@link mObservers} - and that could cause problems if
10487             // an iterator is used on the ArrayList {@link mObservers}.
10488             // to avoid such problems, just march thru the list in the reverse order.
10489             for (int i = mObservers.size() - 1; i >= 0; i--) {
10490                 mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
10491             }
10492         }
10493 
notifyItemRangeInserted(int positionStart, int itemCount)10494         public void notifyItemRangeInserted(int positionStart, int itemCount) {
10495             // since onItemRangeInserted() is implemented by the app, it could do anything,
10496             // including removing itself from {@link mObservers} - and that could cause problems if
10497             // an iterator is used on the ArrayList {@link mObservers}.
10498             // to avoid such problems, just march thru the list in the reverse order.
10499             for (int i = mObservers.size() - 1; i >= 0; i--) {
10500                 mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
10501             }
10502         }
10503 
notifyItemRangeRemoved(int positionStart, int itemCount)10504         public void notifyItemRangeRemoved(int positionStart, int itemCount) {
10505             // since onItemRangeRemoved() is implemented by the app, it could do anything, including
10506             // removing itself from {@link mObservers} - and that could cause problems if
10507             // an iterator is used on the ArrayList {@link mObservers}.
10508             // to avoid such problems, just march thru the list in the reverse order.
10509             for (int i = mObservers.size() - 1; i >= 0; i--) {
10510                 mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
10511             }
10512         }
10513 
notifyItemMoved(int fromPosition, int toPosition)10514         public void notifyItemMoved(int fromPosition, int toPosition) {
10515             for (int i = mObservers.size() - 1; i >= 0; i--) {
10516                 mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
10517             }
10518         }
10519     }
10520 
10521     /**
10522      * This is public so that the CREATOR can be access on cold launch.
10523      * @hide
10524      */
10525     public static class SavedState extends AbsSavedState {
10526 
10527         Parcelable mLayoutState;
10528 
10529         /**
10530          * called by CREATOR
10531          */
SavedState(Parcel in, ClassLoader loader)10532         SavedState(Parcel in, ClassLoader loader) {
10533             super(in, loader);
10534             mLayoutState = in.readParcelable(
10535                     loader != null ? loader : LayoutManager.class.getClassLoader());
10536         }
10537 
10538         /**
10539          * Called by onSaveInstanceState
10540          */
SavedState(Parcelable superState)10541         SavedState(Parcelable superState) {
10542             super(superState);
10543         }
10544 
10545         @Override
writeToParcel(Parcel dest, int flags)10546         public void writeToParcel(Parcel dest, int flags) {
10547             super.writeToParcel(dest, flags);
10548             dest.writeParcelable(mLayoutState, 0);
10549         }
10550 
copyFrom(SavedState other)10551         private void copyFrom(SavedState other) {
10552             mLayoutState = other.mLayoutState;
10553         }
10554 
10555         public static final Creator<SavedState> CREATOR = ParcelableCompat.newCreator(
10556                 new ParcelableCompatCreatorCallbacks<SavedState>() {
10557                     @Override
10558                     public SavedState createFromParcel(Parcel in, ClassLoader loader) {
10559                         return new SavedState(in, loader);
10560                     }
10561 
10562                     @Override
10563                     public SavedState[] newArray(int size) {
10564                         return new SavedState[size];
10565                     }
10566                 });
10567     }
10568     /**
10569      * <p>Contains useful information about the current RecyclerView state like target scroll
10570      * position or view focus. State object can also keep arbitrary data, identified by resource
10571      * ids.</p>
10572      * <p>Often times, RecyclerView components will need to pass information between each other.
10573      * To provide a well defined data bus between components, RecyclerView passes the same State
10574      * object to component callbacks and these components can use it to exchange data.</p>
10575      * <p>If you implement custom components, you can use State's put/get/remove methods to pass
10576      * data between your components without needing to manage their lifecycles.</p>
10577      */
10578     public static class State {
10579         static final int STEP_START = 1;
10580         static final int STEP_LAYOUT = 1 << 1;
10581         static final int STEP_ANIMATIONS = 1 << 2;
10582 
assertLayoutStep(int accepted)10583         void assertLayoutStep(int accepted) {
10584             if ((accepted & mLayoutStep) == 0) {
10585                 throw new IllegalStateException("Layout state should be one of "
10586                         + Integer.toBinaryString(accepted) + " but it is "
10587                         + Integer.toBinaryString(mLayoutStep));
10588             }
10589         }
10590 
10591         @IntDef(flag = true, value = {
10592                 STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
10593         })
10594         @Retention(RetentionPolicy.SOURCE)
10595         @interface LayoutState {}
10596 
10597         private int mTargetPosition = RecyclerView.NO_POSITION;
10598 
10599         @LayoutState
10600         private int mLayoutStep = STEP_START;
10601 
10602         private SparseArray<Object> mData;
10603 
10604         /**
10605          * Number of items adapter has.
10606          */
10607         int mItemCount = 0;
10608 
10609         /**
10610          * Number of items adapter had in the previous layout.
10611          */
10612         private int mPreviousLayoutItemCount = 0;
10613 
10614         /**
10615          * Number of items that were NOT laid out but has been deleted from the adapter after the
10616          * previous layout.
10617          */
10618         private int mDeletedInvisibleItemCountSincePreviousLayout = 0;
10619 
10620         private boolean mStructureChanged = false;
10621 
10622         private boolean mInPreLayout = false;
10623 
10624         private boolean mRunSimpleAnimations = false;
10625 
10626         private boolean mRunPredictiveAnimations = false;
10627 
10628         private boolean mTrackOldChangeHolders = false;
10629 
10630         private boolean mIsMeasuring = false;
10631 
10632         /**
10633          * This data is saved before a layout calculation happens. After the layout is finished,
10634          * if the previously focused view has been replaced with another view for the same item, we
10635          * move the focus to the new item automatically.
10636          */
10637         int mFocusedItemPosition;
10638         long mFocusedItemId;
10639         // when a sub child has focus, record its id and see if we can directly request focus on
10640         // that one instead
10641         int mFocusedSubChildId;
10642 
reset()10643         State reset() {
10644             mTargetPosition = RecyclerView.NO_POSITION;
10645             if (mData != null) {
10646                 mData.clear();
10647             }
10648             mItemCount = 0;
10649             mStructureChanged = false;
10650             mIsMeasuring = false;
10651             return this;
10652         }
10653 
10654         /**
10655          * Returns true if the RecyclerView is currently measuring the layout. This value is
10656          * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
10657          * has non-exact measurement specs.
10658          * <p>
10659          * Note that if the LayoutManager supports predictive animations and it is calculating the
10660          * pre-layout step, this value will be {@code false} even if the RecyclerView is in
10661          * {@code onMeasure} call. This is because pre-layout means the previous state of the
10662          * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
10663          * LayoutManager is always guaranteed to receive another call to
10664          * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
10665          *
10666          * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
10667          */
isMeasuring()10668         public boolean isMeasuring() {
10669             return mIsMeasuring;
10670         }
10671 
10672         /**
10673          * Returns true if
10674          * @return
10675          */
isPreLayout()10676         public boolean isPreLayout() {
10677             return mInPreLayout;
10678         }
10679 
10680         /**
10681          * Returns whether RecyclerView will run predictive animations in this layout pass
10682          * or not.
10683          *
10684          * @return true if RecyclerView is calculating predictive animations to be run at the end
10685          *         of the layout pass.
10686          */
willRunPredictiveAnimations()10687         public boolean willRunPredictiveAnimations() {
10688             return mRunPredictiveAnimations;
10689         }
10690 
10691         /**
10692          * Returns whether RecyclerView will run simple animations in this layout pass
10693          * or not.
10694          *
10695          * @return true if RecyclerView is calculating simple animations to be run at the end of
10696          *         the layout pass.
10697          */
willRunSimpleAnimations()10698         public boolean willRunSimpleAnimations() {
10699             return mRunSimpleAnimations;
10700         }
10701 
10702         /**
10703          * Removes the mapping from the specified id, if there was any.
10704          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
10705          *                   preserve cross functionality and avoid conflicts.
10706          */
remove(int resourceId)10707         public void remove(int resourceId) {
10708             if (mData == null) {
10709                 return;
10710             }
10711             mData.remove(resourceId);
10712         }
10713 
10714         /**
10715          * Gets the Object mapped from the specified id, or <code>null</code>
10716          * if no such data exists.
10717          *
10718          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
10719          *                   to
10720          *                   preserve cross functionality and avoid conflicts.
10721          */
get(int resourceId)10722         public <T> T get(int resourceId) {
10723             if (mData == null) {
10724                 return null;
10725             }
10726             return (T) mData.get(resourceId);
10727         }
10728 
10729         /**
10730          * Adds a mapping from the specified id to the specified value, replacing the previous
10731          * mapping from the specified key if there was one.
10732          *
10733          * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
10734          *                   preserve cross functionality and avoid conflicts.
10735          * @param data       The data you want to associate with the resourceId.
10736          */
put(int resourceId, Object data)10737         public void put(int resourceId, Object data) {
10738             if (mData == null) {
10739                 mData = new SparseArray<Object>();
10740             }
10741             mData.put(resourceId, data);
10742         }
10743 
10744         /**
10745          * If scroll is triggered to make a certain item visible, this value will return the
10746          * adapter index of that item.
10747          * @return Adapter index of the target item or
10748          * {@link RecyclerView#NO_POSITION} if there is no target
10749          * position.
10750          */
getTargetScrollPosition()10751         public int getTargetScrollPosition() {
10752             return mTargetPosition;
10753         }
10754 
10755         /**
10756          * Returns if current scroll has a target position.
10757          * @return true if scroll is being triggered to make a certain position visible
10758          * @see #getTargetScrollPosition()
10759          */
hasTargetScrollPosition()10760         public boolean hasTargetScrollPosition() {
10761             return mTargetPosition != RecyclerView.NO_POSITION;
10762         }
10763 
10764         /**
10765          * @return true if the structure of the data set has changed since the last call to
10766          *         onLayoutChildren, false otherwise
10767          */
didStructureChange()10768         public boolean didStructureChange() {
10769             return mStructureChanged;
10770         }
10771 
10772         /**
10773          * Returns the total number of items that can be laid out. Note that this number is not
10774          * necessarily equal to the number of items in the adapter, so you should always use this
10775          * number for your position calculations and never access the adapter directly.
10776          * <p>
10777          * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
10778          * data changes on existing Views. These calculations are used to decide which animations
10779          * should be run.
10780          * <p>
10781          * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
10782          * present the correct state to LayoutManager in pre-layout pass.
10783          * <p>
10784          * For example, a newly added item is not included in pre-layout item count because
10785          * pre-layout reflects the contents of the adapter before the item is added. Behind the
10786          * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
10787          * LayoutManager does not know about the new item's existence in pre-layout. The item will
10788          * be available in second layout pass and will be included in the item count. Similar
10789          * adjustments are made for moved and removed items as well.
10790          * <p>
10791          * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
10792          *
10793          * @return The number of items currently available
10794          * @see LayoutManager#getItemCount()
10795          */
getItemCount()10796         public int getItemCount() {
10797             return mInPreLayout ?
10798                     (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout) :
10799                     mItemCount;
10800         }
10801 
10802         @Override
toString()10803         public String toString() {
10804             return "State{" +
10805                     "mTargetPosition=" + mTargetPosition +
10806                     ", mData=" + mData +
10807                     ", mItemCount=" + mItemCount +
10808                     ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount +
10809                     ", mDeletedInvisibleItemCountSincePreviousLayout="
10810                     + mDeletedInvisibleItemCountSincePreviousLayout +
10811                     ", mStructureChanged=" + mStructureChanged +
10812                     ", mInPreLayout=" + mInPreLayout +
10813                     ", mRunSimpleAnimations=" + mRunSimpleAnimations +
10814                     ", mRunPredictiveAnimations=" + mRunPredictiveAnimations +
10815                     '}';
10816         }
10817     }
10818 
10819     /**
10820      * This class defines the behavior of fling if the developer wishes to handle it.
10821      * <p>
10822      * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
10823      *
10824      * @see #setOnFlingListener(OnFlingListener)
10825      */
10826     public static abstract class OnFlingListener {
10827 
10828         /**
10829          * Override this to handle a fling given the velocities in both x and y directions.
10830          * Note that this method will only be called if the associated {@link LayoutManager}
10831          * supports scrolling and the fling is not handled by nested scrolls first.
10832          *
10833          * @param velocityX the fling velocity on the X axis
10834          * @param velocityY the fling velocity on the Y axis
10835          *
10836          * @return true if the fling washandled, false otherwise.
10837          */
onFling(int velocityX, int velocityY)10838         public abstract boolean onFling(int velocityX, int velocityY);
10839     }
10840 
10841     /**
10842      * Internal listener that manages items after animations finish. This is how items are
10843      * retained (not recycled) during animations, but allowed to be recycled afterwards.
10844      * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
10845      * method on the animator's listener when it is done animating any item.
10846      */
10847     private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
10848 
10849         @Override
onAnimationFinished(ViewHolder item)10850         public void onAnimationFinished(ViewHolder item) {
10851             item.setIsRecyclable(true);
10852             if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
10853                 item.mShadowedHolder = null;
10854             }
10855             // always null this because an OldViewHolder can never become NewViewHolder w/o being
10856             // recycled.
10857             item.mShadowingHolder = null;
10858             if (!item.shouldBeKeptAsChild()) {
10859                 if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
10860                     removeDetachedView(item.itemView, false);
10861                 }
10862             }
10863         }
10864     }
10865 
10866     /**
10867      * This class defines the animations that take place on items as changes are made
10868      * to the adapter.
10869      *
10870      * Subclasses of ItemAnimator can be used to implement custom animations for actions on
10871      * ViewHolder items. The RecyclerView will manage retaining these items while they
10872      * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
10873      * when a ViewHolder's animation is finished. In other words, there must be a matching
10874      * {@link #dispatchAnimationFinished(ViewHolder)} call for each
10875      * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
10876      * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
10877      * animateChange()}
10878      * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
10879      * and
10880      * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
10881      * animateDisappearance()} call.
10882      *
10883      * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
10884      *
10885      * @see #setItemAnimator(ItemAnimator)
10886      */
10887     @SuppressWarnings("UnusedParameters")
10888     public static abstract class ItemAnimator {
10889 
10890         /**
10891          * The Item represented by this ViewHolder is updated.
10892          * <p>
10893          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
10894          */
10895         public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
10896 
10897         /**
10898          * The Item represented by this ViewHolder is removed from the adapter.
10899          * <p>
10900          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
10901          */
10902         public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
10903 
10904         /**
10905          * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
10906          * represented by this ViewHolder is invalid.
10907          * <p>
10908          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
10909          */
10910         public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
10911 
10912         /**
10913          * The position of the Item represented by this ViewHolder has been changed. This flag is
10914          * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
10915          * any adapter change that may have a side effect on this item. (e.g. The item before this
10916          * one has been removed from the Adapter).
10917          * <p>
10918          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
10919          */
10920         public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
10921 
10922         /**
10923          * This ViewHolder was not laid out but has been added to the layout in pre-layout state
10924          * by the {@link LayoutManager}. This means that the item was already in the Adapter but
10925          * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
10926          * to add new items in pre-layout to specify their virtual location when they are invisible
10927          * (e.g. to specify the item should <i>animate in</i> from below the visible area).
10928          * <p>
10929          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
10930          */
10931         public static final int FLAG_APPEARED_IN_PRE_LAYOUT
10932                 = ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
10933 
10934         /**
10935          * The set of flags that might be passed to
10936          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
10937          */
10938         @IntDef(flag=true, value={
10939                 FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
10940                 FLAG_APPEARED_IN_PRE_LAYOUT
10941         })
10942         @Retention(RetentionPolicy.SOURCE)
10943         public @interface AdapterChanges {}
10944         private ItemAnimatorListener mListener = null;
10945         private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
10946                 new ArrayList<ItemAnimatorFinishedListener>();
10947 
10948         private long mAddDuration = 120;
10949         private long mRemoveDuration = 120;
10950         private long mMoveDuration = 250;
10951         private long mChangeDuration = 250;
10952 
10953         /**
10954          * Gets the current duration for which all move animations will run.
10955          *
10956          * @return The current move duration
10957          */
getMoveDuration()10958         public long getMoveDuration() {
10959             return mMoveDuration;
10960         }
10961 
10962         /**
10963          * Sets the duration for which all move animations will run.
10964          *
10965          * @param moveDuration The move duration
10966          */
setMoveDuration(long moveDuration)10967         public void setMoveDuration(long moveDuration) {
10968             mMoveDuration = moveDuration;
10969         }
10970 
10971         /**
10972          * Gets the current duration for which all add animations will run.
10973          *
10974          * @return The current add duration
10975          */
getAddDuration()10976         public long getAddDuration() {
10977             return mAddDuration;
10978         }
10979 
10980         /**
10981          * Sets the duration for which all add animations will run.
10982          *
10983          * @param addDuration The add duration
10984          */
setAddDuration(long addDuration)10985         public void setAddDuration(long addDuration) {
10986             mAddDuration = addDuration;
10987         }
10988 
10989         /**
10990          * Gets the current duration for which all remove animations will run.
10991          *
10992          * @return The current remove duration
10993          */
getRemoveDuration()10994         public long getRemoveDuration() {
10995             return mRemoveDuration;
10996         }
10997 
10998         /**
10999          * Sets the duration for which all remove animations will run.
11000          *
11001          * @param removeDuration The remove duration
11002          */
setRemoveDuration(long removeDuration)11003         public void setRemoveDuration(long removeDuration) {
11004             mRemoveDuration = removeDuration;
11005         }
11006 
11007         /**
11008          * Gets the current duration for which all change animations will run.
11009          *
11010          * @return The current change duration
11011          */
getChangeDuration()11012         public long getChangeDuration() {
11013             return mChangeDuration;
11014         }
11015 
11016         /**
11017          * Sets the duration for which all change animations will run.
11018          *
11019          * @param changeDuration The change duration
11020          */
setChangeDuration(long changeDuration)11021         public void setChangeDuration(long changeDuration) {
11022             mChangeDuration = changeDuration;
11023         }
11024 
11025         /**
11026          * Internal only:
11027          * Sets the listener that must be called when the animator is finished
11028          * animating the item (or immediately if no animation happens). This is set
11029          * internally and is not intended to be set by external code.
11030          *
11031          * @param listener The listener that must be called.
11032          */
setListener(ItemAnimatorListener listener)11033         void setListener(ItemAnimatorListener listener) {
11034             mListener = listener;
11035         }
11036 
11037         /**
11038          * Called by the RecyclerView before the layout begins. Item animator should record
11039          * necessary information about the View before it is potentially rebound, moved or removed.
11040          * <p>
11041          * The data returned from this method will be passed to the related <code>animate**</code>
11042          * methods.
11043          * <p>
11044          * Note that this method may be called after pre-layout phase if LayoutManager adds new
11045          * Views to the layout in pre-layout pass.
11046          * <p>
11047          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
11048          * the View and the adapter change flags.
11049          *
11050          * @param state       The current State of RecyclerView which includes some useful data
11051          *                    about the layout that will be calculated.
11052          * @param viewHolder  The ViewHolder whose information should be recorded.
11053          * @param changeFlags Additional information about what changes happened in the Adapter
11054          *                    about the Item represented by this ViewHolder. For instance, if
11055          *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
11056          * @param payloads    The payload list that was previously passed to
11057          *                    {@link Adapter#notifyItemChanged(int, Object)} or
11058          *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
11059          *
11060          * @return An ItemHolderInfo instance that preserves necessary information about the
11061          * ViewHolder. This object will be passed back to related <code>animate**</code> methods
11062          * after layout is complete.
11063          *
11064          * @see #recordPostLayoutInformation(State, ViewHolder)
11065          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11066          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11067          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11068          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11069          */
recordPreLayoutInformation(@onNull State state, @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags, @NonNull List<Object> payloads)11070         public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
11071                 @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
11072                 @NonNull List<Object> payloads) {
11073             return obtainHolderInfo().setFrom(viewHolder);
11074         }
11075 
11076         /**
11077          * Called by the RecyclerView after the layout is complete. Item animator should record
11078          * necessary information about the View's final state.
11079          * <p>
11080          * The data returned from this method will be passed to the related <code>animate**</code>
11081          * methods.
11082          * <p>
11083          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
11084          * the View.
11085          *
11086          * @param state      The current State of RecyclerView which includes some useful data about
11087          *                   the layout that will be calculated.
11088          * @param viewHolder The ViewHolder whose information should be recorded.
11089          *
11090          * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
11091          * This object will be passed back to related <code>animate**</code> methods when
11092          * RecyclerView decides how items should be animated.
11093          *
11094          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11095          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11096          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11097          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11098          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11099          */
recordPostLayoutInformation(@onNull State state, @NonNull ViewHolder viewHolder)11100         public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
11101                 @NonNull ViewHolder viewHolder) {
11102             return obtainHolderInfo().setFrom(viewHolder);
11103         }
11104 
11105         /**
11106          * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
11107          * <p>
11108          * This means that the View was a child of the LayoutManager when layout started but has
11109          * been removed by the LayoutManager. It might have been removed from the adapter or simply
11110          * become invisible due to other factors. You can distinguish these two cases by checking
11111          * the change flags that were passed to
11112          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11113          * <p>
11114          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
11115          * animation callback method which will be called by the RecyclerView depends on the
11116          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
11117          * LayoutManager's decision whether to layout the changed version of a disappearing
11118          * ViewHolder or not. RecyclerView will call
11119          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11120          * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
11121          * returns {@code false} from
11122          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
11123          * LayoutManager lays out a new disappearing view that holds the updated information.
11124          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
11125          * <p>
11126          * If LayoutManager supports predictive animations, it might provide a target disappear
11127          * location for the View by laying it out in that location. When that happens,
11128          * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
11129          * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
11130          * <p>
11131          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11132          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11133          * decides not to animate the view).
11134          *
11135          * @param viewHolder    The ViewHolder which should be animated
11136          * @param preLayoutInfo The information that was returned from
11137          *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11138          * @param postLayoutInfo The information that was returned from
11139          *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
11140          *                       null if the LayoutManager did not layout the item.
11141          *
11142          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11143          * false otherwise.
11144          */
animateDisappearance(@onNull ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo)11145         public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
11146                 @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
11147 
11148         /**
11149          * Called by the RecyclerView when a ViewHolder is added to the layout.
11150          * <p>
11151          * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
11152          * but has  been added by the LayoutManager. It might be newly added to the adapter or
11153          * simply become visible due to other factors.
11154          * <p>
11155          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11156          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11157          * decides not to animate the view).
11158          *
11159          * @param viewHolder     The ViewHolder which should be animated
11160          * @param preLayoutInfo  The information that was returned from
11161          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11162          *                       Might be null if Item was just added to the adapter or
11163          *                       LayoutManager does not support predictive animations or it could
11164          *                       not predict that this ViewHolder will become visible.
11165          * @param postLayoutInfo The information that was returned from {@link
11166          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11167          *
11168          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11169          * false otherwise.
11170          */
animateAppearance(@onNull ViewHolder viewHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)11171         public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
11172                 @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11173 
11174         /**
11175          * Called by the RecyclerView when a ViewHolder is present in both before and after the
11176          * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
11177          * for it or a {@link Adapter#notifyDataSetChanged()} call.
11178          * <p>
11179          * This ViewHolder still represents the same data that it was representing when the layout
11180          * started but its position / size may be changed by the LayoutManager.
11181          * <p>
11182          * If the Item's layout position didn't change, RecyclerView still calls this method because
11183          * it does not track this information (or does not necessarily know that an animation is
11184          * not required). Your ItemAnimator should handle this case and if there is nothing to
11185          * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
11186          * <code>false</code>.
11187          * <p>
11188          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11189          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11190          * decides not to animate the view).
11191          *
11192          * @param viewHolder     The ViewHolder which should be animated
11193          * @param preLayoutInfo  The information that was returned from
11194          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11195          * @param postLayoutInfo The information that was returned from {@link
11196          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11197          *
11198          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11199          * false otherwise.
11200          */
animatePersistence(@onNull ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)11201         public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
11202                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11203 
11204         /**
11205          * Called by the RecyclerView when an adapter item is present both before and after the
11206          * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
11207          * for it. This method may also be called when
11208          * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
11209          * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
11210          * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
11211          * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
11212          * called for the new ViewHolder and the old one will be recycled.
11213          * <p>
11214          * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
11215          * a good possibility that item contents didn't really change but it is rebound from the
11216          * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
11217          * screen didn't change and your animator should handle this case as well and avoid creating
11218          * unnecessary animations.
11219          * <p>
11220          * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
11221          * previous presentation of the item as-is and supply a new ViewHolder for the updated
11222          * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
11223          * This is useful if you don't know the contents of the Item and would like
11224          * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
11225          * <p>
11226          * When you are writing a custom item animator for your layout, it might be more performant
11227          * and elegant to re-use the same ViewHolder and animate the content changes manually.
11228          * <p>
11229          * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
11230          * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
11231          * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
11232          * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
11233          * which represent the same Item. In that case, only the new ViewHolder is visible
11234          * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
11235          * <p>
11236          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
11237          * ViewHolder when their animation is complete
11238          * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
11239          * animate the view).
11240          * <p>
11241          *  If oldHolder and newHolder are the same instance, you should call
11242          * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
11243          * <p>
11244          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
11245          * animation callback method which will be called by the RecyclerView depends on the
11246          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
11247          * LayoutManager's decision whether to layout the changed version of a disappearing
11248          * ViewHolder or not. RecyclerView will call
11249          * {@code animateChange} instead of
11250          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11251          * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
11252          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
11253          * LayoutManager lays out a new disappearing view that holds the updated information.
11254          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
11255          *
11256          * @param oldHolder     The ViewHolder before the layout is started, might be the same
11257          *                      instance with newHolder.
11258          * @param newHolder     The ViewHolder after the layout is finished, might be the same
11259          *                      instance with oldHolder.
11260          * @param preLayoutInfo  The information that was returned from
11261          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11262          * @param postLayoutInfo The information that was returned from {@link
11263          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11264          *
11265          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11266          * false otherwise.
11267          */
animateChange(@onNull ViewHolder oldHolder, @NonNull ViewHolder newHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)11268         public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
11269                 @NonNull ViewHolder newHolder,
11270                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11271 
buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder)11272         @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
11273             int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
11274             if (viewHolder.isInvalid()) {
11275                 return FLAG_INVALIDATED;
11276             }
11277             if ((flags & FLAG_INVALIDATED) == 0) {
11278                 final int oldPos = viewHolder.getOldPosition();
11279                 final int pos = viewHolder.getAdapterPosition();
11280                 if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos){
11281                     flags |= FLAG_MOVED;
11282                 }
11283             }
11284             return flags;
11285         }
11286 
11287         /**
11288          * Called when there are pending animations waiting to be started. This state
11289          * is governed by the return values from
11290          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11291          * animateAppearance()},
11292          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11293          * animateChange()}
11294          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11295          * animatePersistence()}, and
11296          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11297          * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
11298          * called later to start the associated animations. runPendingAnimations() will be scheduled
11299          * to be run on the next frame.
11300          */
runPendingAnimations()11301         abstract public void runPendingAnimations();
11302 
11303         /**
11304          * Method called when an animation on a view should be ended immediately.
11305          * This could happen when other events, like scrolling, occur, so that
11306          * animating views can be quickly put into their proper end locations.
11307          * Implementations should ensure that any animations running on the item
11308          * are canceled and affected properties are set to their end values.
11309          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
11310          * animation since the animations are effectively done when this method is called.
11311          *
11312          * @param item The item for which an animation should be stopped.
11313          */
endAnimation(ViewHolder item)11314         abstract public void endAnimation(ViewHolder item);
11315 
11316         /**
11317          * Method called when all item animations should be ended immediately.
11318          * This could happen when other events, like scrolling, occur, so that
11319          * animating views can be quickly put into their proper end locations.
11320          * Implementations should ensure that any animations running on any items
11321          * are canceled and affected properties are set to their end values.
11322          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
11323          * animation since the animations are effectively done when this method is called.
11324          */
endAnimations()11325         abstract public void endAnimations();
11326 
11327         /**
11328          * Method which returns whether there are any item animations currently running.
11329          * This method can be used to determine whether to delay other actions until
11330          * animations end.
11331          *
11332          * @return true if there are any item animations currently running, false otherwise.
11333          */
isRunning()11334         abstract public boolean isRunning();
11335 
11336         /**
11337          * Method to be called by subclasses when an animation is finished.
11338          * <p>
11339          * For each call RecyclerView makes to
11340          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11341          * animateAppearance()},
11342          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11343          * animatePersistence()}, or
11344          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11345          * animateDisappearance()}, there
11346          * should
11347          * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
11348          * <p>
11349          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11350          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
11351          * and <code>newHolder</code>  (if they are not the same instance).
11352          *
11353          * @param viewHolder The ViewHolder whose animation is finished.
11354          * @see #onAnimationFinished(ViewHolder)
11355          */
dispatchAnimationFinished(ViewHolder viewHolder)11356         public final void dispatchAnimationFinished(ViewHolder viewHolder) {
11357             onAnimationFinished(viewHolder);
11358             if (mListener != null) {
11359                 mListener.onAnimationFinished(viewHolder);
11360             }
11361         }
11362 
11363         /**
11364          * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
11365          * ItemAnimator.
11366          *
11367          * @param viewHolder The ViewHolder whose animation is finished. There might still be other
11368          *                   animations running on this ViewHolder.
11369          * @see #dispatchAnimationFinished(ViewHolder)
11370          */
onAnimationFinished(ViewHolder viewHolder)11371         public void onAnimationFinished(ViewHolder viewHolder) {
11372         }
11373 
11374         /**
11375          * Method to be called by subclasses when an animation is started.
11376          * <p>
11377          * For each call RecyclerView makes to
11378          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11379          * animateAppearance()},
11380          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11381          * animatePersistence()}, or
11382          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11383          * animateDisappearance()}, there should be a matching
11384          * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
11385          * <p>
11386          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11387          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
11388          * and <code>newHolder</code> (if they are not the same instance).
11389          * <p>
11390          * If your ItemAnimator decides not to animate a ViewHolder, it should call
11391          * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
11392          * {@link #dispatchAnimationStarted(ViewHolder)}.
11393          *
11394          * @param viewHolder The ViewHolder whose animation is starting.
11395          * @see #onAnimationStarted(ViewHolder)
11396          */
dispatchAnimationStarted(ViewHolder viewHolder)11397         public final void dispatchAnimationStarted(ViewHolder viewHolder) {
11398             onAnimationStarted(viewHolder);
11399         }
11400 
11401         /**
11402          * Called when a new animation is started on the given ViewHolder.
11403          *
11404          * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
11405          *                   might already be animating and this might be another animation.
11406          * @see #dispatchAnimationStarted(ViewHolder)
11407          */
onAnimationStarted(ViewHolder viewHolder)11408         public void onAnimationStarted(ViewHolder viewHolder) {
11409 
11410         }
11411 
11412         /**
11413          * Like {@link #isRunning()}, this method returns whether there are any item
11414          * animations currently running. Additionally, the listener passed in will be called
11415          * when there are no item animations running, either immediately (before the method
11416          * returns) if no animations are currently running, or when the currently running
11417          * animations are {@link #dispatchAnimationsFinished() finished}.
11418          *
11419          * <p>Note that the listener is transient - it is either called immediately and not
11420          * stored at all, or stored only until it is called when running animations
11421          * are finished sometime later.</p>
11422          *
11423          * @param listener A listener to be called immediately if no animations are running
11424          * or later when currently-running animations have finished. A null listener is
11425          * equivalent to calling {@link #isRunning()}.
11426          * @return true if there are any item animations currently running, false otherwise.
11427          */
isRunning(ItemAnimatorFinishedListener listener)11428         public final boolean isRunning(ItemAnimatorFinishedListener listener) {
11429             boolean running = isRunning();
11430             if (listener != null) {
11431                 if (!running) {
11432                     listener.onAnimationsFinished();
11433                 } else {
11434                     mFinishedListeners.add(listener);
11435                 }
11436             }
11437             return running;
11438         }
11439 
11440         /**
11441          * When an item is changed, ItemAnimator can decide whether it wants to re-use
11442          * the same ViewHolder for animations or RecyclerView should create a copy of the
11443          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
11444          * <p>
11445          * Note that this method will only be called if the {@link ViewHolder} still has the same
11446          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
11447          * both {@link ViewHolder}s in the
11448          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
11449          * <p>
11450          * If your application is using change payloads, you can override
11451          * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
11452          *
11453          * @param viewHolder The ViewHolder which represents the changed item's old content.
11454          *
11455          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
11456          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
11457          *         ItemAnimator to animate. Default implementation returns <code>true</code>.
11458          *
11459          * @see #canReuseUpdatedViewHolder(ViewHolder, List)
11460          */
canReuseUpdatedViewHolder(@onNull ViewHolder viewHolder)11461         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
11462             return true;
11463         }
11464 
11465         /**
11466          * When an item is changed, ItemAnimator can decide whether it wants to re-use
11467          * the same ViewHolder for animations or RecyclerView should create a copy of the
11468          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
11469          * <p>
11470          * Note that this method will only be called if the {@link ViewHolder} still has the same
11471          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
11472          * both {@link ViewHolder}s in the
11473          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
11474          *
11475          * @param viewHolder The ViewHolder which represents the changed item's old content.
11476          * @param payloads A non-null list of merged payloads that were sent with change
11477          *                 notifications. Can be empty if the adapter is invalidated via
11478          *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
11479          *                 payloads will be passed into
11480          *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
11481          *                 method <b>if</b> this method returns <code>true</code>.
11482          *
11483          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
11484          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
11485          *         ItemAnimator to animate. Default implementation calls
11486          *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
11487          *
11488          * @see #canReuseUpdatedViewHolder(ViewHolder)
11489          */
canReuseUpdatedViewHolder(@onNull ViewHolder viewHolder, @NonNull List<Object> payloads)11490         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
11491                 @NonNull List<Object> payloads) {
11492             return canReuseUpdatedViewHolder(viewHolder);
11493         }
11494 
11495         /**
11496          * This method should be called by ItemAnimator implementations to notify
11497          * any listeners that all pending and active item animations are finished.
11498          */
dispatchAnimationsFinished()11499         public final void dispatchAnimationsFinished() {
11500             final int count = mFinishedListeners.size();
11501             for (int i = 0; i < count; ++i) {
11502                 mFinishedListeners.get(i).onAnimationsFinished();
11503             }
11504             mFinishedListeners.clear();
11505         }
11506 
11507         /**
11508          * Returns a new {@link ItemHolderInfo} which will be used to store information about the
11509          * ViewHolder. This information will later be passed into <code>animate**</code> methods.
11510          * <p>
11511          * You can override this method if you want to extend {@link ItemHolderInfo} and provide
11512          * your own instances.
11513          *
11514          * @return A new {@link ItemHolderInfo}.
11515          */
obtainHolderInfo()11516         public ItemHolderInfo obtainHolderInfo() {
11517             return new ItemHolderInfo();
11518         }
11519 
11520         /**
11521          * The interface to be implemented by listeners to animation events from this
11522          * ItemAnimator. This is used internally and is not intended for developers to
11523          * create directly.
11524          */
11525         interface ItemAnimatorListener {
onAnimationFinished(ViewHolder item)11526             void onAnimationFinished(ViewHolder item);
11527         }
11528 
11529         /**
11530          * This interface is used to inform listeners when all pending or running animations
11531          * in an ItemAnimator are finished. This can be used, for example, to delay an action
11532          * in a data set until currently-running animations are complete.
11533          *
11534          * @see #isRunning(ItemAnimatorFinishedListener)
11535          */
11536         public interface ItemAnimatorFinishedListener {
onAnimationsFinished()11537             void onAnimationsFinished();
11538         }
11539 
11540         /**
11541          * A simple data structure that holds information about an item's bounds.
11542          * This information is used in calculating item animations. Default implementation of
11543          * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
11544          * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
11545          * structure. You can extend this class if you would like to keep more information about
11546          * the Views.
11547          * <p>
11548          * If you want to provide your own implementation but still use `super` methods to record
11549          * basic information, you can override {@link #obtainHolderInfo()} to provide your own
11550          * instances.
11551          */
11552         public static class ItemHolderInfo {
11553 
11554             /**
11555              * The left edge of the View (excluding decorations)
11556              */
11557             public int left;
11558 
11559             /**
11560              * The top edge of the View (excluding decorations)
11561              */
11562             public int top;
11563 
11564             /**
11565              * The right edge of the View (excluding decorations)
11566              */
11567             public int right;
11568 
11569             /**
11570              * The bottom edge of the View (excluding decorations)
11571              */
11572             public int bottom;
11573 
11574             /**
11575              * The change flags that were passed to
11576              * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
11577              */
11578             @AdapterChanges
11579             public int changeFlags;
11580 
ItemHolderInfo()11581             public ItemHolderInfo() {
11582             }
11583 
11584             /**
11585              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
11586              * the given ViewHolder. Clears all {@link #changeFlags}.
11587              *
11588              * @param holder The ViewHolder whose bounds should be copied.
11589              * @return This {@link ItemHolderInfo}
11590              */
setFrom(RecyclerView.ViewHolder holder)11591             public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder) {
11592                 return setFrom(holder, 0);
11593             }
11594 
11595             /**
11596              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
11597              * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
11598              *
11599              * @param holder The ViewHolder whose bounds should be copied.
11600              * @param flags  The adapter change flags that were passed into
11601              *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
11602              *               List)}.
11603              * @return This {@link ItemHolderInfo}
11604              */
setFrom(RecyclerView.ViewHolder holder, @AdapterChanges int flags)11605             public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder,
11606                     @AdapterChanges int flags) {
11607                 final View view = holder.itemView;
11608                 this.left = view.getLeft();
11609                 this.top = view.getTop();
11610                 this.right = view.getRight();
11611                 this.bottom = view.getBottom();
11612                 return this;
11613             }
11614         }
11615     }
11616 
11617     @Override
getChildDrawingOrder(int childCount, int i)11618     protected int getChildDrawingOrder(int childCount, int i) {
11619         if (mChildDrawingOrderCallback == null) {
11620             return super.getChildDrawingOrder(childCount, i);
11621         } else {
11622             return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
11623         }
11624     }
11625 
11626     /**
11627      * A callback interface that can be used to alter the drawing order of RecyclerView children.
11628      * <p>
11629      * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
11630      * that applies to that method also applies to this callback. For example, changing the drawing
11631      * order of two views will not have any effect if their elevation values are different since
11632      * elevation overrides the result of this callback.
11633      */
11634     public interface ChildDrawingOrderCallback {
11635         /**
11636          * Returns the index of the child to draw for this iteration. Override this
11637          * if you want to change the drawing order of children. By default, it
11638          * returns i.
11639          *
11640          * @param i The current iteration.
11641          * @return The index of the child to draw this iteration.
11642          *
11643          * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
11644          */
onGetChildDrawingOrder(int childCount, int i)11645         int onGetChildDrawingOrder(int childCount, int i);
11646     }
11647 
getScrollingChildHelper()11648     private NestedScrollingChildHelper getScrollingChildHelper() {
11649         if (mScrollingChildHelper == null) {
11650             mScrollingChildHelper = new NestedScrollingChildHelper(this);
11651         }
11652         return mScrollingChildHelper;
11653     }
11654 }
11655